1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QQMLDATA_P_H
5#define QQMLDATA_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <private/qtqmlglobal_p.h>
19#include <private/qobject_p.h>
20#include <private/qqmlpropertyindex_p.h>
21#include <private/qv4value_p.h>
22#include <private/qv4persistent_p.h>
23#include <private/qqmlrefcount_p.h>
24#include <private/qqmlpropertycache_p.h>
25#include <qqmlprivate.h>
26#include <qjsengine.h>
27#include <qvector.h>
28
29QT_BEGIN_NAMESPACE
30
31template <class Key, class T> class QHash;
32class QQmlEngine;
33class QQmlGuardImpl;
34class QQmlAbstractBinding;
35class QQmlBoundSignal;
36class QQmlContext;
37class QQmlPropertyCache;
38class QQmlContextData;
39class QQmlNotifier;
40class QQmlDataExtended;
41class QQmlNotifierEndpoint;
42class QQmlPropertyObserver;
43
44namespace QV4 {
45class ExecutableCompilationUnit;
46namespace CompiledData {
47struct Binding;
48}
49}
50
51// This class is structured in such a way, that simply zero'ing it is the
52// default state for elemental object allocations. This is crucial in the
53// workings of the QQmlInstruction::CreateSimpleObject instruction.
54// Don't change anything here without first considering that case!
55class Q_QML_PRIVATE_EXPORT QQmlData : public QAbstractDeclarativeData
56{
57public:
58 enum Ownership { DoesNotOwnMemory, OwnsMemory };
59
60 QQmlData(Ownership ownership);
61 ~QQmlData();
62
63 static inline void init() {
64 static bool initialized = false;
65 if (!initialized) {
66 initialized = true;
67 QAbstractDeclarativeData::destroyed = destroyed;
68 QAbstractDeclarativeData::signalEmitted = signalEmitted;
69 QAbstractDeclarativeData::receivers = receivers;
70 QAbstractDeclarativeData::isSignalConnected = isSignalConnected;
71 }
72 }
73
74 static void destroyed(QAbstractDeclarativeData *, QObject *);
75 static void signalEmitted(QAbstractDeclarativeData *, QObject *, int, void **);
76 static int receivers(QAbstractDeclarativeData *, const QObject *, int);
77 static bool isSignalConnected(QAbstractDeclarativeData *, const QObject *, int);
78
79 void destroyed(QObject *);
80
81 void setImplicitDestructible() {
82 if (!explicitIndestructibleSet) indestructible = false;
83 }
84
85 // If ownMemomry is true, the QQmlData was normally allocated. Otherwise it was allocated
86 // with placement new and QQmlData::destroyed is not allowed to free the memory
87 quint32 ownMemory:1;
88 // indestructible is set if and only if the object has CppOwnership
89 // This can be explicitly set with QJSEngine::setObjectOwnership
90 // Top level objects generally have CppOwnership (see QQmlcCmponentprivate::beginCreate),
91 // unless created by special methods like the QML component.createObject() function
92 quint32 indestructible:1;
93 // indestructible was explicitly set with setObjectOwnership
94 // or the object is a top-level object
95 quint32 explicitIndestructibleSet:1;
96 // set when one QObject has been wrapped into QObjectWrapper in multiple engines
97 // at the same time - a rather rare case
98 quint32 hasTaintedV4Object:1;
99 quint32 isQueuedForDeletion:1;
100 /*
101 * rootObjectInCreation should be true only when creating top level CPP and QML objects,
102 * v4 GC will check this flag, only deletes the objects when rootObjectInCreation is false.
103 */
104 quint32 rootObjectInCreation:1;
105 // set when at least one of the object's properties is intercepted
106 quint32 hasInterceptorMetaObject:1;
107 quint32 hasVMEMetaObject:1;
108 // If we have another wrapper for a const QObject * in the multiply wrapped QObjects.
109 quint32 hasConstWrapper: 1;
110 quint32 dummy:7;
111
112 // When bindingBitsSize < sizeof(ptr), we store the binding bit flags inside
113 // bindingBitsValue. When we need more than sizeof(ptr) bits, we allocated
114 // sufficient space and use bindingBits to point to it.
115 quint32 bindingBitsArraySize : 16;
116 typedef quintptr BindingBitsType;
117 enum {
118 BitsPerType = sizeof(BindingBitsType) * 8,
119 InlineBindingArraySize = 2
120 };
121 union {
122 BindingBitsType *bindingBits;
123 BindingBitsType bindingBitsValue[InlineBindingArraySize];
124 };
125
126 struct NotifyList {
127 QAtomicInteger<quint64> connectionMask;
128 QQmlNotifierEndpoint *todo = nullptr;
129 QQmlNotifierEndpoint**notifies = nullptr;
130 quint16 maximumTodoIndex = 0;
131 quint16 notifiesSize = 0;
132 void layout();
133 private:
134 void layout(QQmlNotifierEndpoint*);
135 };
136 QAtomicPointer<NotifyList> notifyList;
137
138 inline QQmlNotifierEndpoint *notify(int index) const;
139 void addNotify(int index, QQmlNotifierEndpoint *);
140 int endpointCount(int index);
141 bool signalHasEndpoint(int index) const;
142
143 enum class DeleteNotifyList { Yes, No };
144 void disconnectNotifiers(DeleteNotifyList doDelete);
145
146 // The context that created the C++ object; not refcounted to prevent cycles
147 QQmlContextData *context = nullptr;
148 // The outermost context in which this object lives; not refcounted to prevent cycles
149 QQmlContextData *outerContext = nullptr;
150 QQmlRefPointer<QQmlContextData> ownContext;
151
152 QQmlAbstractBinding *bindings;
153 QQmlBoundSignal *signalHandlers;
154 std::vector<QQmlPropertyObserver> propertyObservers;
155
156 // Linked list for QQmlContext::contextObjects
157 QQmlData *nextContextObject;
158 QQmlData**prevContextObject;
159
160 inline bool hasBindingBit(int) const;
161 inline void setBindingBit(QObject *obj, int);
162 inline void clearBindingBit(int);
163
164 inline bool hasPendingBindingBit(int index) const;
165 inline void setPendingBindingBit(QObject *obj, int);
166 inline void clearPendingBindingBit(int);
167
168 quint16 lineNumber;
169 quint16 columnNumber;
170
171 quint32 jsEngineId; // id of the engine that created the jsWrapper
172
173 struct DeferredData {
174 DeferredData();
175 ~DeferredData();
176 unsigned int deferredIdx;
177 QMultiHash<int, const QV4::CompiledData::Binding *> bindings;
178
179 // Not always the same as the other compilation unit
180 QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
181
182 // Could be either context or outerContext
183 QQmlRefPointer<QQmlContextData> context;
184 Q_DISABLE_COPY(DeferredData);
185 };
186 QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
187 QVector<DeferredData *> deferredData;
188
189 void deferData(int objectIndex, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &,
190 const QQmlRefPointer<QQmlContextData> &);
191 void releaseDeferredData();
192
193 QV4::WeakValue jsWrapper;
194
195 QQmlPropertyCache::ConstPtr propertyCache;
196
197 QQmlGuardImpl *guards = nullptr;
198
199 static QQmlData *get(QObjectPrivate *priv, bool create) {
200 // If QObjectData::isDeletingChildren is set then access to QObjectPrivate::declarativeData has
201 // to be avoided because QObjectPrivate::currentChildBeingDeleted is in use.
202 if (priv->isDeletingChildren || priv->wasDeleted) {
203 Q_ASSERT(!create);
204 return nullptr;
205 } else if (priv->declarativeData) {
206 return static_cast<QQmlData *>(priv->declarativeData);
207 } else if (create) {
208 return createQQmlData(priv);
209 } else {
210 return nullptr;
211 }
212 }
213
214 static QQmlData *get(const QObjectPrivate *priv) {
215 // If QObjectData::isDeletingChildren is set then access to QObjectPrivate::declarativeData has
216 // to be avoided because QObjectPrivate::currentChildBeingDeleted is in use.
217 if (priv->isDeletingChildren || priv->wasDeleted)
218 return nullptr;
219 if (priv->declarativeData)
220 return static_cast<QQmlData *>(priv->declarativeData);
221 return nullptr;
222 }
223
224 static QQmlData *get(QObject *object, bool create) {
225 return QQmlData::get(priv: QObjectPrivate::get(o: object), create);
226 }
227
228 static QQmlData *get(const QObject *object) {
229 return QQmlData::get(priv: QObjectPrivate::get(o: object));
230
231 }
232
233 static bool keepAliveDuringGarbageCollection(const QObject *object) {
234 QQmlData *ddata = get(object);
235 if (!ddata || ddata->indestructible || ddata->rootObjectInCreation)
236 return true;
237 return false;
238 }
239
240 bool hasExtendedData() const { return extendedData != nullptr; }
241 QHash<QQmlAttachedPropertiesFunc, QObject *> *attachedProperties() const;
242
243 static inline bool wasDeleted(const QObject *);
244 static inline bool wasDeleted(const QObjectPrivate *);
245
246 static void markAsDeleted(QObject *);
247 static void setQueuedForDeletion(QObject *);
248
249 static inline void flushPendingBinding(QObject *object, int coreIndex);
250 void flushPendingBinding(int coreIndex);
251
252 static QQmlPropertyCache::ConstPtr ensurePropertyCache(QObject *object)
253 {
254 QQmlData *ddata = QQmlData::get(object, /*create*/create: true);
255 if (Q_LIKELY(ddata->propertyCache))
256 return ddata->propertyCache;
257 return createPropertyCache(object);
258 }
259
260 Q_ALWAYS_INLINE static uint offsetForBit(int bit) { return static_cast<uint>(bit) / BitsPerType; }
261 Q_ALWAYS_INLINE static BindingBitsType bitFlagForBit(int bit) { return BindingBitsType(1) << (static_cast<uint>(bit) & (BitsPerType - 1)); }
262
263private:
264 // For attachedProperties
265 mutable QQmlDataExtended *extendedData;
266
267 Q_NEVER_INLINE static QQmlData *createQQmlData(QObjectPrivate *priv);
268 Q_NEVER_INLINE static QQmlPropertyCache::ConstPtr createPropertyCache(QObject *object);
269
270 Q_ALWAYS_INLINE bool hasBitSet(int bit) const
271 {
272 uint offset = offsetForBit(bit);
273 if (bindingBitsArraySize <= offset)
274 return false;
275
276 const BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
277 return bits[offset] & bitFlagForBit(bit);
278 }
279
280 Q_ALWAYS_INLINE void clearBit(int bit)
281 {
282 uint offset = QQmlData::offsetForBit(bit);
283 if (bindingBitsArraySize > offset) {
284 BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
285 bits[offset] &= ~QQmlData::bitFlagForBit(bit);
286 }
287 }
288
289 Q_ALWAYS_INLINE void setBit(QObject *obj, int bit)
290 {
291 uint offset = QQmlData::offsetForBit(bit);
292 BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
293 if (Q_UNLIKELY(bindingBitsArraySize <= offset))
294 bits = growBits(obj, bit);
295 bits[offset] |= QQmlData::bitFlagForBit(bit);
296 }
297
298 Q_NEVER_INLINE BindingBitsType *growBits(QObject *obj, int bit);
299
300 Q_DISABLE_COPY_MOVE(QQmlData);
301};
302
303bool QQmlData::wasDeleted(const QObjectPrivate *priv)
304{
305 if (!priv || priv->wasDeleted || priv->isDeletingChildren)
306 return true;
307
308 const QQmlData *ddata = QQmlData::get(priv);
309 return ddata && ddata->isQueuedForDeletion;
310}
311
312bool QQmlData::wasDeleted(const QObject *object)
313{
314 if (!object)
315 return true;
316
317 const QObjectPrivate *priv = QObjectPrivate::get(o: object);
318 return QQmlData::wasDeleted(priv);
319}
320
321inline bool isIndexInConnectionMask(quint64 connectionMask, int index)
322{
323 return connectionMask & (1ULL << quint64(index % 64));
324}
325
326QQmlNotifierEndpoint *QQmlData::notify(int index) const
327{
328 // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
329
330 Q_ASSERT(index <= 0xFFFF);
331
332 NotifyList *list = notifyList.loadRelaxed();
333 if (!list || !isIndexInConnectionMask(connectionMask: list->connectionMask.loadRelaxed(), index))
334 return nullptr;
335
336 if (index < list->notifiesSize)
337 return list->notifies[index];
338
339 if (index <= list->maximumTodoIndex) {
340 list->layout();
341 if (index < list->notifiesSize)
342 return list->notifies[index];
343 }
344
345 return nullptr;
346}
347
348/*
349 The index MUST be in the range returned by QObjectPrivate::signalIndex()
350 This is different than the index returned by QMetaMethod::methodIndex()
351*/
352inline bool QQmlData::signalHasEndpoint(int index) const
353{
354 // This can be called from any thread.
355 // We still use relaxed semantics. If we're on a thread different from the "home" thread
356 // of the QQmlData, two interesting things might happen:
357 //
358 // 1. The list might go away while we hold it. In that case we are dealing with an object whose
359 // QObject dtor is being executed concurrently. This is UB already without the notify lists.
360 // Therefore, we don't need to consider it.
361 // 2. The connectionMask may be amended or zeroed while we are looking at it. In that case
362 // we "misreport" the endpoint. Since ordering of events across threads is inherently
363 // nondeterministic, either result is correct in that case. We can accept it.
364
365 NotifyList *list = notifyList.loadRelaxed();
366 return list && isIndexInConnectionMask(connectionMask: list->connectionMask.loadRelaxed(), index);
367}
368
369bool QQmlData::hasBindingBit(int coreIndex) const
370{
371 Q_ASSERT(coreIndex >= 0);
372 Q_ASSERT(coreIndex <= 0xffff);
373
374 return hasBitSet(bit: coreIndex * 2);
375}
376
377void QQmlData::setBindingBit(QObject *obj, int coreIndex)
378{
379 Q_ASSERT(coreIndex >= 0);
380 Q_ASSERT(coreIndex <= 0xffff);
381 setBit(obj, bit: coreIndex * 2);
382}
383
384void QQmlData::clearBindingBit(int coreIndex)
385{
386 Q_ASSERT(coreIndex >= 0);
387 Q_ASSERT(coreIndex <= 0xffff);
388 clearBit(bit: coreIndex * 2);
389}
390
391bool QQmlData::hasPendingBindingBit(int coreIndex) const
392{
393 Q_ASSERT(coreIndex >= 0);
394 Q_ASSERT(coreIndex <= 0xffff);
395
396 return hasBitSet(bit: coreIndex * 2 + 1);
397}
398
399void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex)
400{
401 Q_ASSERT(coreIndex >= 0);
402 Q_ASSERT(coreIndex <= 0xffff);
403 setBit(obj, bit: coreIndex * 2 + 1);
404}
405
406void QQmlData::clearPendingBindingBit(int coreIndex)
407{
408 Q_ASSERT(coreIndex >= 0);
409 Q_ASSERT(coreIndex <= 0xffff);
410 clearBit(bit: coreIndex * 2 + 1);
411}
412
413void QQmlData::flushPendingBinding(QObject *object, int coreIndex)
414{
415 QQmlData *data = QQmlData::get(object, create: false);
416 if (data && data->hasPendingBindingBit(coreIndex))
417 data->flushPendingBinding(coreIndex);
418}
419
420QT_END_NAMESPACE
421
422#endif // QQMLDATA_P_H
423

source code of qtdeclarative/src/qml/qml/qqmldata_p.h