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
4#ifndef QQMLGUARD_P_H
5#define QQMLGUARD_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists for the convenience
12// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
13// file may change from version to version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <private/qqmldata_p.h>
19#include <private/qqmlglobal_p.h>
20
21QT_BEGIN_NAMESPACE
22
23class QQmlGuardImpl
24{
25public:
26 using ObjectDestroyedFn = void(*)(QQmlGuardImpl *);
27
28 inline QQmlGuardImpl();
29 inline QQmlGuardImpl(QObject *);
30 inline QQmlGuardImpl(const QQmlGuardImpl &);
31protected:
32 inline ~QQmlGuardImpl();
33
34public: // ### make so it can be private
35 QObject *o = nullptr;
36 QQmlGuardImpl *next = nullptr;
37 QQmlGuardImpl **prev = nullptr;
38 ObjectDestroyedFn objectDestroyed = nullptr;
39
40 inline void addGuard();
41 inline void remGuard();
42
43 inline void setObject(QObject *g);
44 bool isNull() const noexcept { return !o; }
45};
46
47class QObject;
48template<class T>
49class QQmlGuard : protected QQmlGuardImpl
50{
51 friend class QQmlData;
52public:
53 Q_NODISCARD_CTOR inline QQmlGuard();
54 Q_NODISCARD_CTOR inline QQmlGuard(ObjectDestroyedFn objectDestroyed, T *);
55 Q_NODISCARD_CTOR inline QQmlGuard(T *);
56 Q_NODISCARD_CTOR inline QQmlGuard(const QQmlGuard<T> &);
57
58 inline QQmlGuard<T> &operator=(const QQmlGuard<T> &o);
59 inline QQmlGuard<T> &operator=(T *);
60
61 T *object() const noexcept { return static_cast<T *>(o); }
62 void setObject(T *g) { QQmlGuardImpl::setObject(g); }
63
64 using QQmlGuardImpl::isNull;
65
66 T *operator->() const noexcept { return object(); }
67 T &operator*() const { return *object(); }
68 operator T *() const noexcept { return object(); }
69 T *data() const noexcept { return object(); }
70};
71
72/* used in QQmlStrongJSQObjectReference to indicate that the
73 * object has JS ownership
74 * We save it in objectDestroyFn to save space
75 * (implemented in qqmlengine.cpp)
76 */
77void Q_QML_PRIVATE_EXPORT hasJsOwnershipIndicator(QQmlGuardImpl *);
78
79template <typename T>
80class QQmlStrongJSQObjectReference final : protected QQmlGuardImpl
81{
82public:
83 T *object() const noexcept { return static_cast<T *>(o); }
84
85 using QQmlGuardImpl::isNull;
86
87 T *operator->() const noexcept { return object(); }
88 T &operator*() const { return *object(); }
89 operator T *() const noexcept { return object(); }
90 T *data() const noexcept { return object(); }
91
92 void setObject(T *obj, QObject *parent) {
93 T *old = object();
94 if (obj == old)
95 return;
96
97 if (hasJsOwnership() && old && old->parent() == parent)
98 QQml_setParent_noEvent(old, nullptr);
99
100 QQmlGuardImpl::setObject(obj);
101
102 if (obj && !obj->parent() && !QQmlData::keepAliveDuringGarbageCollection(object: obj)) {
103 setJsOwnership(true);
104 QQml_setParent_noEvent(obj, parent);
105 } else {
106 setJsOwnership(false);
107 }
108 }
109
110private:
111 bool hasJsOwnership() {
112 return objectDestroyed == hasJsOwnershipIndicator;
113 }
114
115 void setJsOwnership(bool itHasOwnership) {
116 objectDestroyed = itHasOwnership ? hasJsOwnershipIndicator : nullptr;
117 }
118};
119
120QT_END_NAMESPACE
121
122Q_DECLARE_METATYPE(QQmlGuard<QObject>)
123
124QT_BEGIN_NAMESPACE
125
126QQmlGuardImpl::QQmlGuardImpl()
127{
128}
129
130QQmlGuardImpl::QQmlGuardImpl(QObject *g)
131: o(g)
132{
133 if (o) addGuard();
134}
135
136/*
137 \internal
138 Copying a QQmlGuardImpl leaves the old one in the intrinsic linked list of guards.
139 The fresh copy does not contain the list pointer of the existing guard; instead
140 only the object and objectDestroyed pointers are copied, and if there is an object
141 we add the new guard to the object's list of guards.
142 */
143QQmlGuardImpl::QQmlGuardImpl(const QQmlGuardImpl &g)
144: o(g.o), objectDestroyed(g.objectDestroyed)
145{
146 if (o) addGuard();
147}
148
149QQmlGuardImpl::~QQmlGuardImpl()
150{
151 if (prev) remGuard();
152 o = nullptr;
153}
154
155void QQmlGuardImpl::addGuard()
156{
157 Q_ASSERT(!prev);
158
159 if (QObjectPrivate::get(o)->wasDeleted)
160 return;
161
162 QQmlData *data = QQmlData::get(object: o, create: true);
163 next = data->guards;
164 if (next) next->prev = &next;
165 data->guards = this;
166 prev = &data->guards;
167}
168
169void QQmlGuardImpl::remGuard()
170{
171 Q_ASSERT(prev);
172
173 if (next) next->prev = prev;
174 *prev = next;
175 next = nullptr;
176 prev = nullptr;
177}
178
179template<class T>
180QQmlGuard<T>::QQmlGuard()
181{
182}
183
184template<class T>
185QQmlGuard<T>::QQmlGuard(ObjectDestroyedFn objDestroyed, T *obj)
186 : QQmlGuardImpl(obj)
187{
188 objectDestroyed = objDestroyed;
189}
190
191template<class T>
192QQmlGuard<T>::QQmlGuard(T *g)
193: QQmlGuardImpl(g)
194{
195}
196
197template<class T>
198QQmlGuard<T>::QQmlGuard(const QQmlGuard<T> &g)
199: QQmlGuardImpl(g)
200{
201}
202
203template<class T>
204QQmlGuard<T> &QQmlGuard<T>::operator=(const QQmlGuard<T> &g)
205{
206 objectDestroyed = g.objectDestroyed;
207 setObject(g.object());
208 return *this;
209}
210
211template<class T>
212QQmlGuard<T> &QQmlGuard<T>::operator=(T *g)
213{
214 /* this does not touch objectDestroyed, as operator= is only a convenience
215 * for setObject. All logic involving objectDestroyed is (sub-)class specific
216 * and remains unaffected.
217 */
218 setObject(g);
219 return *this;
220}
221
222void QQmlGuardImpl::setObject(QObject *g)
223{
224 if (g != o) {
225 if (prev) remGuard();
226 o = g;
227 if (o) addGuard();
228 }
229}
230
231QT_END_NAMESPACE
232
233#endif // QQMLGUARD_P_H
234

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