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