Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-29 09:18:31

0001 // Copyright (C) 2020 The Qt Company Ltd.
0002 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
0003 // Qt-Security score:significant reason:default
0004 
0005 #ifndef QPROPERTYPRIVATE_H
0006 #define QPROPERTYPRIVATE_H
0007 
0008 //
0009 //  W A R N I N G
0010 //  -------------
0011 //
0012 // This file is not part of the Qt API.  It exists purely as an
0013 // implementation detail.  This header file may change from version to
0014 // version without notice, or even be removed.
0015 //
0016 // We mean it.
0017 //
0018 
0019 #include <QtCore/qglobal.h>
0020 #include <QtCore/qtaggedpointer.h>
0021 #include <QtCore/qmetatype.h>
0022 #include <QtCore/qcontainerfwd.h>
0023 #include <QtCore/qttypetraits.h>
0024 
0025 #include <functional>
0026 
0027 QT_BEGIN_NAMESPACE
0028 
0029 class QBindingStorage;
0030 
0031 template<typename Class, typename T, auto Offset, auto Setter, auto Signal, auto Getter>
0032 class QObjectCompatProperty;
0033 
0034 class QPropertyBindingPrivatePtr;
0035 using PendingBindingObserverList = QVarLengthArray<QPropertyBindingPrivatePtr>;
0036 
0037 namespace QtPrivate {
0038 // QPropertyBindingPrivatePtr operates on a RefCountingMixin solely so that we can inline
0039 // the constructor and copy constructor
0040 struct RefCounted {
0041 
0042     int refCount() const { return ref; }
0043     void addRef() { ++ref; }
0044     bool deref() { return --ref != 0; }
0045 
0046 private:
0047     int ref = 0;
0048 };
0049 }
0050 
0051 class QQmlPropertyBinding;
0052 class QPropertyBindingPrivate;
0053 class QPropertyBindingPrivatePtr
0054 {
0055 public:
0056     using T = QtPrivate::RefCounted;
0057     T &operator*() const { return *d; }
0058     T *operator->() noexcept { return d; }
0059     T *operator->() const noexcept { return d; }
0060     explicit operator T *() { return d; }
0061     explicit operator const T *() const noexcept { return d; }
0062     T *data() const noexcept { return d; }
0063     T *get() const noexcept { return d; }
0064     const T *constData() const noexcept { return d; }
0065     T *take() noexcept { T *x = d; d = nullptr; return x; }
0066 
0067     QPropertyBindingPrivatePtr() noexcept : d(nullptr) { }
0068     ~QPropertyBindingPrivatePtr()
0069     {
0070         if (d && !d->deref())
0071             destroyAndFreeMemory();
0072     }
0073     Q_CORE_EXPORT void destroyAndFreeMemory();
0074 
0075     explicit QPropertyBindingPrivatePtr(T *data) noexcept : d(data) { if (d) d->addRef(); }
0076     QPropertyBindingPrivatePtr(const QPropertyBindingPrivatePtr &o) noexcept
0077         : d(o.d) { if (d) d->addRef(); }
0078 
0079     void reset(T *ptr = nullptr) noexcept;
0080 
0081     QPropertyBindingPrivatePtr &operator=(const QPropertyBindingPrivatePtr &o) noexcept
0082     {
0083         reset(o.d);
0084         return *this;
0085     }
0086     QPropertyBindingPrivatePtr &operator=(T *o) noexcept
0087     {
0088         reset(o);
0089         return *this;
0090     }
0091     QPropertyBindingPrivatePtr(QPropertyBindingPrivatePtr &&o) noexcept : d(std::exchange(o.d, nullptr)) {}
0092     QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QPropertyBindingPrivatePtr)
0093 
0094     operator bool () const noexcept { return d != nullptr; }
0095     bool operator!() const noexcept { return d == nullptr; }
0096 
0097     void swap(QPropertyBindingPrivatePtr &other) noexcept
0098     { qt_ptr_swap(d, other.d); }
0099 
0100 private:
0101     friend bool comparesEqual(const QPropertyBindingPrivatePtr &lhs,
0102                               const QPropertyBindingPrivatePtr &rhs) noexcept
0103     { return lhs.d == rhs.d; }
0104     Q_DECLARE_EQUALITY_COMPARABLE(QPropertyBindingPrivatePtr)
0105     friend bool comparesEqual(const QPropertyBindingPrivatePtr &lhs,
0106                               const T *rhs) noexcept
0107     { return lhs.d == rhs; }
0108     Q_DECLARE_EQUALITY_COMPARABLE(QPropertyBindingPrivatePtr, T*)
0109     friend bool comparesEqual(const QPropertyBindingPrivatePtr &lhs,
0110                               std::nullptr_t) noexcept
0111     { return !lhs; }
0112     Q_DECLARE_EQUALITY_COMPARABLE(QPropertyBindingPrivatePtr, std::nullptr_t)
0113 
0114     QtPrivate::RefCounted *d;
0115 };
0116 
0117 class QUntypedPropertyBinding;
0118 class QPropertyBindingPrivate;
0119 struct QPropertyBindingDataPointer;
0120 class QPropertyObserver;
0121 struct QPropertyObserverPointer;
0122 
0123 class QUntypedPropertyData
0124 {
0125 };
0126 
0127 namespace QtPrivate {
0128 template <typename T>
0129 using IsUntypedPropertyData = std::enable_if_t<std::is_base_of_v<QUntypedPropertyData, T>, bool>;
0130 }
0131 
0132 template <typename T>
0133 class QPropertyData;
0134 
0135 // Used for grouped property evaluations
0136 namespace QtPrivate {
0137 class QPropertyBindingData;
0138 }
0139 struct QPropertyDelayedNotifications;
0140 struct QPropertyProxyBindingData
0141 {
0142     // acts as QPropertyBindingData::d_ptr
0143     quintptr d_ptr;
0144     /*
0145         The two members below store the original binding data and property
0146         data pointer of the property which gets proxied.
0147         They are set in QPropertyDelayedNotifications::addProperty
0148     */
0149     const QtPrivate::QPropertyBindingData *originalBindingData;
0150     QUntypedPropertyData *propertyData;
0151 };
0152 
0153 namespace QtPrivate {
0154 struct BindingEvaluationState;
0155 
0156 /*  used in BindingFunctionVTable::createFor; on all other compilers, void would work, but on
0157     MSVC this causes C2182 when compiling in C++20 mode. As we only need to provide some default
0158     value which gets ignored, we introduce this dummy type.
0159 */
0160 struct MSVCWorkAround {};
0161 
0162 struct BindingFunctionVTable
0163 {
0164     using CallFn = bool(*)(QMetaType, QUntypedPropertyData *, void *);
0165     using DtorFn = void(*)(void *);
0166     using MoveCtrFn = void(*)(void *, void *);
0167     const CallFn call;
0168     const DtorFn destroy;
0169     const MoveCtrFn moveConstruct;
0170     const qsizetype size;
0171 
0172     template<typename Callable, typename PropertyType=MSVCWorkAround>
0173     static constexpr BindingFunctionVTable createFor()
0174     {
0175         static_assert (alignof(Callable) <= alignof(std::max_align_t), "Bindings do not support overaligned functors!");
0176         return {
0177             /*call=*/[](QMetaType metaType, QUntypedPropertyData *dataPtr, void *f){
0178                 if constexpr (!std::is_invocable_v<Callable>) {
0179                     // we got an untyped callable
0180                     static_assert (std::is_invocable_r_v<bool, Callable, QMetaType, QUntypedPropertyData *> );
0181                     auto untypedEvaluationFunction = static_cast<Callable *>(f);
0182                     return std::invoke(*untypedEvaluationFunction, metaType, dataPtr);
0183                 } else if constexpr (!std::is_same_v<PropertyType, MSVCWorkAround>) {
0184                     Q_UNUSED(metaType);
0185                     QPropertyData<PropertyType> *propertyPtr = static_cast<QPropertyData<PropertyType> *>(dataPtr);
0186                     // That is allowed by POSIX even if Callable is a function pointer
0187                     auto evaluationFunction = static_cast<Callable *>(f);
0188                     PropertyType newValue = std::invoke(*evaluationFunction);
0189                     if constexpr (QTypeTraits::has_operator_equal_v<PropertyType>) {
0190                         if (newValue == propertyPtr->valueBypassingBindings())
0191                             return false;
0192                     }
0193                     propertyPtr->setValueBypassingBindings(std::move(newValue));
0194                     return true;
0195                 } else {
0196                     // Our code will never instantiate this
0197                     Q_UNREACHABLE_RETURN(false);
0198                 }
0199             },
0200             /*destroy*/[](void *f){ static_cast<Callable *>(f)->~Callable(); },
0201             /*moveConstruct*/[](void *addr, void *other){
0202                 new (addr) Callable(std::move(*static_cast<Callable *>(other)));
0203             },
0204             /*size*/sizeof(Callable)
0205         };
0206     }
0207 };
0208 
0209 template<typename Callable, typename PropertyType=MSVCWorkAround>
0210 inline constexpr BindingFunctionVTable bindingFunctionVTable = BindingFunctionVTable::createFor<Callable, PropertyType>();
0211 
0212 
0213 // writes binding result into dataPtr
0214 struct QPropertyBindingFunction {
0215     const QtPrivate::BindingFunctionVTable *vtable;
0216     void *functor;
0217 };
0218 
0219 using QPropertyObserverCallback = void (*)(QUntypedPropertyData *);
0220 using QPropertyBindingWrapper = bool(*)(QMetaType, QUntypedPropertyData *dataPtr, QPropertyBindingFunction);
0221 
0222 /*!
0223     \internal
0224     A property normally consists of the actual property value and metadata for the binding system.
0225     QPropertyBindingData is the latter part. It stores a pointer to either
0226     - a (potentially empty) linked list of notifiers, in case there is no binding set,
0227     - an actual QUntypedPropertyBinding when the property has a binding,
0228     - or a pointer to QPropertyProxyBindingData when notifications occur inside a grouped update.
0229 
0230     \sa QPropertyDelayedNotifications, beginPropertyUpdateGroup
0231  */
0232 class Q_CORE_EXPORT QPropertyBindingData
0233 {
0234     // Mutable because the address of the observer of the currently evaluating binding is stored here, for
0235     // notification later when the value changes.
0236     mutable quintptr d_ptr = 0;
0237     friend struct QT_PREPEND_NAMESPACE(QPropertyBindingDataPointer);
0238     friend class QT_PREPEND_NAMESPACE(QQmlPropertyBinding);
0239     friend struct QT_PREPEND_NAMESPACE(QPropertyDelayedNotifications);
0240 
0241     template<typename Class, typename T, auto Offset, auto Setter, auto Signal, auto Getter>
0242     friend class QT_PREPEND_NAMESPACE(QObjectCompatProperty);
0243 
0244     Q_DISABLE_COPY(QPropertyBindingData)
0245 public:
0246     QPropertyBindingData() = default;
0247     QPropertyBindingData(QPropertyBindingData &&other);
0248     QPropertyBindingData &operator=(QPropertyBindingData &&other) = delete;
0249     ~QPropertyBindingData();
0250 
0251     // Is d_ptr pointing to a binding (1) or list of notifiers (0)?
0252     static inline constexpr quintptr BindingBit = 0x1;
0253     // Is d_ptr pointing to QPropertyProxyBindingData (1) or to an actual binding/list of notifiers?
0254     static inline constexpr quintptr DelayedNotificationBit = 0x2;
0255 
0256     bool hasBinding() const { return d_ptr & BindingBit; }
0257     bool isNotificationDelayed() const { return d_ptr & DelayedNotificationBit; }
0258 
0259     QUntypedPropertyBinding setBinding(const QUntypedPropertyBinding &newBinding,
0260                                        QUntypedPropertyData *propertyDataPtr,
0261                                        QPropertyObserverCallback staticObserverCallback = nullptr,
0262                                        QPropertyBindingWrapper bindingWrapper = nullptr);
0263 
0264     QPropertyBindingPrivate *binding() const
0265     {
0266         quintptr dd = d();
0267         if (dd & BindingBit)
0268             return reinterpret_cast<QPropertyBindingPrivate*>(dd - BindingBit);
0269         return nullptr;
0270 
0271     }
0272 
0273     void evaluateIfDirty(const QUntypedPropertyData *) const; // ### Kept for BC reasons, unused
0274 
0275     void removeBinding()
0276     {
0277         if (hasBinding())
0278             removeBinding_helper();
0279     }
0280 
0281     void registerWithCurrentlyEvaluatingBinding(QtPrivate::BindingEvaluationState *currentBinding) const
0282     {
0283         if (!currentBinding)
0284             return;
0285         registerWithCurrentlyEvaluatingBinding_helper(currentBinding);
0286     }
0287     void registerWithCurrentlyEvaluatingBinding() const;
0288     void notifyObservers(QUntypedPropertyData *propertyDataPtr) const;
0289     void notifyObservers(QUntypedPropertyData *propertyDataPtr, QBindingStorage *storage) const;
0290 private:
0291     /*!
0292         \internal
0293         Returns a reference to d_ptr, except when d_ptr points to a proxy.
0294         In that case, a reference to proxy->d_ptr is returned instead.
0295 
0296         To properly support proxying, direct access to d_ptr only occurs when
0297         - a function actually deals with proxying (e.g.
0298           QPropertyDelayedNotifications::addProperty),
0299         - only the tag value is accessed (e.g. hasBinding) or
0300         - inside a constructor.
0301      */
0302     quintptr &d_ref() const
0303     {
0304         quintptr &d = d_ptr;
0305         if (isNotificationDelayed())
0306             return proxyData()->d_ptr;
0307         return d;
0308     }
0309     quintptr d() const { return d_ref(); }
0310     QPropertyProxyBindingData *proxyData() const
0311     {
0312         Q_ASSERT(isNotificationDelayed());
0313         return reinterpret_cast<QPropertyProxyBindingData *>(d_ptr & ~(BindingBit|DelayedNotificationBit));
0314     }
0315     void registerWithCurrentlyEvaluatingBinding_helper(BindingEvaluationState *currentBinding) const;
0316     void removeBinding_helper();
0317 
0318     enum NotificationResult { Delayed, Evaluated };
0319     NotificationResult notifyObserver_helper(
0320             QUntypedPropertyData *propertyDataPtr, QBindingStorage *storage,
0321             QPropertyObserverPointer observer,
0322             PendingBindingObserverList &bindingObservers) const;
0323 };
0324 
0325 template <typename T, typename Tag>
0326 class QTagPreservingPointerToPointer
0327 {
0328 public:
0329     constexpr QTagPreservingPointerToPointer() = default;
0330 
0331     QTagPreservingPointerToPointer(T **ptr)
0332         : d(reinterpret_cast<quintptr*>(ptr))
0333     {}
0334 
0335     QTagPreservingPointerToPointer<T, Tag> &operator=(T **ptr)
0336     {
0337         d = reinterpret_cast<quintptr *>(ptr);
0338         return *this;
0339     }
0340 
0341     QTagPreservingPointerToPointer<T, Tag> &operator=(QTaggedPointer<T, Tag> *ptr)
0342     {
0343         d = reinterpret_cast<quintptr *>(ptr);
0344         return *this;
0345     }
0346 
0347     void clear()
0348     {
0349         d = nullptr;
0350     }
0351 
0352     void setPointer(T *ptr)
0353     {
0354         *d = reinterpret_cast<quintptr>(ptr) | (*d & QTaggedPointer<T, Tag>::tagMask());
0355     }
0356 
0357     T *get() const
0358     {
0359         return reinterpret_cast<T*>(*d & QTaggedPointer<T, Tag>::pointerMask());
0360     }
0361 
0362     explicit operator bool() const
0363     {
0364         return d != nullptr;
0365     }
0366 
0367 private:
0368     quintptr *d = nullptr;
0369 };
0370 
0371 namespace detail {
0372     template <typename F>
0373     struct ExtractClassFromFunctionPointer;
0374 
0375     template<typename T, typename C>
0376     struct ExtractClassFromFunctionPointer<T C::*> { using Class = C; };
0377 
0378     constexpr size_t getOffset(size_t o)
0379     {
0380         return o;
0381     }
0382     constexpr size_t getOffset(size_t (*offsetFn)())
0383     {
0384         return offsetFn();
0385     }
0386 }
0387 
0388 } // namespace QtPrivate
0389 
0390 QT_END_NAMESPACE
0391 
0392 #endif // QPROPERTYPRIVATE_H