// Copyright (C) 2016 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 #ifndef QQMLDATA_P_H #define QQMLDATA_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 #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE template class QHash; class QQmlEngine; class QQmlGuardImpl; class QQmlAbstractBinding; class QQmlBoundSignal; class QQmlContext; class QQmlPropertyCache; class QQmlContextData; class QQmlNotifier; class QQmlDataExtended; class QQmlNotifierEndpoint; class QQmlPropertyObserver; namespace QV4 { class ExecutableCompilationUnit; namespace CompiledData { struct Binding; } } // This class is structured in such a way, that simply zero'ing it is the // default state for elemental object allocations. This is crucial in the // workings of the QQmlInstruction::CreateSimpleObject instruction. // Don't change anything here without first considering that case! class Q_QML_PRIVATE_EXPORT QQmlData : public QAbstractDeclarativeData { public: enum Ownership { DoesNotOwnMemory, OwnsMemory }; QQmlData(Ownership ownership); ~QQmlData(); static inline void init() { static bool initialized = false; if (!initialized) { initialized = true; QAbstractDeclarativeData::destroyed = destroyed; QAbstractDeclarativeData::signalEmitted = signalEmitted; QAbstractDeclarativeData::receivers = receivers; QAbstractDeclarativeData::isSignalConnected = isSignalConnected; } } static void destroyed(QAbstractDeclarativeData *, QObject *); static void signalEmitted(QAbstractDeclarativeData *, QObject *, int, void **); static int receivers(QAbstractDeclarativeData *, const QObject *, int); static bool isSignalConnected(QAbstractDeclarativeData *, const QObject *, int); void destroyed(QObject *); void setImplicitDestructible() { if (!explicitIndestructibleSet) indestructible = false; } // If ownMemomry is true, the QQmlData was normally allocated. Otherwise it was allocated // with placement new and QQmlData::destroyed is not allowed to free the memory quint32 ownMemory:1; // indestructible is set if and only if the object has CppOwnership // This can be explicitly set with QJSEngine::setObjectOwnership // Top level objects generally have CppOwnership (see QQmlcCmponentprivate::beginCreate), // unless created by special methods like the QML component.createObject() function quint32 indestructible:1; // indestructible was explicitly set with setObjectOwnership // or the object is a top-level object quint32 explicitIndestructibleSet:1; // set when one QObject has been wrapped into QObjectWrapper in multiple engines // at the same time - a rather rare case quint32 hasTaintedV4Object:1; quint32 isQueuedForDeletion:1; /* * rootObjectInCreation should be true only when creating top level CPP and QML objects, * v4 GC will check this flag, only deletes the objects when rootObjectInCreation is false. */ quint32 rootObjectInCreation:1; // set when at least one of the object's properties is intercepted quint32 hasInterceptorMetaObject:1; quint32 hasVMEMetaObject:1; // If we have another wrapper for a const QObject * in the multiply wrapped QObjects. quint32 hasConstWrapper: 1; quint32 dummy:7; // When bindingBitsSize < sizeof(ptr), we store the binding bit flags inside // bindingBitsValue. When we need more than sizeof(ptr) bits, we allocated // sufficient space and use bindingBits to point to it. quint32 bindingBitsArraySize : 16; typedef quintptr BindingBitsType; enum { BitsPerType = sizeof(BindingBitsType) * 8, InlineBindingArraySize = 2 }; union { BindingBitsType *bindingBits; BindingBitsType bindingBitsValue[InlineBindingArraySize]; }; struct NotifyList { quint64 connectionMask; quint16 maximumTodoIndex; quint16 notifiesSize; QQmlNotifierEndpoint *todo; QQmlNotifierEndpoint**notifies; void layout(); private: void layout(QQmlNotifierEndpoint*); }; NotifyList *notifyList = nullptr; inline QQmlNotifierEndpoint *notify(int index); void addNotify(int index, QQmlNotifierEndpoint *); int endpointCount(int index); bool signalHasEndpoint(int index) const; void disconnectNotifiers(); // The context that created the C++ object; not refcounted to prevent cycles QQmlContextData *context = nullptr; // The outermost context in which this object lives; not refcounted to prevent cycles QQmlContextData *outerContext = nullptr; QQmlRefPointer ownContext; QQmlAbstractBinding *bindings = nullptr; QQmlBoundSignal *signalHandlers = nullptr; std::vector propertyObservers; // Linked list for QQmlContext::contextObjects QQmlData *nextContextObject = nullptr; QQmlData**prevContextObject = nullptr; inline bool hasBindingBit(int) const; inline void setBindingBit(QObject *obj, int); inline void clearBindingBit(int); inline bool hasPendingBindingBit(int index) const; inline void setPendingBindingBit(QObject *obj, int); inline void clearPendingBindingBit(int); quint16 lineNumber = 0; quint16 columnNumber = 0; quint32 jsEngineId = 0; // id of the engine that created the jsWrapper struct DeferredData { DeferredData(); ~DeferredData(); unsigned int deferredIdx; QMultiHash bindings; // Not always the same as the other compilation unit QQmlRefPointer compilationUnit; // Could be either context or outerContext QQmlRefPointer context; Q_DISABLE_COPY(DeferredData); }; QQmlRefPointer compilationUnit; QVector deferredData; void deferData(int objectIndex, const QQmlRefPointer &, const QQmlRefPointer &); void releaseDeferredData(); QV4::WeakValue jsWrapper; QQmlPropertyCache::ConstPtr propertyCache; QQmlGuardImpl *guards = 0; static QQmlData *get(QObjectPrivate *priv, bool create) { // If QObjectData::isDeletingChildren is set then access to QObjectPrivate::declarativeData has // to be avoided because QObjectPrivate::currentChildBeingDeleted is in use. if (priv->isDeletingChildren || priv->wasDeleted) { Q_ASSERT(!create); return nullptr; } else if (priv->declarativeData) { return static_cast(priv->declarativeData); } else if (create) { return createQQmlData(priv); } else { return nullptr; } } static QQmlData *get(const QObjectPrivate *priv) { // If QObjectData::isDeletingChildren is set then access to QObjectPrivate::declarativeData has // to be avoided because QObjectPrivate::currentChildBeingDeleted is in use. if (priv->isDeletingChildren || priv->wasDeleted) return nullptr; if (priv->declarativeData) return static_cast(priv->declarativeData); return nullptr; } static QQmlData *get(QObject *object, bool create) { return QQmlData::get(QObjectPrivate::get(object), create); } static QQmlData *get(const QObject *object) { return QQmlData::get(QObjectPrivate::get(object)); } static bool keepAliveDuringGarbageCollection(const QObject *object) { QQmlData *ddata = get(object); if (!ddata || ddata->indestructible || ddata->rootObjectInCreation) return true; return false; } bool hasExtendedData() const { return extendedData != nullptr; } QHash *attachedProperties() const; static inline bool wasDeleted(const QObject *); static inline bool wasDeleted(const QObjectPrivate *); static void markAsDeleted(QObject *); static void setQueuedForDeletion(QObject *); static inline void flushPendingBinding(QObject *object, int coreIndex); void flushPendingBinding(int coreIndex); static QQmlPropertyCache::ConstPtr ensurePropertyCache(QObject *object) { QQmlData *ddata = QQmlData::get(object, /*create*/true); if (Q_LIKELY(ddata->propertyCache)) return ddata->propertyCache; return createPropertyCache(object); } Q_ALWAYS_INLINE static uint offsetForBit(int bit) { return static_cast(bit) / BitsPerType; } Q_ALWAYS_INLINE static BindingBitsType bitFlagForBit(int bit) { return BindingBitsType(1) << (static_cast(bit) & (BitsPerType - 1)); } private: // For attachedProperties mutable QQmlDataExtended *extendedData = nullptr; Q_NEVER_INLINE static QQmlData *createQQmlData(QObjectPrivate *priv); Q_NEVER_INLINE static QQmlPropertyCache::ConstPtr createPropertyCache(QObject *object); Q_ALWAYS_INLINE bool hasBitSet(int bit) const { uint offset = offsetForBit(bit); if (bindingBitsArraySize <= offset) return false; const BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits; return bits[offset] & bitFlagForBit(bit); } Q_ALWAYS_INLINE void clearBit(int bit) { uint offset = QQmlData::offsetForBit(bit); if (bindingBitsArraySize > offset) { BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits; bits[offset] &= ~QQmlData::bitFlagForBit(bit); } } Q_ALWAYS_INLINE void setBit(QObject *obj, int bit) { uint offset = QQmlData::offsetForBit(bit); BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits; if (Q_UNLIKELY(bindingBitsArraySize <= offset)) bits = growBits(obj, bit); bits[offset] |= QQmlData::bitFlagForBit(bit); } Q_NEVER_INLINE BindingBitsType *growBits(QObject *obj, int bit); Q_DISABLE_COPY_MOVE(QQmlData); }; bool QQmlData::wasDeleted(const QObjectPrivate *priv) { if (!priv || priv->wasDeleted || priv->isDeletingChildren) return true; const QQmlData *ddata = QQmlData::get(priv); return ddata && ddata->isQueuedForDeletion; } bool QQmlData::wasDeleted(const QObject *object) { if (!object) return true; const QObjectPrivate *priv = QObjectPrivate::get(object); return QQmlData::wasDeleted(priv); } QQmlNotifierEndpoint *QQmlData::notify(int index) { Q_ASSERT(index <= 0xFFFF); if (!notifyList || !(notifyList->connectionMask & (1ULL << quint64(index % 64)))) { return nullptr; } else if (index < notifyList->notifiesSize) { return notifyList->notifies[index]; } else if (index <= notifyList->maximumTodoIndex) { notifyList->layout(); } if (index < notifyList->notifiesSize) { return notifyList->notifies[index]; } else { return nullptr; } } /* The index MUST be in the range returned by QObjectPrivate::signalIndex() This is different than the index returned by QMetaMethod::methodIndex() */ inline bool QQmlData::signalHasEndpoint(int index) const { return notifyList && (notifyList->connectionMask & (1ULL << quint64(index % 64))); } bool QQmlData::hasBindingBit(int coreIndex) const { Q_ASSERT(coreIndex >= 0); Q_ASSERT(coreIndex <= 0xffff); return hasBitSet(coreIndex * 2); } void QQmlData::setBindingBit(QObject *obj, int coreIndex) { Q_ASSERT(coreIndex >= 0); Q_ASSERT(coreIndex <= 0xffff); setBit(obj, coreIndex * 2); } void QQmlData::clearBindingBit(int coreIndex) { Q_ASSERT(coreIndex >= 0); Q_ASSERT(coreIndex <= 0xffff); clearBit(coreIndex * 2); } bool QQmlData::hasPendingBindingBit(int coreIndex) const { Q_ASSERT(coreIndex >= 0); Q_ASSERT(coreIndex <= 0xffff); return hasBitSet(coreIndex * 2 + 1); } void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex) { Q_ASSERT(coreIndex >= 0); Q_ASSERT(coreIndex <= 0xffff); setBit(obj, coreIndex * 2 + 1); } void QQmlData::clearPendingBindingBit(int coreIndex) { Q_ASSERT(coreIndex >= 0); Q_ASSERT(coreIndex <= 0xffff); clearBit(coreIndex * 2 + 1); } void QQmlData::flushPendingBinding(QObject *object, int coreIndex) { QQmlData *data = QQmlData::get(object, false); if (data && data->hasPendingBindingBit(coreIndex)) data->flushPendingBinding(coreIndex); } QT_END_NAMESPACE #endif // QQMLDATA_P_H