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 * in one of the engines. If multiple engines are used, we might not actually have
110 * a const wrapper. However, if the flag is not set, we can avoid some additional
111 * lookups in the map.
112 */
113 quint32 hasConstWrapper: 1;
114 quint32 dummy:7;
115
116 // When bindingBitsSize < sizeof(ptr), we store the binding bit flags inside
117 // bindingBitsValue. When we need more than sizeof(ptr) bits, we allocated
118 // sufficient space and use bindingBits to point to it.
119 quint32 bindingBitsArraySize : 16;
120 typedef quintptr BindingBitsType;
121 enum {
122 BitsPerType = sizeof(BindingBitsType) * 8,
123 InlineBindingArraySize = 2
124 };
125 union {
126 BindingBitsType *bindingBits;
127 BindingBitsType bindingBitsValue[InlineBindingArraySize];
128 };
129
130 struct NotifyList {
131 QAtomicInteger<quint64> connectionMask;
132 QQmlNotifierEndpoint *todo = nullptr;
133 QQmlNotifierEndpoint**notifies = nullptr;
134 quint16 maximumTodoIndex = 0;
135 quint16 notifiesSize = 0;
136 void layout();
137 private:
138 void layout(QQmlNotifierEndpoint*);
139 };
140 QAtomicPointer<NotifyList> notifyList;
141
142 inline QQmlNotifierEndpoint *notify(int index) const;
143 void addNotify(int index, QQmlNotifierEndpoint *);
144 int endpointCount(int index);
145 bool signalHasEndpoint(int index) const;
146
147 enum class DeleteNotifyList { Yes, No };
148 void disconnectNotifiers(DeleteNotifyList doDelete);
149
150 // The context that created the C++ object; not refcounted to prevent cycles
151 QQmlContextData *context = nullptr;
152 // The outermost context in which this object lives; not refcounted to prevent cycles
153 QQmlContextData *outerContext = nullptr;
154 QQmlRefPointer<QQmlContextData> ownContext;
155
156 QQmlAbstractBinding *bindings = nullptr;
157 QQmlBoundSignal *signalHandlers = nullptr;
158 std::vector<QQmlPropertyObserver> propertyObservers;
159
160 // Linked list for QQmlContext::contextObjects
161 QQmlData *nextContextObject = nullptr;
162 QQmlData**prevContextObject = nullptr;
163
164 inline bool hasBindingBit(int) const;
165 inline void setBindingBit(QObject *obj, int);
166 inline void clearBindingBit(int);
167
168 inline bool hasPendingBindingBit(int index) const;
169 inline void setPendingBindingBit(QObject *obj, int);
170 inline void clearPendingBindingBit(int);
171
172 quint16 lineNumber = 0;
173 quint16 columnNumber = 0;
174
175 quint32 jsEngineId = 0; // id of the engine that created the jsWrapper
176
177 struct DeferredData {
178 DeferredData();
179 ~DeferredData();
180 unsigned int deferredIdx;
181 QMultiHash<int, const QV4::CompiledData::Binding *> bindings;
182
183 // Not always the same as the other compilation unit
184 QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
185
186 // Could be either context or outerContext
187 QQmlRefPointer<QQmlContextData> context;
188 /* set if the deferred binding originates in an inline component,
189 necessary to adjust the compilationUnit; null if there was no
190 inline component */
191 QString inlineComponentName;
192 Q_DISABLE_COPY(DeferredData);
193 };
194 QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
195 QVector<DeferredData *> deferredData;
196
197 void deferData(int objectIndex, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &,
198 const QQmlRefPointer<QQmlContextData> &, const QString &inlineComponentName);
199 void releaseDeferredData();
200
201 QV4::WeakValue jsWrapper;
202
203 QQmlPropertyCache::ConstPtr propertyCache;
204
205 QQmlGuardImpl *guards = nullptr;
206
207 static QQmlData *get(QObjectPrivate *priv, bool create) {
208 // If QObjectData::isDeletingChildren is set then access to QObjectPrivate::declarativeData has
209 // to be avoided because QObjectPrivate::currentChildBeingDeleted is in use.
210 if (priv->isDeletingChildren || priv->wasDeleted) {
211 Q_ASSERT(!create);
212 return nullptr;
213 } else if (priv->declarativeData) {
214 return static_cast<QQmlData *>(priv->declarativeData);
215 } else if (create) {
216 return createQQmlData(priv);
217 } else {
218 return nullptr;
219 }
220 }
221
222 static QQmlData *get(const QObjectPrivate *priv) {
223 // If QObjectData::isDeletingChildren is set then access to QObjectPrivate::declarativeData has
224 // to be avoided because QObjectPrivate::currentChildBeingDeleted is in use.
225 if (priv->isDeletingChildren || priv->wasDeleted)
226 return nullptr;
227 if (priv->declarativeData)
228 return static_cast<QQmlData *>(priv->declarativeData);
229 return nullptr;
230 }
231
232 static QQmlData *get(QObject *object, bool create) {
233 return QQmlData::get(QObjectPrivate::get(object), create);
234 }
235
236 static QQmlData *get(const QObject *object) {
237 return QQmlData::get(QObjectPrivate::get(object));
238
239 }
240
241 static bool keepAliveDuringGarbageCollection(const QObject *object) {
242 QQmlData *ddata = get(object);
243 if (!ddata || ddata->indestructible || ddata->rootObjectInCreation)
244 return true;
245 return false;
246 }
247
248 bool hasExtendedData() const { return extendedData != nullptr; }
249 QHash<QQmlAttachedPropertiesFunc, QObject *> *attachedProperties() const;
250
251 static inline bool wasDeleted(const QObject *);
252 static inline bool wasDeleted(const QObjectPrivate *);
253
254 static void markAsDeleted(QObject *);
255 static void setQueuedForDeletion(QObject *);
256
257 static inline void flushPendingBinding(QObject *object, int coreIndex);
258 void flushPendingBinding(int coreIndex);
259
260 static QQmlPropertyCache::ConstPtr ensurePropertyCache(QObject *object)
261 {
262 QQmlData *ddata = QQmlData::get(object, /*create*/true);
263 if (Q_LIKELY(ddata->propertyCache))
264 return ddata->propertyCache;
265 return createPropertyCache(object);
266 }
267
268 Q_ALWAYS_INLINE static uint offsetForBit(int bit) { return static_cast<uint>(bit) / BitsPerType; }
269 Q_ALWAYS_INLINE static BindingBitsType bitFlagForBit(int bit) { return BindingBitsType(1) << (static_cast<uint>(bit) & (BitsPerType - 1)); }
270
271private:
272 // For attachedProperties
273 mutable QQmlDataExtended *extendedData = nullptr;
274
275 Q_NEVER_INLINE static QQmlData *createQQmlData(QObjectPrivate *priv);
276 Q_NEVER_INLINE static QQmlPropertyCache::ConstPtr createPropertyCache(QObject *object);
277
278 Q_ALWAYS_INLINE bool hasBitSet(int bit) const
279 {
280 uint offset = offsetForBit(bit);
281 if (bindingBitsArraySize <= offset)
282 return false;
283
284 const BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
285 return bits[offset] & bitFlagForBit(bit);
286 }
287
288 Q_ALWAYS_INLINE void clearBit(int bit)
289 {
290 uint offset = QQmlData::offsetForBit(bit);
291 if (bindingBitsArraySize > offset) {
292 BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
293 bits[offset] &= ~QQmlData::bitFlagForBit(bit);
294 }
295 }
296
297 Q_ALWAYS_INLINE void setBit(QObject *obj, int bit)
298 {
299 uint offset = QQmlData::offsetForBit(bit);
300 BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
301 if (Q_UNLIKELY(bindingBitsArraySize <= offset))
302 bits = growBits(obj, bit);
303 bits[offset] |= QQmlData::bitFlagForBit(bit);
304 }
305
306 Q_NEVER_INLINE BindingBitsType *growBits(QObject *obj, int bit);
307
308 Q_DISABLE_COPY_MOVE(QQmlData);
309};
310
311bool QQmlData::wasDeleted(const QObjectPrivate *priv)
312{
313 if (!priv || priv->wasDeleted || priv->isDeletingChildren)
314 return true;
315
316 const QQmlData *ddata = QQmlData::get(priv);
317 return ddata && ddata->isQueuedForDeletion;
318}
319
320bool QQmlData::wasDeleted(const QObject *object)
321{
322 if (!object)
323 return true;
324
325 const QObjectPrivate *priv = QObjectPrivate::get(object);
326 return QQmlData::wasDeleted(priv);
327}
328
329inline bool isIndexInConnectionMask(quint64 connectionMask, int index)
330{
331 return connectionMask & (1ULL << quint64(index % 64));
332}
333
334QQmlNotifierEndpoint *QQmlData::notify(int index) const
335{
336 // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
337
338 Q_ASSERT(index <= 0xFFFF);
339
340 NotifyList *list = notifyList.loadRelaxed();
341 if (!list || !isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index))
342 return nullptr;
343
344 if (index < list->notifiesSize)
345 return list->notifies[index];
346
347 if (index <= list->maximumTodoIndex) {
348 list->layout();
349 if (index < list->notifiesSize)
350 return list->notifies[index];
351 }
352
353 return nullptr;
354}
355
356/*
357 The index MUST be in the range returned by QObjectPrivate::signalIndex()
358 This is different than the index returned by QMetaMethod::methodIndex()
359*/
360inline bool QQmlData::signalHasEndpoint(int index) const
361{
362 // This can be called from any thread.
363 // We still use relaxed semantics. If we're on a thread different from the "home" thread
364 // of the QQmlData, two interesting things might happen:
365 //
366 // 1. The list might go away while we hold it. In that case we are dealing with an object whose
367 // QObject dtor is being executed concurrently. This is UB already without the notify lists.
368 // Therefore, we don't need to consider it.
369 // 2. The connectionMask may be amended or zeroed while we are looking at it. In that case
370 // we "misreport" the endpoint. Since ordering of events across threads is inherently
371 // nondeterministic, either result is correct in that case. We can accept it.
372
373 NotifyList *list = notifyList.loadRelaxed();
374 return list && isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index);
375}
376
377bool QQmlData::hasBindingBit(int coreIndex) const
378{
379 Q_ASSERT(coreIndex >= 0);
380 Q_ASSERT(coreIndex <= 0xffff);
381
382 return hasBitSet(coreIndex * 2);
383}
384
385void QQmlData::setBindingBit(QObject *obj, int coreIndex)
386{
387 Q_ASSERT(coreIndex >= 0);
388 Q_ASSERT(coreIndex <= 0xffff);
389 setBit(obj, coreIndex * 2);
390}
391
392void QQmlData::clearBindingBit(int coreIndex)
393{
394 Q_ASSERT(coreIndex >= 0);
395 Q_ASSERT(coreIndex <= 0xffff);
396 clearBit(coreIndex * 2);
397}
398
399bool QQmlData::hasPendingBindingBit(int coreIndex) const
400{
401 Q_ASSERT(coreIndex >= 0);
402 Q_ASSERT(coreIndex <= 0xffff);
403
404 return hasBitSet(coreIndex * 2 + 1);
405}
406
407void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex)
408{
409 Q_ASSERT(coreIndex >= 0);
410 Q_ASSERT(coreIndex <= 0xffff);
411 setBit(obj, coreIndex * 2 + 1);
412}
413
414void QQmlData::clearPendingBindingBit(int coreIndex)
415{
416 Q_ASSERT(coreIndex >= 0);
417 Q_ASSERT(coreIndex <= 0xffff);
418 clearBit(coreIndex * 2 + 1);
419}
420
421void QQmlData::flushPendingBinding(QObject *object, int coreIndex)
422{
423 QQmlData *data = QQmlData::get(object, false);
424 if (data && data->hasPendingBindingBit(coreIndex))
425 data->flushPendingBinding(coreIndex);
426}
427
428QT_END_NAMESPACE
429
430#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
Definition qjsvalue.h:23
bool isIndexInConnectionMask(quint64 connectionMask, int index)
Definition qqmldata_p.h:329