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 // The compiled-object index within compilationUnit for this object.
201 // Set for all QML-created objects, including those without a VMEMetaObject.
202 int cuObjectIndex = -1;
203
204 void deferData(int objectIndex, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &,
205 const QQmlRefPointer<QQmlContextData> &, const QString &inlineComponentName);
206 void releaseDeferredData();
207
208 QV4::WeakValue jsWrapper;
209
210 QQmlPropertyCache::ConstPtr propertyCache;
211
212 QQmlGuardImpl *guards = nullptr;
213
214 static QQmlData *get(QObjectPrivate *priv, bool create) {
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 Q_ASSERT(!create);
219 return nullptr;
220 } else if (priv->declarativeData) {
221 return static_cast<QQmlData *>(priv->declarativeData);
222 } else if (create) {
223 return createQQmlData(priv);
224 } else {
225 return nullptr;
226 }
227 }
228
229 static QQmlData *get(const QObjectPrivate *priv) {
230 // If QObjectData::isDeletingChildren is set then access to QObjectPrivate::declarativeData has
231 // to be avoided because QObjectPrivate::currentChildBeingDeleted is in use.
232 if (priv->isDeletingChildren || priv->wasDeleted)
233 return nullptr;
234 if (priv->declarativeData)
235 return static_cast<QQmlData *>(priv->declarativeData);
236 return nullptr;
237 }
238
239 static QQmlData *get(QObject *object, bool create) {
240 return QQmlData::get(QObjectPrivate::get(object), create);
241 }
242
243 static QQmlData *get(const QObject *object) {
244 return QQmlData::get(QObjectPrivate::get(object));
245
246 }
247
248 static bool keepAliveDuringGarbageCollection(const QObject *object) {
249 QQmlData *ddata = get(object);
250 if (!ddata || ddata->indestructible || ddata->rootObjectInCreation)
251 return true;
252 return false;
253 }
254
255 bool hasExtendedData() const { return extendedData != nullptr; }
256 QHash<QQmlAttachedPropertiesFunc, QObject *> *attachedProperties() const;
257
258 static inline bool wasDeleted(const QObject *);
259 static inline bool wasDeleted(const QObjectPrivate *);
260
261 static void markAsDeleted(QObject *);
262 static void setQueuedForDeletion(QObject *);
263
264 static inline void flushPendingBinding(QObject *object, int coreIndex);
265 void flushPendingBinding(int coreIndex);
266
267 static QQmlPropertyCache::ConstPtr ensurePropertyCache(QObject *object)
268 {
269 QQmlData *ddata = QQmlData::get(object, /*create*/true);
270 if (Q_LIKELY(ddata->propertyCache))
271 return ddata->propertyCache;
272 return createPropertyCache(object);
273 }
274
275 Q_ALWAYS_INLINE static uint offsetForBit(int bit) { return static_cast<uint>(bit) / BitsPerType; }
276 Q_ALWAYS_INLINE static BindingBitsType bitFlagForBit(int bit) { return BindingBitsType(1) << (static_cast<uint>(bit) & (BitsPerType - 1)); }
277
278 void clear();
279
280private:
281 // For attachedProperties
282 mutable QQmlDataExtended *extendedData = nullptr;
283
284 Q_NEVER_INLINE static QQmlData *createQQmlData(QObjectPrivate *priv);
285 Q_NEVER_INLINE static QQmlPropertyCache::ConstPtr createPropertyCache(QObject *object);
286
287 void removeFromContext();
288 void clearBindings();
289 bool clearSignalHandlers();
290
291 Q_ALWAYS_INLINE bool hasBitSet(int bit) const
292 {
293 uint offset = offsetForBit(bit);
294 if (bindingBitsArraySize <= offset)
295 return false;
296
297 const BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
298 return bits[offset] & bitFlagForBit(bit);
299 }
300
301 Q_ALWAYS_INLINE void clearBit(int bit)
302 {
303 uint offset = QQmlData::offsetForBit(bit);
304 if (bindingBitsArraySize > offset) {
305 BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
306 bits[offset] &= ~QQmlData::bitFlagForBit(bit);
307 }
308 }
309
310 Q_ALWAYS_INLINE void setBit(QObject *obj, int bit)
311 {
312 uint offset = QQmlData::offsetForBit(bit);
313 BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
314 if (Q_UNLIKELY(bindingBitsArraySize <= offset))
315 bits = growBits(obj, bit);
316 bits[offset] |= QQmlData::bitFlagForBit(bit);
317 }
318
319 Q_NEVER_INLINE BindingBitsType *growBits(QObject *obj, int bit);
320
321 Q_DISABLE_COPY_MOVE(QQmlData);
322};
323
324bool QQmlData::wasDeleted(const QObjectPrivate *priv)
325{
326 if (!priv || priv->wasDeleted || priv->isDeletingChildren)
327 return true;
328
329 const QQmlData *ddata = QQmlData::get(priv);
330 return ddata && ddata->isQueuedForDeletion;
331}
332
333bool QQmlData::wasDeleted(const QObject *object)
334{
335 if (!object)
336 return true;
337
338 const QObjectPrivate *priv = QObjectPrivate::get(object);
339 return QQmlData::wasDeleted(priv);
340}
341
342inline bool isIndexInConnectionMask(quint64 connectionMask, int index)
343{
344 return connectionMask & (1ULL << quint64(index % 64));
345}
346
347QQmlNotifierEndpoint *QQmlData::notify(int index) const
348{
349 // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
350
351 Q_ASSERT(index <= 0xFFFF);
352
353 NotifyList *list = notifyList.loadRelaxed();
354 if (!list || !isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index))
355 return nullptr;
356
357 if (index < list->notifiesSize)
358 return list->notifies[index];
359
360 if (index <= list->maximumTodoIndex) {
361 list->layout();
362 if (index < list->notifiesSize)
363 return list->notifies[index];
364 }
365
366 return nullptr;
367}
368
369/*
370 The index MUST be in the range returned by QObjectPrivate::signalIndex()
371 This is different than the index returned by QMetaMethod::methodIndex()
372*/
373inline bool QQmlData::signalHasEndpoint(int index) const
374{
375 // This can be called from any thread.
376 // We still use relaxed semantics. If we're on a thread different from the "home" thread
377 // of the QQmlData, two interesting things might happen:
378 //
379 // 1. The list might go away while we hold it. In that case we are dealing with an object whose
380 // QObject dtor is being executed concurrently. This is UB already without the notify lists.
381 // Therefore, we don't need to consider it.
382 // 2. The connectionMask may be amended or zeroed while we are looking at it. In that case
383 // we "misreport" the endpoint. Since ordering of events across threads is inherently
384 // nondeterministic, either result is correct in that case. We can accept it.
385
386 NotifyList *list = notifyList.loadRelaxed();
387 return list && isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index);
388}
389
390bool QQmlData::hasBindingBit(int coreIndex) const
391{
392 Q_ASSERT(coreIndex >= 0);
393 Q_ASSERT(coreIndex <= 0xffff);
394
395 return hasBitSet(coreIndex * 2);
396}
397
398void QQmlData::setBindingBit(QObject *obj, int coreIndex)
399{
400 Q_ASSERT(coreIndex >= 0);
401 Q_ASSERT(coreIndex <= 0xffff);
402 setBit(obj, coreIndex * 2);
403}
404
405void QQmlData::clearBindingBit(int coreIndex)
406{
407 Q_ASSERT(coreIndex >= 0);
408 Q_ASSERT(coreIndex <= 0xffff);
409 clearBit(coreIndex * 2);
410}
411
412bool QQmlData::hasPendingBindingBit(int coreIndex) const
413{
414 Q_ASSERT(coreIndex >= 0);
415 Q_ASSERT(coreIndex <= 0xffff);
416
417 return hasBitSet(coreIndex * 2 + 1);
418}
419
420void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex)
421{
422 Q_ASSERT(coreIndex >= 0);
423 Q_ASSERT(coreIndex <= 0xffff);
424 setBit(obj, coreIndex * 2 + 1);
425}
426
427void QQmlData::clearPendingBindingBit(int coreIndex)
428{
429 Q_ASSERT(coreIndex >= 0);
430 Q_ASSERT(coreIndex <= 0xffff);
431 clearBit(coreIndex * 2 + 1);
432}
433
434void QQmlData::flushPendingBinding(QObject *object, int coreIndex)
435{
436 QQmlData *data = QQmlData::get(object, false);
437 if (data && data->hasPendingBindingBit(coreIndex))
438 data->flushPendingBinding(coreIndex);
439}
440
441QT_END_NAMESPACE
442
443#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
QT_END_NAMESPACE Q_DECLARE_METATYPE(QT_PREPEND_NAMESPACE(QOhosPlatformWindow::NativeNodeRenderFitPolicy))
bool isIndexInConnectionMask(quint64 connectionMask, int index)
Definition qqmldata_p.h:342