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 | |
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 | |
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 | |
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 | |
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 | |
332 | bool 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 | |
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 | */ |
368 | inline bool QQmlData::signalHasEndpoint(int index) const |
369 | { |
370 | return notifyList && (notifyList->connectionMask & (1ULL << quint64(index % 64))); |
371 | } |
372 | |
373 | bool 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 | |
381 | void 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 | |
388 | void QQmlData::clearBindingBit(int coreIndex) |
389 | { |
390 | Q_ASSERT(coreIndex >= 0); |
391 | Q_ASSERT(coreIndex <= 0xffff); |
392 | clearBit(bit: coreIndex * 2); |
393 | } |
394 | |
395 | bool 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 | |
403 | void 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 | |
410 | void QQmlData::clearPendingBindingBit(int coreIndex) |
411 | { |
412 | Q_ASSERT(coreIndex >= 0); |
413 | Q_ASSERT(coreIndex <= 0xffff); |
414 | clearBit(bit: coreIndex * 2 + 1); |
415 | } |
416 | |
417 | void 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 | |
424 | QT_END_NAMESPACE |
425 | |
426 | #endif // QQMLDATA_P_H |
427 | |