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
64 QT_BEGIN_NAMESPACE
65
66 template <class Key, class T> class QHash;
67 class QQmlEngine;
68 class QQmlGuardImpl;
69 class QQmlAbstractBinding;
70 class QQmlBoundSignal;
71 class QQmlContext;
72 class QQmlPropertyCache;
73 class QQmlContextData;
74 class QQmlNotifier;
75 class QQmlDataExtended;
76 class QQmlNotifierEndpoint;
77
78 namespace QV4 {
79 class ExecutableCompilationUnit;
80 namespace CompiledData {
81 struct 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.
87 class QQmlContextData;
88 class Q_QML_PRIVATE_EXPORT QQmlContextDataRef
89 {
90 public:
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
isNull()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
106 private:
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!
117 class Q_QML_PRIVATE_EXPORT QQmlData : public QAbstractDeclarativeData
118 {
119 public:
120 QQmlData();
121 ~QQmlData();
122
init()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
setImplicitDestructible()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(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
keepAliveDuringGarbageCollection(const QObject * object)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
hasExtendedData()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
ensurePropertyCache(QJSEngine * engine,QObject * object)278 static QQmlPropertyCache *ensurePropertyCache(QJSEngine *engine, QObject *object)
279 {
280 Q_ASSERT(engine);
281 QQmlData *ddata = QQmlData::get(object, /*create*/true);
282 if (Q_LIKELY(ddata->propertyCache))
283 return ddata->propertyCache;
284 return createPropertyCache(engine, object);
285 }
286
offsetForBit(int bit)287 Q_ALWAYS_INLINE static uint offsetForBit(int bit) { return static_cast<uint>(bit) / BitsPerType; }
bitFlagForBit(int bit)288 Q_ALWAYS_INLINE static BindingBitsType bitFlagForBit(int bit) { return BindingBitsType(1) << (static_cast<uint>(bit) & (BitsPerType - 1)); }
289
290 private:
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
hasBitSet(int bit)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
clearBit(int bit)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
setBit(QObject * obj,int bit)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
wasDeleted(const QObject * object)332 bool QQmlData::wasDeleted(const QObject *object)
333 {
334 if (!object)
335 return true;
336
337 const QObjectPrivate *priv = QObjectPrivate::get(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
notify(int index)345 QQmlNotifierEndpoint *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 */
signalHasEndpoint(int index)368 inline bool QQmlData::signalHasEndpoint(int index) const
369 {
370 return notifyList && (notifyList->connectionMask & (1ULL << quint64(index % 64)));
371 }
372
hasBindingBit(int coreIndex)373 bool QQmlData::hasBindingBit(int coreIndex) const
374 {
375 Q_ASSERT(coreIndex >= 0);
376 Q_ASSERT(coreIndex <= 0xffff);
377
378 return hasBitSet(coreIndex * 2);
379 }
380
setBindingBit(QObject * obj,int coreIndex)381 void QQmlData::setBindingBit(QObject *obj, int coreIndex)
382 {
383 Q_ASSERT(coreIndex >= 0);
384 Q_ASSERT(coreIndex <= 0xffff);
385 setBit(obj, coreIndex * 2);
386 }
387
clearBindingBit(int coreIndex)388 void QQmlData::clearBindingBit(int coreIndex)
389 {
390 Q_ASSERT(coreIndex >= 0);
391 Q_ASSERT(coreIndex <= 0xffff);
392 clearBit(coreIndex * 2);
393 }
394
hasPendingBindingBit(int coreIndex)395 bool QQmlData::hasPendingBindingBit(int coreIndex) const
396 {
397 Q_ASSERT(coreIndex >= 0);
398 Q_ASSERT(coreIndex <= 0xffff);
399
400 return hasBitSet(coreIndex * 2 + 1);
401 }
402
setPendingBindingBit(QObject * obj,int coreIndex)403 void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex)
404 {
405 Q_ASSERT(coreIndex >= 0);
406 Q_ASSERT(coreIndex <= 0xffff);
407 setBit(obj, coreIndex * 2 + 1);
408 }
409
clearPendingBindingBit(int coreIndex)410 void QQmlData::clearPendingBindingBit(int coreIndex)
411 {
412 Q_ASSERT(coreIndex >= 0);
413 Q_ASSERT(coreIndex <= 0xffff);
414 clearBit(coreIndex * 2 + 1);
415 }
416
flushPendingBinding(QObject * o,QQmlPropertyIndex propertyIndex)417 void QQmlData::flushPendingBinding(QObject *o, QQmlPropertyIndex propertyIndex)
418 {
419 QQmlData *data = QQmlData::get(o, false);
420 if (data && data->hasPendingBindingBit(propertyIndex.coreIndex()))
421 data->flushPendingBindingImpl(propertyIndex);
422 }
423
424 QT_END_NAMESPACE
425
426 #endif // QQMLDATA_P_H
427