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