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
278private:
279 // For attachedProperties
280 mutable QQmlDataExtended *extendedData = nullptr;
281
282 Q_NEVER_INLINE static QQmlData *createQQmlData(QObjectPrivate *priv);
283 Q_NEVER_INLINE static QQmlPropertyCache::ConstPtr createPropertyCache(QObject *object);
284
285 void removeFromContext();
286 void clearBindings();
287 bool clearSignalHandlers();
288
289 Q_ALWAYS_INLINE bool hasBitSet(int bit) const
290 {
291 uint offset = offsetForBit(bit);
292 if (bindingBitsArraySize <= offset)
293 return false;
294
295 const BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
296 return bits[offset] & bitFlagForBit(bit);
297 }
298
299 Q_ALWAYS_INLINE void clearBit(int bit)
300 {
301 uint offset = QQmlData::offsetForBit(bit);
302 if (bindingBitsArraySize > offset) {
303 BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
304 bits[offset] &= ~QQmlData::bitFlagForBit(bit);
305 }
306 }
307
308 Q_ALWAYS_INLINE void setBit(QObject *obj, int bit)
309 {
310 uint offset = QQmlData::offsetForBit(bit);
311 BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
312 if (Q_UNLIKELY(bindingBitsArraySize <= offset))
313 bits = growBits(obj, bit);
314 bits[offset] |= QQmlData::bitFlagForBit(bit);
315 }
316
317 Q_NEVER_INLINE BindingBitsType *growBits(QObject *obj, int bit);
318
319 Q_DISABLE_COPY_MOVE(QQmlData);
320};
321
322bool QQmlData::wasDeleted(const QObjectPrivate *priv)
323{
324 if (!priv || priv->wasDeleted || priv->isDeletingChildren)
325 return true;
326
327 const QQmlData *ddata = QQmlData::get(priv);
328 return ddata && ddata->isQueuedForDeletion;
329}
330
331bool QQmlData::wasDeleted(const QObject *object)
332{
333 if (!object)
334 return true;
335
336 const QObjectPrivate *priv = QObjectPrivate::get(object);
337 return QQmlData::wasDeleted(priv);
338}
339
340inline bool isIndexInConnectionMask(quint64 connectionMask, int index)
341{
342 return connectionMask & (1ULL << quint64(index % 64));
343}
344
345QQmlNotifierEndpoint *QQmlData::notify(int index) const
346{
347 // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
348
349 Q_ASSERT(index <= 0xFFFF);
350
351 NotifyList *list = notifyList.loadRelaxed();
352 if (!list || !isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index))
353 return nullptr;
354
355 if (index < list->notifiesSize)
356 return list->notifies[index];
357
358 if (index <= list->maximumTodoIndex) {
359 list->layout();
360 if (index < list->notifiesSize)
361 return list->notifies[index];
362 }
363
364 return nullptr;
365}
366
367/*
368 The index MUST be in the range returned by QObjectPrivate::signalIndex()
369 This is different than the index returned by QMetaMethod::methodIndex()
370*/
371inline bool QQmlData::signalHasEndpoint(int index) const
372{
373 // This can be called from any thread.
374 // We still use relaxed semantics. If we're on a thread different from the "home" thread
375 // of the QQmlData, two interesting things might happen:
376 //
377 // 1. The list might go away while we hold it. In that case we are dealing with an object whose
378 // QObject dtor is being executed concurrently. This is UB already without the notify lists.
379 // Therefore, we don't need to consider it.
380 // 2. The connectionMask may be amended or zeroed while we are looking at it. In that case
381 // we "misreport" the endpoint. Since ordering of events across threads is inherently
382 // nondeterministic, either result is correct in that case. We can accept it.
383
384 NotifyList *list = notifyList.loadRelaxed();
385 return list && isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index);
386}
387
388bool QQmlData::hasBindingBit(int coreIndex) const
389{
390 Q_ASSERT(coreIndex >= 0);
391 Q_ASSERT(coreIndex <= 0xffff);
392
393 return hasBitSet(coreIndex * 2);
394}
395
396void QQmlData::setBindingBit(QObject *obj, int coreIndex)
397{
398 Q_ASSERT(coreIndex >= 0);
399 Q_ASSERT(coreIndex <= 0xffff);
400 setBit(obj, coreIndex * 2);
401}
402
403void QQmlData::clearBindingBit(int coreIndex)
404{
405 Q_ASSERT(coreIndex >= 0);
406 Q_ASSERT(coreIndex <= 0xffff);
407 clearBit(coreIndex * 2);
408}
409
410bool QQmlData::hasPendingBindingBit(int coreIndex) const
411{
412 Q_ASSERT(coreIndex >= 0);
413 Q_ASSERT(coreIndex <= 0xffff);
414
415 return hasBitSet(coreIndex * 2 + 1);
416}
417
418void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex)
419{
420 Q_ASSERT(coreIndex >= 0);
421 Q_ASSERT(coreIndex <= 0xffff);
422 setBit(obj, coreIndex * 2 + 1);
423}
424
425void QQmlData::clearPendingBindingBit(int coreIndex)
426{
427 Q_ASSERT(coreIndex >= 0);
428 Q_ASSERT(coreIndex <= 0xffff);
429 clearBit(coreIndex * 2 + 1);
430}
431
432void QQmlData::flushPendingBinding(QObject *object, int coreIndex)
433{
434 QQmlData *data = QQmlData::get(object, false);
435 if (data && data->hasPendingBindingBit(coreIndex))
436 data->flushPendingBinding(coreIndex);
437}
438
439QT_END_NAMESPACE
440
441#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:340