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