1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#ifndef QQMLDATA_P_H
41#define QQMLDATA_P_H
42
43//
44// W A R N I N G
45// -------------
46//
47// This file is not part of the Qt API. It exists purely as an
48// implementation detail. This header file may change from version to
49// version without notice, or even be removed.
50//
51// We mean it.
52//
53
54#include <private/qtqmlglobal_p.h>
55#include <private/qobject_p.h>
56#include <private/qqmlpropertyindex_p.h>
57#include <private/qv4value_p.h>
58#include <private/qv4persistent_p.h>
59#include <private/qqmlrefcount_p.h>
60#include <qqmlprivate.h>
61#include <qjsengine.h>
62#include <qvector.h>
63
64QT_BEGIN_NAMESPACE
65
66template <class Key, class T> class QHash;
67class QQmlEngine;
68class QQmlGuardImpl;
69class QQmlAbstractBinding;
70class QQmlBoundSignal;
71class QQmlContext;
72class QQmlPropertyCache;
73class QQmlContextData;
74class QQmlNotifier;
75class QQmlDataExtended;
76class QQmlNotifierEndpoint;
77
78namespace QV4 {
79class ExecutableCompilationUnit;
80namespace CompiledData {
81struct Binding;
82}
83}
84
85// This is declared here because QQmlData below needs it and this file
86// in turn is included from qqmlcontext_p.h.
87class QQmlContextData;
88class Q_QML_PRIVATE_EXPORT QQmlContextDataRef
89{
90public:
91 inline QQmlContextDataRef();
92 inline QQmlContextDataRef(QQmlContextData *);
93 inline QQmlContextDataRef(const QQmlContextDataRef &);
94 inline ~QQmlContextDataRef();
95
96 inline QQmlContextData *contextData() const;
97 inline void setContextData(QQmlContextData *);
98
99 inline bool isNull() const { return !m_contextData; }
100
101 inline operator QQmlContextData*() const { return m_contextData; }
102 inline QQmlContextData* operator->() const { return m_contextData; }
103 inline QQmlContextDataRef &operator=(QQmlContextData *d);
104 inline QQmlContextDataRef &operator=(const QQmlContextDataRef &other);
105
106private:
107
108 inline void clear();
109
110 QQmlContextData *m_contextData;
111};
112
113// This class is structured in such a way, that simply zero'ing it is the
114// default state for elemental object allocations. This is crucial in the
115// workings of the QQmlInstruction::CreateSimpleObject instruction.
116// Don't change anything here without first considering that case!
117class Q_QML_PRIVATE_EXPORT QQmlData : public QAbstractDeclarativeData
118{
119public:
120 QQmlData();
121 ~QQmlData();
122
123 static inline void init() {
124 static bool initialized = false;
125 if (!initialized) {
126 initialized = true;
127 QAbstractDeclarativeData::destroyed = destroyed;
128 QAbstractDeclarativeData::parentChanged = parentChanged;
129 QAbstractDeclarativeData::signalEmitted = signalEmitted;
130 QAbstractDeclarativeData::receivers = receivers;
131 QAbstractDeclarativeData::isSignalConnected = isSignalConnected;
132 }
133 }
134
135 static void destroyed(QAbstractDeclarativeData *, QObject *);
136 static void parentChanged(QAbstractDeclarativeData *, QObject *, QObject *);
137 static void signalEmitted(QAbstractDeclarativeData *, QObject *, int, void **);
138 static int receivers(QAbstractDeclarativeData *, const QObject *, int);
139 static bool isSignalConnected(QAbstractDeclarativeData *, const QObject *, int);
140
141 void destroyed(QObject *);
142 void parentChanged(QObject *, QObject *);
143
144 void setImplicitDestructible() {
145 if (!explicitIndestructibleSet) indestructible = false;
146 }
147
148 quint32 ownedByQml1:1; // This bit is shared with QML1's QDeclarativeData.
149 quint32 ownMemory:1;
150 quint32 indestructible:1;
151 quint32 explicitIndestructibleSet:1;
152 quint32 hasTaintedV4Object:1;
153 quint32 isQueuedForDeletion:1;
154 /*
155 * rootObjectInCreation should be true only when creating top level CPP and QML objects,
156 * v8 GC will check this flag, only deletes the objects when rootObjectInCreation is false.
157 */
158 quint32 rootObjectInCreation:1;
159 quint32 hasInterceptorMetaObject:1;
160 quint32 hasVMEMetaObject:1;
161 quint32 parentFrozen:1;
162 quint32 dummy:6;
163
164 // When bindingBitsSize < sizeof(ptr), we store the binding bit flags inside
165 // bindingBitsValue. When we need more than sizeof(ptr) bits, we allocated
166 // sufficient space and use bindingBits to point to it.
167 quint32 bindingBitsArraySize : 16;
168 typedef quintptr BindingBitsType;
169 enum {
170 BitsPerType = sizeof(BindingBitsType) * 8,
171 InlineBindingArraySize = 2
172 };
173 union {
174 BindingBitsType *bindingBits;
175 BindingBitsType bindingBitsValue[InlineBindingArraySize];
176 };
177
178 struct NotifyList {
179 quint64 connectionMask;
180
181 quint16 maximumTodoIndex;
182 quint16 notifiesSize;
183
184 QQmlNotifierEndpoint *todo;
185 QQmlNotifierEndpoint**notifies;
186 void layout();
187 private:
188 void layout(QQmlNotifierEndpoint*);
189 };
190 NotifyList *notifyList;
191
192 inline QQmlNotifierEndpoint *notify(int index);
193 void addNotify(int index, QQmlNotifierEndpoint *);
194 int endpointCount(int index);
195 bool signalHasEndpoint(int index) const;
196 void disconnectNotifiers();
197
198 // The context that created the C++ object
199 QQmlContextData *context = nullptr;
200 // The outermost context in which this object lives
201 QQmlContextData *outerContext = nullptr;
202 QQmlContextDataRef ownContext;
203
204 QQmlAbstractBinding *bindings;
205 QQmlBoundSignal *signalHandlers;
206
207 // Linked list for QQmlContext::contextObjects
208 QQmlData *nextContextObject;
209 QQmlData**prevContextObject;
210
211 inline bool hasBindingBit(int) const;
212 inline void setBindingBit(QObject *obj, int);
213 inline void clearBindingBit(int);
214
215 inline bool hasPendingBindingBit(int index) const;
216 inline void setPendingBindingBit(QObject *obj, int);
217 inline void clearPendingBindingBit(int);
218
219 quint16 lineNumber;
220 quint16 columnNumber;
221
222 quint32 jsEngineId; // id of the engine that created the jsWrapper
223
224 struct DeferredData {
225 DeferredData();
226 ~DeferredData();
227 unsigned int deferredIdx;
228 QMultiHash<int, const QV4::CompiledData::Binding *> bindings;
229 QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;//Not always the same as the other compilation unit
230 QQmlContextData *context;//Could be either context or outerContext
231 Q_DISABLE_COPY(DeferredData);
232 };
233 QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
234 QVector<DeferredData *> deferredData;
235
236 void deferData(int objectIndex, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, QQmlContextData *);
237 void releaseDeferredData();
238
239 QV4::WeakValue jsWrapper;
240
241 QQmlPropertyCache *propertyCache;
242
243 QQmlGuardImpl *guards;
244
245 static QQmlData *get(const QObject *object, bool create = false) {
246 QObjectPrivate *priv = QObjectPrivate::get(o: const_cast<QObject *>(object));
247 // If QObjectData::isDeletingChildren is set then access to QObjectPrivate::declarativeData has
248 // to be avoided because QObjectPrivate::currentChildBeingDeleted is in use.
249 if (priv->isDeletingChildren || priv->wasDeleted) {
250 Q_ASSERT(!create);
251 return nullptr;
252 } else if (priv->declarativeData) {
253 return static_cast<QQmlData *>(priv->declarativeData);
254 } else if (create) {
255 return createQQmlData(priv);
256 } else {
257 return nullptr;
258 }
259 }
260
261 static bool keepAliveDuringGarbageCollection(const QObject *object) {
262 QQmlData *ddata = get(object);
263 if (!ddata || ddata->indestructible || ddata->rootObjectInCreation)
264 return true;
265 return false;
266 }
267
268 bool hasExtendedData() const { return extendedData != nullptr; }
269 QHash<QQmlAttachedPropertiesFunc, QObject *> *attachedProperties() const;
270
271 static inline bool wasDeleted(const QObject *);
272
273 static void markAsDeleted(QObject *);
274 static void setQueuedForDeletion(QObject *);
275
276 static inline void flushPendingBinding(QObject *, QQmlPropertyIndex propertyIndex);
277
278 static QQmlPropertyCache *ensurePropertyCache(QJSEngine *engine, QObject *object)
279 {
280 Q_ASSERT(engine);
281 QQmlData *ddata = QQmlData::get(object, /*create*/create: true);
282 if (Q_LIKELY(ddata->propertyCache))
283 return ddata->propertyCache;
284 return createPropertyCache(engine, object);
285 }
286
287 Q_ALWAYS_INLINE static uint offsetForBit(int bit) { return static_cast<uint>(bit) / BitsPerType; }
288 Q_ALWAYS_INLINE static BindingBitsType bitFlagForBit(int bit) { return BindingBitsType(1) << (static_cast<uint>(bit) & (BitsPerType - 1)); }
289
290private:
291 // For attachedProperties
292 mutable QQmlDataExtended *extendedData;
293
294 Q_NEVER_INLINE static QQmlData *createQQmlData(QObjectPrivate *priv);
295 Q_NEVER_INLINE static QQmlPropertyCache *createPropertyCache(QJSEngine *engine, QObject *object);
296
297 void flushPendingBindingImpl(QQmlPropertyIndex index);
298
299 Q_ALWAYS_INLINE bool hasBitSet(int bit) const
300 {
301 uint offset = offsetForBit(bit);
302 if (bindingBitsArraySize <= offset)
303 return false;
304
305 const BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
306 return bits[offset] & bitFlagForBit(bit);
307 }
308
309 Q_ALWAYS_INLINE void clearBit(int bit)
310 {
311 uint offset = QQmlData::offsetForBit(bit);
312 if (bindingBitsArraySize > offset) {
313 BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
314 bits[offset] &= ~QQmlData::bitFlagForBit(bit);
315 }
316 }
317
318 Q_ALWAYS_INLINE void setBit(QObject *obj, int bit)
319 {
320 uint offset = QQmlData::offsetForBit(bit);
321 BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
322 if (Q_UNLIKELY(bindingBitsArraySize <= offset))
323 bits = growBits(obj, bit);
324 bits[offset] |= QQmlData::bitFlagForBit(bit);
325 }
326
327 Q_NEVER_INLINE BindingBitsType *growBits(QObject *obj, int bit);
328
329 Q_DISABLE_COPY(QQmlData);
330};
331
332bool QQmlData::wasDeleted(const QObject *object)
333{
334 if (!object)
335 return true;
336
337 const QObjectPrivate *priv = QObjectPrivate::get(o: object);
338 if (!priv || priv->wasDeleted || priv->isDeletingChildren)
339 return true;
340
341 const QQmlData *ddata = QQmlData::get(object);
342 return ddata && ddata->isQueuedForDeletion;
343}
344
345QQmlNotifierEndpoint *QQmlData::notify(int index)
346{
347 Q_ASSERT(index <= 0xFFFF);
348
349 if (!notifyList || !(notifyList->connectionMask & (1ULL << quint64(index % 64)))) {
350 return nullptr;
351 } else if (index < notifyList->notifiesSize) {
352 return notifyList->notifies[index];
353 } else if (index <= notifyList->maximumTodoIndex) {
354 notifyList->layout();
355 }
356
357 if (index < notifyList->notifiesSize) {
358 return notifyList->notifies[index];
359 } else {
360 return nullptr;
361 }
362}
363
364/*
365 The index MUST be in the range returned by QObjectPrivate::signalIndex()
366 This is different than the index returned by QMetaMethod::methodIndex()
367*/
368inline bool QQmlData::signalHasEndpoint(int index) const
369{
370 return notifyList && (notifyList->connectionMask & (1ULL << quint64(index % 64)));
371}
372
373bool QQmlData::hasBindingBit(int coreIndex) const
374{
375 Q_ASSERT(coreIndex >= 0);
376 Q_ASSERT(coreIndex <= 0xffff);
377
378 return hasBitSet(bit: coreIndex * 2);
379}
380
381void QQmlData::setBindingBit(QObject *obj, int coreIndex)
382{
383 Q_ASSERT(coreIndex >= 0);
384 Q_ASSERT(coreIndex <= 0xffff);
385 setBit(obj, bit: coreIndex * 2);
386}
387
388void QQmlData::clearBindingBit(int coreIndex)
389{
390 Q_ASSERT(coreIndex >= 0);
391 Q_ASSERT(coreIndex <= 0xffff);
392 clearBit(bit: coreIndex * 2);
393}
394
395bool QQmlData::hasPendingBindingBit(int coreIndex) const
396{
397 Q_ASSERT(coreIndex >= 0);
398 Q_ASSERT(coreIndex <= 0xffff);
399
400 return hasBitSet(bit: coreIndex * 2 + 1);
401}
402
403void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex)
404{
405 Q_ASSERT(coreIndex >= 0);
406 Q_ASSERT(coreIndex <= 0xffff);
407 setBit(obj, bit: coreIndex * 2 + 1);
408}
409
410void QQmlData::clearPendingBindingBit(int coreIndex)
411{
412 Q_ASSERT(coreIndex >= 0);
413 Q_ASSERT(coreIndex <= 0xffff);
414 clearBit(bit: coreIndex * 2 + 1);
415}
416
417void QQmlData::flushPendingBinding(QObject *o, QQmlPropertyIndex propertyIndex)
418{
419 QQmlData *data = QQmlData::get(object: o, create: false);
420 if (data && data->hasPendingBindingBit(coreIndex: propertyIndex.coreIndex()))
421 data->flushPendingBindingImpl(index: propertyIndex);
422}
423
424QT_END_NAMESPACE
425
426#endif // QQMLDATA_P_H
427

source code of qtdeclarative/src/qml/qml/qqmldata_p.h