Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmldata_p.h
Go to the documentation of this file.
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
30
31template <class Key, class T> class QHash;
32class QQmlEngine;
33class QQmlGuardImpl;
34class QQmlAbstractBinding;
35class QQmlBoundSignal;
36class QQmlContext;
37class QQmlPropertyCache;
38class QQmlContextData;
39class QQmlNotifier;
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_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 = nullptr;
153 QQmlBoundSignal *signalHandlers = nullptr;
154 std::vector<QQmlPropertyObserver> propertyObservers;
155
156 // Linked list for QQmlContext::contextObjects
157 QQmlData *nextContextObject = nullptr;
158 QQmlData**prevContextObject = nullptr;
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 = 0;
169 quint16 columnNumber = 0;
170
171 quint32 jsEngineId = 0; // 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 /* set if the deferred binding originates in an inline component,
185 necessary to adjust the compilationUnit; null if there was no
186 inline component */
187 QString inlineComponentName;
188 Q_DISABLE_COPY(DeferredData);
189 };
190 QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
191 QVector<DeferredData *> deferredData;
192
193 void deferData(int objectIndex, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &,
194 const QQmlRefPointer<QQmlContextData> &, const QString &inlineComponentName);
195 void releaseDeferredData();
196
197 QV4::WeakValue jsWrapper;
198
199 QQmlPropertyCache::ConstPtr propertyCache;
200
201 QQmlGuardImpl *guards = nullptr;
202
203 static QQmlData *get(QObjectPrivate *priv, bool create) {
204 // If QObjectData::isDeletingChildren is set then access to QObjectPrivate::declarativeData has
205 // to be avoided because QObjectPrivate::currentChildBeingDeleted is in use.
206 if (priv->isDeletingChildren || priv->wasDeleted) {
207 Q_ASSERT(!create);
208 return nullptr;
209 } else if (priv->declarativeData) {
210 return static_cast<QQmlData *>(priv->declarativeData);
211 } else if (create) {
212 return createQQmlData(priv);
213 } else {
214 return nullptr;
215 }
216 }
217
218 static QQmlData *get(const QObjectPrivate *priv) {
219 // If QObjectData::isDeletingChildren is set then access to QObjectPrivate::declarativeData has
220 // to be avoided because QObjectPrivate::currentChildBeingDeleted is in use.
221 if (priv->isDeletingChildren || priv->wasDeleted)
222 return nullptr;
223 if (priv->declarativeData)
224 return static_cast<QQmlData *>(priv->declarativeData);
225 return nullptr;
226 }
227
228 static QQmlData *get(QObject *object, bool create) {
229 return QQmlData::get(QObjectPrivate::get(object), create);
230 }
231
232 static QQmlData *get(const QObject *object) {
233 return QQmlData::get(QObjectPrivate::get(object));
234
235 }
236
237 static bool keepAliveDuringGarbageCollection(const QObject *object) {
238 QQmlData *ddata = get(object);
239 if (!ddata || ddata->indestructible || ddata->rootObjectInCreation)
240 return true;
241 return false;
242 }
243
244 bool hasExtendedData() const { return extendedData != nullptr; }
245 QHash<QQmlAttachedPropertiesFunc, QObject *> *attachedProperties() const;
246
247 static inline bool wasDeleted(const QObject *);
248 static inline bool wasDeleted(const QObjectPrivate *);
249
250 static void markAsDeleted(QObject *);
251 static void setQueuedForDeletion(QObject *);
252
253 static inline void flushPendingBinding(QObject *object, int coreIndex);
254 void flushPendingBinding(int coreIndex);
255
256 static QQmlPropertyCache::ConstPtr ensurePropertyCache(QObject *object)
257 {
258 QQmlData *ddata = QQmlData::get(object, /*create*/true);
259 if (Q_LIKELY(ddata->propertyCache))
260 return ddata->propertyCache;
261 return createPropertyCache(object);
262 }
263
264 Q_ALWAYS_INLINE static uint offsetForBit(int bit) { return static_cast<uint>(bit) / BitsPerType; }
265 Q_ALWAYS_INLINE static BindingBitsType bitFlagForBit(int bit) { return BindingBitsType(1) << (static_cast<uint>(bit) & (BitsPerType - 1)); }
266
267private:
268 // For attachedProperties
269 mutable QQmlDataExtended *extendedData = nullptr;
270
271 Q_NEVER_INLINE static QQmlData *createQQmlData(QObjectPrivate *priv);
272 Q_NEVER_INLINE static QQmlPropertyCache::ConstPtr createPropertyCache(QObject *object);
273
274 Q_ALWAYS_INLINE bool hasBitSet(int bit) const
275 {
276 uint offset = offsetForBit(bit);
277 if (bindingBitsArraySize <= offset)
278 return false;
279
280 const BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
281 return bits[offset] & bitFlagForBit(bit);
282 }
283
284 Q_ALWAYS_INLINE void clearBit(int bit)
285 {
286 uint offset = QQmlData::offsetForBit(bit);
287 if (bindingBitsArraySize > offset) {
288 BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
289 bits[offset] &= ~QQmlData::bitFlagForBit(bit);
290 }
291 }
292
293 Q_ALWAYS_INLINE void setBit(QObject *obj, int bit)
294 {
295 uint offset = QQmlData::offsetForBit(bit);
296 BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
297 if (Q_UNLIKELY(bindingBitsArraySize <= offset))
298 bits = growBits(obj, bit);
299 bits[offset] |= QQmlData::bitFlagForBit(bit);
300 }
301
302 Q_NEVER_INLINE BindingBitsType *growBits(QObject *obj, int bit);
303
304 Q_DISABLE_COPY_MOVE(QQmlData);
305};
306
307bool QQmlData::wasDeleted(const QObjectPrivate *priv)
308{
309 if (!priv || priv->wasDeleted || priv->isDeletingChildren)
310 return true;
311
312 const QQmlData *ddata = QQmlData::get(priv);
313 return ddata && ddata->isQueuedForDeletion;
314}
315
316bool QQmlData::wasDeleted(const QObject *object)
317{
318 if (!object)
319 return true;
320
321 const QObjectPrivate *priv = QObjectPrivate::get(object);
322 return QQmlData::wasDeleted(priv);
323}
324
325inline bool isIndexInConnectionMask(quint64 connectionMask, int index)
326{
327 return connectionMask & (1ULL << quint64(index % 64));
328}
329
330QQmlNotifierEndpoint *QQmlData::notify(int index) const
331{
332 // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
333
334 Q_ASSERT(index <= 0xFFFF);
335
336 NotifyList *list = notifyList.loadRelaxed();
337 if (!list || !isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index))
338 return nullptr;
339
340 if (index < list->notifiesSize)
341 return list->notifies[index];
342
343 if (index <= list->maximumTodoIndex) {
344 list->layout();
345 if (index < list->notifiesSize)
346 return list->notifies[index];
347 }
348
349 return nullptr;
350}
351
352/*
353 The index MUST be in the range returned by QObjectPrivate::signalIndex()
354 This is different than the index returned by QMetaMethod::methodIndex()
355*/
356inline bool QQmlData::signalHasEndpoint(int index) const
357{
358 // This can be called from any thread.
359 // We still use relaxed semantics. If we're on a thread different from the "home" thread
360 // of the QQmlData, two interesting things might happen:
361 //
362 // 1. The list might go away while we hold it. In that case we are dealing with an object whose
363 // QObject dtor is being executed concurrently. This is UB already without the notify lists.
364 // Therefore, we don't need to consider it.
365 // 2. The connectionMask may be amended or zeroed while we are looking at it. In that case
366 // we "misreport" the endpoint. Since ordering of events across threads is inherently
367 // nondeterministic, either result is correct in that case. We can accept it.
368
369 NotifyList *list = notifyList.loadRelaxed();
370 return list && isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index);
371}
372
373bool QQmlData::hasBindingBit(int coreIndex) const
374{
375 Q_ASSERT(coreIndex >= 0);
376 Q_ASSERT(coreIndex <= 0xffff);
377
378 return hasBitSet(coreIndex * 2);
379}
380
381void QQmlData::setBindingBit(QObject *obj, int coreIndex)
382{
383 Q_ASSERT(coreIndex >= 0);
384 Q_ASSERT(coreIndex <= 0xffff);
385 setBit(obj, coreIndex * 2);
386}
387
388void QQmlData::clearBindingBit(int coreIndex)
389{
390 Q_ASSERT(coreIndex >= 0);
391 Q_ASSERT(coreIndex <= 0xffff);
392 clearBit(coreIndex * 2);
393}
394
395bool QQmlData::hasPendingBindingBit(int coreIndex) const
396{
397 Q_ASSERT(coreIndex >= 0);
398 Q_ASSERT(coreIndex <= 0xffff);
399
400 return hasBitSet(coreIndex * 2 + 1);
401}
402
403void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex)
404{
405 Q_ASSERT(coreIndex >= 0);
406 Q_ASSERT(coreIndex <= 0xffff);
407 setBit(obj, coreIndex * 2 + 1);
408}
409
410void QQmlData::clearPendingBindingBit(int coreIndex)
411{
412 Q_ASSERT(coreIndex >= 0);
413 Q_ASSERT(coreIndex <= 0xffff);
414 clearBit(coreIndex * 2 + 1);
415}
416
417void QQmlData::flushPendingBinding(QObject *object, int coreIndex)
418{
419 QQmlData *data = QQmlData::get(object, false);
420 if (data && data->hasPendingBindingBit(coreIndex))
421 data->flushPendingBinding(coreIndex);
422}
423
424QT_END_NAMESPACE
425
426#endif // QQMLDATA_P_H
Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags) override final
Q_ALWAYS_INLINE bool write(void *result, QMetaType type, bool isUndefined, QQmlPropertyData::WriteFlags flags) override final
Q_ALWAYS_INLINE bool doStore(T value, const QQmlPropertyData *pd, QQmlPropertyData::WriteFlags flags) const
QObjectPointerBinding(QMetaType propertyType)
Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags) override final
Q_ALWAYS_INLINE bool write(void *result, QMetaType type, bool isUndefined, QQmlPropertyData::WriteFlags flags) override final
The QQmlScriptString class encapsulates a script and its context.
QString bindingValue() const override
QQmlSourceLocation sourceLocation() const override final
QQmlTranslationBindingFromBinding(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit, const QV4::CompiledData::Binding *binding)
QQmlSourceLocation sourceLocation() const override final
QQmlTranslationBindingFromTranslationInfo(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit, const QQmlTranslation &translationData, quint16 line, quint16 column)
virtual QString bindingValue() const override
QQmlTranslationBinding(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit)
void doUpdate(const DeleteWatcher &watcher, QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) override final
static void onLanguageChange(QPropertyObserver *observer, QUntypedPropertyData *)
virtual QString bindingValue() const =0
bool hasDependencies() const override final
Combined button and popup list for selecting options.
Definition qjsvalue.h:23
bool isIndexInConnectionMask(quint64 connectionMask, int index)
Definition qqmldata_p.h:325