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/qqmlnotifylist_p.h>
21#include <private/qqmlpropertycache_p.h>
22#include <private/qqmlpropertyindex_p.h>
23#include <private/qqmlrefcount_p.h>
24#include <private/qtqmlglobal_p.h>
25#include <private/qv4persistent_p.h>
26#include <private/qv4value_p.h>
27
28#include <QtQml/qqmlprivate.h>
29#include <QtQml/qjsengine.h>
30
31#include <QtCore/qvector.h>
32
34
35template <class Key, class T> class QHash;
36class QQmlEngine;
37class QQmlGuardImpl;
38class QQmlAbstractBinding;
39class QQmlBoundSignal;
40class QQmlContext;
41class QQmlPropertyCache;
42class QQmlContextData;
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 QAtomicPointer<QQmlNotifyList> notifyList;
133
134 inline QQmlNotifierEndpoint *notify(int index) const;
135 void addNotify(int index, QQmlNotifierEndpoint *);
136 int endpointCount(int index);
137 bool signalHasEndpoint(int index) const;
138 static void connectEndpoint(QQmlNotifierEndpoint *endPoint, QObject *source, int sourceSignal,
139 QQmlEngine *engine, bool doNotify = true);
140
141 enum class DeleteNotifyList { Yes, No };
142 void disconnectNotifiers(DeleteNotifyList doDelete);
143
144 // The context that created the C++ object; not refcounted to prevent cycles
145 QQmlContextData *context = nullptr;
146 // The outermost context in which this object lives; not refcounted to prevent cycles
147 QQmlContextData *outerContext = nullptr;
148 QQmlRefPointer<QQmlContextData> ownContext;
149
150 QQmlAbstractBinding *bindings = nullptr;
151 QQmlBoundSignal *signalHandlers = nullptr;
152 std::vector<QQmlPropertyObserver> propertyObservers;
153
154 // Linked list for QQmlContext::contextObjects
155 QQmlData *nextContextObject = nullptr;
156 QQmlData**prevContextObject = nullptr;
157
158 inline bool hasBindingBit(int) const;
159 inline void setBindingBit(QObject *obj, int);
160 inline void clearBindingBit(int);
161
162 inline bool hasPendingBindingBit(int index) const;
163 inline void setPendingBindingBit(QObject *obj, int);
164 inline void clearPendingBindingBit(int);
165
166 quint16 lineNumber = 0;
167 quint16 columnNumber = 0;
168
169 quint32 jsEngineId = 0; // id of the engine that created the jsWrapper
170
171 struct DeferredData {
172 DeferredData();
173 ~DeferredData();
174 unsigned int deferredIdx;
175 QMultiHash<int, const QV4::CompiledData::Binding *> bindings;
176
177 // Not always the same as the other compilation unit
178 QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
179
180 // Could be either context or outerContext
181 QQmlRefPointer<QQmlContextData> context;
182 /* set if the deferred binding originates in an inline component,
183 necessary to adjust the compilationUnit; null if there was no
184 inline component */
185 QString inlineComponentName;
186 Q_DISABLE_COPY(DeferredData);
187 };
188 QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
189 QList<DeferredData *> deferredData;
190
191 // The compiled-object index within compilationUnit for this object.
192 // Set for all QML-created objects, including those without a VMEMetaObject.
193 int cuObjectIndex = -1;
194
195 void deferData(int objectIndex, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &,
196 const QQmlRefPointer<QQmlContextData> &, const QString &inlineComponentName);
197 void releaseDeferredData();
198
199 QV4::WeakValue jsWrapper;
200
201 QQmlPropertyCache::ConstPtr propertyCache;
202
203 QQmlGuardImpl *guards = nullptr;
204
205 static QQmlData *get(QObjectPrivate *priv, bool create) {
206 // If QObjectData::isDeletingChildren is set then access to QObjectPrivate::declarativeData has
207 // to be avoided because QObjectPrivate::currentChildBeingDeleted is in use.
208 if (priv->isDeletingChildren || priv->wasDeleted) {
209 Q_ASSERT(!create);
210 return nullptr;
211 } else if (priv->declarativeData) {
212 return static_cast<QQmlData *>(priv->declarativeData);
213 } else if (create) {
214 return createQQmlData(priv);
215 } else {
216 return nullptr;
217 }
218 }
219
220 static QQmlData *get(const QObjectPrivate *priv) {
221 // If QObjectData::isDeletingChildren is set then access to QObjectPrivate::declarativeData has
222 // to be avoided because QObjectPrivate::currentChildBeingDeleted is in use.
223 if (priv->isDeletingChildren || priv->wasDeleted)
224 return nullptr;
225 if (priv->declarativeData)
226 return static_cast<QQmlData *>(priv->declarativeData);
227 return nullptr;
228 }
229
230 static QQmlData *get(QObject *object, bool create) {
231 return QQmlData::get(QObjectPrivate::get(object), create);
232 }
233
234 static QQmlData *get(const QObject *object) {
235 return QQmlData::get(QObjectPrivate::get(object));
236
237 }
238
239 static bool keepAliveDuringGarbageCollection(const QObject *object) {
240 QQmlData *ddata = get(object);
241 if (!ddata || ddata->indestructible || ddata->rootObjectInCreation)
242 return true;
243 return false;
244 }
245
246 bool hasExtendedData() const { return extendedData != nullptr; }
247 QHash<QQmlAttachedPropertiesFunc, QObject *> *attachedProperties() const;
248
249 static inline bool wasDeleted(const QObject *);
250 static inline bool wasDeleted(const QObjectPrivate *);
251
252 static void markAsDeleted(QObject *);
253 static void setQueuedForDeletion(QObject *);
254
255 static inline void flushPendingBinding(QObject *object, int coreIndex);
256 void flushPendingBinding(int coreIndex);
257
258 static QQmlPropertyCache::ConstPtr ensurePropertyCache(QObject *object)
259 {
260 QQmlData *ddata = QQmlData::get(object, /*create*/true);
261 if (Q_LIKELY(ddata->propertyCache))
262 return ddata->propertyCache;
263 return createPropertyCache(object);
264 }
265
266 Q_ALWAYS_INLINE static uint offsetForBit(int bit) { return static_cast<uint>(bit) / BitsPerType; }
267 Q_ALWAYS_INLINE static BindingBitsType bitFlagForBit(int bit) { return BindingBitsType(1) << (static_cast<uint>(bit) & (BitsPerType - 1)); }
268
269 void clear();
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 void removeFromContext();
279 void clearBindings();
280 bool clearSignalHandlers();
281
282 Q_ALWAYS_INLINE bool hasBitSet(int bit) const
283 {
284 uint offset = offsetForBit(bit);
285 if (bindingBitsArraySize <= offset)
286 return false;
287
288 const BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
289 return bits[offset] & bitFlagForBit(bit);
290 }
291
292 Q_ALWAYS_INLINE void clearBit(int bit)
293 {
294 uint offset = QQmlData::offsetForBit(bit);
295 if (bindingBitsArraySize > offset) {
296 BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
297 bits[offset] &= ~QQmlData::bitFlagForBit(bit);
298 }
299 }
300
301 Q_ALWAYS_INLINE void setBit(QObject *obj, int bit)
302 {
303 uint offset = QQmlData::offsetForBit(bit);
304 BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
305 if (Q_UNLIKELY(bindingBitsArraySize <= offset))
306 bits = growBits(obj, bit);
307 bits[offset] |= QQmlData::bitFlagForBit(bit);
308 }
309
310 Q_NEVER_INLINE BindingBitsType *growBits(QObject *obj, int bit);
311
312 Q_DISABLE_COPY_MOVE(QQmlData);
313};
314
315bool QQmlData::wasDeleted(const QObjectPrivate *priv)
316{
317 if (!priv || priv->wasDeleted || priv->isDeletingChildren)
318 return true;
319
320 const QQmlData *ddata = QQmlData::get(priv);
321 return ddata && ddata->isQueuedForDeletion;
322}
323
324bool QQmlData::wasDeleted(const QObject *object)
325{
326 if (!object)
327 return true;
328
329 const QObjectPrivate *priv = QObjectPrivate::get(object);
330 return QQmlData::wasDeleted(priv);
331}
332
333inline bool isIndexInConnectionMask(quint64 connectionMask, int index)
334{
335 return connectionMask & (1ULL << quint64(index % 64));
336}
337
338QQmlNotifierEndpoint *QQmlData::notify(int index) const
339{
340 // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
341
342 Q_ASSERT(index <= 0xFFFF);
343
344 QQmlNotifyList *list = notifyList.loadRelaxed();
345 if (!list || !isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index))
346 return nullptr;
347
348 if (index < list->notifiesSize)
349 return list->notifies[index];
350
351 if (index <= list->maximumTodoIndex) {
352 list->layout();
353 if (index < list->notifiesSize)
354 return list->notifies[index];
355 }
356
357 return nullptr;
358}
359
360/*
361 The index MUST be in the range returned by QObjectPrivate::signalIndex()
362 This is different than the index returned by QMetaMethod::methodIndex()
363*/
364inline bool QQmlData::signalHasEndpoint(int index) const
365{
366 // This can be called from any thread.
367 // We still use relaxed semantics. If we're on a thread different from the "home" thread
368 // of the QQmlData, two interesting things might happen:
369 //
370 // 1. The list might go away while we hold it. In that case we are dealing with an object whose
371 // QObject dtor is being executed concurrently. This is UB already without the notify lists.
372 // Therefore, we don't need to consider it.
373 // 2. The connectionMask may be amended or zeroed while we are looking at it. In that case
374 // we "misreport" the endpoint. Since ordering of events across threads is inherently
375 // nondeterministic, either result is correct in that case. We can accept it.
376
377 QQmlNotifyList *list = notifyList.loadRelaxed();
378 return list && isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index);
379}
380
381/*
382 \a sourceSignal MUST be in the signal index range (see QObjectPrivate::signalIndex()).
383 This is different from QMetaMethod::methodIndex().
384*/
385inline void QQmlData::connectEndpoint(QQmlNotifierEndpoint *endpoint, QObject *source,
386 int sourceSignal, QQmlEngine *engine, bool doNotify)
387{
388 endpoint->disconnect();
389
390 Q_ASSERT(engine);
391 if (QObjectPrivate::get(source)->threadData.loadRelaxed()->threadId.loadRelaxed()
392 != QObjectPrivate::get(engine)->threadData.loadRelaxed()->threadId.loadRelaxed()) {
393
394 QString sourceName;
395 QDebug(&sourceName) << source;
396 sourceName = sourceName.left(sourceName.size() - 1);
397 QString engineName;
398 QDebug(&engineName).nospace() << engine;
399 engineName = engineName.left(engineName.size() - 1);
400
401 qFatal("QQmlEngine: Illegal attempt to connect to %s that is in"
402 " a different thread than the QML engine %s.",
403 qPrintable(sourceName), qPrintable(engineName));
404 }
405
406 endpoint->setSender(qintptr(source));
407 endpoint->sourceSignal = sourceSignal;
408 QQmlPropertyPrivate::flushSignal(source, sourceSignal);
409 QQmlData *ddata = QQmlData::get(source, true);
410 ddata->addNotify(sourceSignal, endpoint);
411 if (doNotify) {
412 endpoint->needsConnectNotify = doNotify;
413 QObjectPrivate *const priv = QObjectPrivate::get(source);
414 priv->connectNotify(QMetaObjectPrivate::signal(source->metaObject(), sourceSignal));
415 }
416}
417
418bool QQmlData::hasBindingBit(int coreIndex) const
419{
420 Q_ASSERT(coreIndex >= 0);
421 Q_ASSERT(coreIndex <= 0xffff);
422
423 return hasBitSet(coreIndex * 2);
424}
425
426void QQmlData::setBindingBit(QObject *obj, int coreIndex)
427{
428 Q_ASSERT(coreIndex >= 0);
429 Q_ASSERT(coreIndex <= 0xffff);
430 setBit(obj, coreIndex * 2);
431}
432
433void QQmlData::clearBindingBit(int coreIndex)
434{
435 Q_ASSERT(coreIndex >= 0);
436 Q_ASSERT(coreIndex <= 0xffff);
437 clearBit(coreIndex * 2);
438}
439
440bool QQmlData::hasPendingBindingBit(int coreIndex) const
441{
442 Q_ASSERT(coreIndex >= 0);
443 Q_ASSERT(coreIndex <= 0xffff);
444
445 return hasBitSet(coreIndex * 2 + 1);
446}
447
448void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex)
449{
450 Q_ASSERT(coreIndex >= 0);
451 Q_ASSERT(coreIndex <= 0xffff);
452 setBit(obj, coreIndex * 2 + 1);
453}
454
455void QQmlData::clearPendingBindingBit(int coreIndex)
456{
457 Q_ASSERT(coreIndex >= 0);
458 Q_ASSERT(coreIndex <= 0xffff);
459 clearBit(coreIndex * 2 + 1);
460}
461
462void QQmlData::flushPendingBinding(QObject *object, int coreIndex)
463{
464 QQmlData *data = QQmlData::get(object, false);
465 if (data && data->hasPendingBindingBit(coreIndex))
466 data->flushPendingBinding(coreIndex);
467}
468
469QT_END_NAMESPACE
470
471#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(QtOhos::enums::kit::ShareKit::systemShare::SelectionMode))
bool isIndexInConnectionMask(quint64 connectionMask, int index)
Definition qqmldata_p.h:333