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 | |
21 | QT_BEGIN_NAMESPACE |
22 | |
23 | class QQmlGuardImpl |
24 | { |
25 | public: |
26 | using ObjectDestroyedFn = void(*)(QQmlGuardImpl *); |
27 | |
28 | inline QQmlGuardImpl(); |
29 | inline QQmlGuardImpl(QObject *); |
30 | inline QQmlGuardImpl(const QQmlGuardImpl &); |
31 | protected: |
32 | inline ~QQmlGuardImpl(); |
33 | |
34 | public: // ### 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 | |
47 | class QObject; |
48 | template<class T> |
49 | class QQmlGuard : protected QQmlGuardImpl |
50 | { |
51 | friend class QQmlData; |
52 | public: |
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 | */ |
77 | void Q_QML_PRIVATE_EXPORT hasJsOwnershipIndicator(QQmlGuardImpl *); |
78 | |
79 | template <typename T> |
80 | class QQmlStrongJSQObjectReference final : protected QQmlGuardImpl |
81 | { |
82 | public: |
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 | |
110 | private: |
111 | bool hasJsOwnership() { |
112 | return objectDestroyed == hasJsOwnershipIndicator; |
113 | } |
114 | |
115 | void setJsOwnership(bool itHasOwnership) { |
116 | objectDestroyed = itHasOwnership ? hasJsOwnershipIndicator : nullptr; |
117 | } |
118 | }; |
119 | |
120 | QT_END_NAMESPACE |
121 | |
122 | Q_DECLARE_METATYPE(QQmlGuard<QObject>) |
123 | |
124 | QT_BEGIN_NAMESPACE |
125 | |
126 | QQmlGuardImpl::QQmlGuardImpl() |
127 | { |
128 | } |
129 | |
130 | QQmlGuardImpl::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 | */ |
143 | QQmlGuardImpl::QQmlGuardImpl(const QQmlGuardImpl &g) |
144 | : o(g.o), objectDestroyed(g.objectDestroyed) |
145 | { |
146 | if (o) addGuard(); |
147 | } |
148 | |
149 | QQmlGuardImpl::~QQmlGuardImpl() |
150 | { |
151 | if (prev) remGuard(); |
152 | o = nullptr; |
153 | } |
154 | |
155 | void 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 | |
169 | void 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 | |
179 | template<class T> |
180 | QQmlGuard<T>::QQmlGuard() |
181 | { |
182 | } |
183 | |
184 | template<class T> |
185 | QQmlGuard<T>::QQmlGuard(ObjectDestroyedFn objDestroyed, T *obj) |
186 | : QQmlGuardImpl(obj) |
187 | { |
188 | objectDestroyed = objDestroyed; |
189 | } |
190 | |
191 | template<class T> |
192 | QQmlGuard<T>::QQmlGuard(T *g) |
193 | : QQmlGuardImpl(g) |
194 | { |
195 | } |
196 | |
197 | template<class T> |
198 | QQmlGuard<T>::QQmlGuard(const QQmlGuard<T> &g) |
199 | : QQmlGuardImpl(g) |
200 | { |
201 | } |
202 | |
203 | template<class T> |
204 | QQmlGuard<T> &QQmlGuard<T>::operator=(const QQmlGuard<T> &g) |
205 | { |
206 | objectDestroyed = g.objectDestroyed; |
207 | setObject(g.object()); |
208 | return *this; |
209 | } |
210 | |
211 | template<class T> |
212 | QQmlGuard<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 | |
222 | void QQmlGuardImpl::setObject(QObject *g) |
223 | { |
224 | if (g != o) { |
225 | if (prev) remGuard(); |
226 | o = g; |
227 | if (o) addGuard(); |
228 | } |
229 | } |
230 | |
231 | QT_END_NAMESPACE |
232 | |
233 | #endif // QQMLGUARD_P_H |
234 | |