1 #pragma once
2 
3 // Please excuse this code. We are trying to do something that Qt really doesn't
4 // want us to do, and as such, this class is a mess. This code is not a role
5 // model. Do not look up to this code. Do not base your code on this code. In
6 // order to do what it does, it has made terrible sacrifices and has blood on its
7 // hands. Use it only when necessary.
8 
9 #include <QSharedPointer>
10 #include <QMetaMethod>
11 #include <QDebug>
12 #include <QtGlobal>
13 
14 template <class T, class B>
15 class QmlSharedPointer;
16 
17 class QmlSharedPointerBase
18     : public QObject
19     , public QSharedPointer<QObject>
20 {
21 public:
QmlSharedPointerBase(X * ptr)22     template <typename X> explicit QmlSharedPointerBase(X *ptr)
23         : QSharedPointer<QObject>(ptr, &QObject::deleteLater)
24     {
25     }
26 
QmlSharedPointerBase(X * ptr,Deleter d)27     template <typename X, typename Deleter> QmlSharedPointerBase(X *ptr, Deleter d)
28         : QSharedPointer<QObject>(ptr, d)
29     {
30     }
31 
32     template <typename X>
QmlSharedPointerBase(const QSharedPointer<X> & other)33     QmlSharedPointerBase(const QSharedPointer<X> &other)
34         : QSharedPointer<QObject>(other)
35     {
36     }
37 
QmlSharedPointerBase(const QmlSharedPointerBase & other)38     QmlSharedPointerBase(const QmlSharedPointerBase &other)
39         : QSharedPointer<QObject>(other)
40     {
41     }
42 
43     template <typename X>
QmlSharedPointerBase(const QWeakPointer<X> & other)44     explicit QmlSharedPointerBase(const QWeakPointer<X> &other)
45         : QSharedPointer<QObject>(other)
46     {
47     }
48 
49     QmlSharedPointerBase& operator=(const QmlSharedPointerBase& d)
50     {
51         QSharedPointer<QObject>::operator=(d);
52         return *this;
53     }
54 
dynamic_metacall(QObject * _o,QMetaObject::Call _c,int _id,void ** _a)55     static int dynamic_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
56     {
57         return _o->QObject::qt_metacall(_c, _id, _a);
58     }
59 };
60 
61 template <class T, class B = QmlSharedPointerBase>
62 class QmlSharedPointer
63     : public B
64 {
65 // Q_OBJECT macro written out
66 public:
67     QT_WARNING_PUSH
68     Q_OBJECT_NO_OVERRIDE_WARNING
69     static const QMetaObject staticMetaObject;
70     QT_TR_FUNCTIONS
71 private:
72     Q_OBJECT_NO_ATTRIBUTES_WARNING
73     Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
74     QT_WARNING_POP
75     struct QPrivateSignal {};
76     QT_ANNOTATE_CLASS(qt_qobject, "")
77 // end Q_OBJECT
78 
79 private:
init()80     void init()
81     {
82         Q_ASSERT(!QSharedPointer<QObject>::isNull());
83         // Connect all signals from the encapsulated item to the QmlSharedPointer
84         // Skip QObject's signals (e.g. destroyed)
85         const int qObjectMethodCount = QObject::staticMetaObject.methodCount();
86         for (int i = qObjectMethodCount; i < T::staticMetaObject.methodCount(); i++)
87         {
88             auto incomingSignal = T::staticMetaObject.method(i);
89             if (incomingSignal.methodType() != QMetaMethod::Signal) continue;
90             //qDebug() << "connect" << data() << i << incomingSignal.methodSignature();
91             QMetaObject::connect(data(), i, this, i);
92         }
93     }
deinit()94     void deinit()
95     {
96         // Disconnect all signals from the encapsulated item to the QmlSharedPointer
97         // Skip QObject's signals (e.g. destroyed)
98         const int qObjectMethodCount = QObject::staticMetaObject.methodCount();
99         for (int i = qObjectMethodCount; i < T::staticMetaObject.methodCount(); i++)
100         {
101             auto incomingSignal = T::staticMetaObject.method(i);
102             if (incomingSignal.methodType() != QMetaMethod::Signal) continue;
103             QMetaObject::disconnect(data(), i, this, i);
104         }
105     }
106     static const QByteArrayData *gen_stringdata();
107     static void findAndActivateSignal(QObject *_o, int _id, void **_a);
108 
109 public:
dynamic_metacall(QObject * _o,QMetaObject::Call _c,int _id,void ** _a)110     static int dynamic_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
111     {
112         //qDebug() << "Welcome to dynamic_metacall on" << staticMetaObject.className() << "with ID" << _id;
113         const int n_methods = staticMetaObject.methodCount() - staticMetaObject.superClass()->methodCount();
114         const int n_properties = staticMetaObject.propertyCount() - staticMetaObject.superClass()->propertyCount();
115         //qDebug() << "First off, lets call" << B::staticMetaObject.className() << "dynamic_metacall.";
116         _id = B::dynamic_metacall(_o, _c, _id, _a);
117         //qDebug() << "ID is now" << _id;
118         if (_id < 0)
119             return _id;
120         if (_c == QMetaObject::InvokeMetaMethod) {
121             if (_id < n_methods)
122                 qt_static_metacall(_o, _c, _id, _a);
123             _id -= n_methods;
124         } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
125             if (_id < n_methods)
126                 *reinterpret_cast<int*>(_a[0]) = -1;
127             _id -= n_methods;
128         }
129         else if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty
130               || _c == QMetaObject::ResetProperty || _c == QMetaObject::RegisterPropertyMetaType) {
131             if (_id < n_properties)
132                 qt_static_metacall(_o, _c, _id, _a);
133             _id -= n_properties;
134         } else if (_c == QMetaObject::QueryPropertyDesignable) {
135             _id -= n_properties;
136         } else if (_c == QMetaObject::QueryPropertyScriptable) {
137             _id -= n_properties;
138         } else if (_c == QMetaObject::QueryPropertyStored) {
139             _id -= n_properties;
140         } else if (_c == QMetaObject::QueryPropertyEditable) {
141             _id -= n_properties;
142         } else if (_c == QMetaObject::QueryPropertyUser) {
143             _id -= n_properties;
144         }
145         return _id;
146     }
147 
QmlSharedPointer()148     QmlSharedPointer()
149         : B(new T())
150     {
151         init();
152     }
153 
QmlSharedPointer(X * ptr)154     template <typename X> explicit QmlSharedPointer(X *ptr)
155         : B(ptr)
156     {
157         init();
158     }
159 
QmlSharedPointer(X * ptr,Deleter d)160     template <typename X, typename Deleter> QmlSharedPointer(X *ptr, Deleter d)
161         : B(ptr, d)
162     {
163         init();
164     }
165 
166     template <typename X>
QmlSharedPointer(const QSharedPointer<X> & other)167     QmlSharedPointer(const QSharedPointer<X> &other)
168         : B(other)
169     {
170         init();
171     }
172 
QmlSharedPointer(const QmlSharedPointer<T,B> & other)173     QmlSharedPointer(const QmlSharedPointer<T, B> &other)
174         : B(other)
175     {
176         init();
177     }
178 
179     template <typename X>
QmlSharedPointer(const QWeakPointer<X> & other)180     QmlSharedPointer(const QWeakPointer<X> &other)
181         : B(other)
182     {
183         init();
184     }
185 
186     QmlSharedPointer<T, B>& operator=(const QmlSharedPointer<T, B>& d)
187     {
188         deinit();
189         B::operator=(d);
190         init();
191         return *this;
192     }
193 
data()194     T *data() const
195     {
196         return (T *)QSharedPointer<QObject>::data();
197     }
198 
199     T &	operator*() const
200     {
201         return *data();
202     }
203 
204     T *	operator->() const
205     {
206         return data();
207     }
208 
clone()209     virtual QmlSharedPointer<T, B> *clone()
210     {
211         return new QmlSharedPointer<T, B>(*this);
212     }
213 
214     // The MOC doesn't generate this for us anymore
metaObject()215     const QMetaObject *metaObject() const
216     {
217         return &staticMetaObject;
218     }
219 
qt_metacast(const char * _clname)220     void *qt_metacast(const char *_clname)
221     {
222         //qDebug() << "Attempted metacast" << _clname;
223         if (!_clname) return nullptr;
224 
225         const QByteArrayDataPtr first = { const_cast<QByteArrayData*>(&staticMetaObject.d.stringdata[0]) };
226         //qDebug() << "new_stringdata[0]" << (QByteArray)first;
227 
228         if (!strcmp(_clname, ((QByteArray)first).constData()))
229             return static_cast<void*>(this);
230         return B::qt_metacast(_clname);
231     }
232 
qt_metacall(QMetaObject::Call _c,int _id,void ** _a)233     int qt_metacall(QMetaObject::Call _c, int _id, void **_a)
234     {
235         //Debug() << "qt_metacall" << _c << _id;
236         return dynamic_metacall(this, _c, _id, _a);
237     }
238 };
239 
findAndActivateSignal(QObject * _o,int _id,void ** _a)240 template <typename T, typename B> void QmlSharedPointer<T, B>::findAndActivateSignal(QObject *_o, int _id, void **_a)
241 {
242     // Find which base class the given signal is for
243     auto current_metaobject = &staticMetaObject;
244     for (;;) {
245         auto super_metaobject = current_metaobject->superClass();
246         auto first_child_method = super_metaobject->methodOffset() + super_metaobject->methodCount();
247         if (_id >= first_child_method) {
248             QMetaObject::activate(_o, current_metaobject, _id - first_child_method, _a);
249             break;
250         }
251         current_metaobject = super_metaobject;
252     }
253 }
254 
qt_static_metacall(QObject * _o,QMetaObject::Call _c,int _id,void ** _a)255 template <typename T, typename B> void QmlSharedPointer<T, B>::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
256 {
257     auto *_t = static_cast<QmlSharedPointer<T, B> *>(_o);
258     auto *_child = _t->data();
259     Q_ASSERT(_child != nullptr);
260 
261     //qDebug() << "Static metacall on..." << staticMetaObject.className() << _o << _c << _id;
262 
263     if (_c == QMetaObject::InvokeMetaMethod) {
264         auto newId = _id + staticMetaObject.superClass()->methodCount();
265 
266         auto method = QmlSharedPointer<T, B>::staticMetaObject.method(newId);
267         auto sig = QMetaObject::normalizedSignature(method.methodSignature());
268         if (method.methodType() == QMetaMethod::Signal) {
269             //qDebug() << "Firing signal" << sig;
270             QMetaObject::activate(_o, &staticMetaObject, _id, _a);
271         } else {
272             _child->qt_metacall(_c, newId, _a);
273         }
274     } else if (_c == QMetaObject::IndexOfMethod ||
275                _c == QMetaObject::ReadProperty ||
276                _c == QMetaObject::WriteProperty ||
277                _c == QMetaObject::ResetProperty) {
278         auto newId = _id + staticMetaObject.superClass()->propertyCount();
279         //qDebug() << "Access property" << newId;
280         _child->qt_metacall(_c, newId, _a);
281     } else {
282         Q_ASSERT(false); // Unhandled request
283     }
284 }
285 
286 template<typename T, typename B>
gen_stringdata()287 const QByteArrayData *QmlSharedPointer<T, B>::gen_stringdata()
288 {
289     // The MOC always places the strings right after the QByteArrayDatas,
290     // so we can back out the number of strings based on the first offset
291     int n_strings = T::staticMetaObject.d.stringdata[0].offset / sizeof (T::staticMetaObject.d.stringdata[0]);
292 
293     // Copy the child's string data
294     // (copy the lookup table, but don't copy the actual strings.
295     // Just point the new lookup table at the old strings)
296     const int MAX_N_STRINGS = 128;
297     Q_ASSERT(n_strings <= MAX_N_STRINGS);
298     static QByteArrayData new_stringdata[MAX_N_STRINGS];
299 
300     for (int i=0; i<MAX_N_STRINGS; i++) {
301 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
302         new_stringdata[i].ref.atomic.store(-1); // Don't attempt to free
303 #else
304         new_stringdata[i].ref.atomic.storeRelaxed(-1); // Don't attempt to free
305 #endif
306     }
307 
308     for (int i=0; i<n_strings; i++) {
309         new_stringdata[i].size = T::staticMetaObject.d.stringdata[i].size;
310         new_stringdata[i].offset = T::staticMetaObject.d.stringdata[i].offset + ((qptrdiff)&(T::staticMetaObject.d.stringdata[i]) - (qptrdiff)&new_stringdata[i]);
311     }
312 
313     // Override the first string, which is the name:
314     const int MAX_N_CHARS = 128;
315     static char new_name[MAX_N_CHARS]{};
316     strcpy(new_name, "QmlSharedPointer<");
317     strncat(new_name, T::staticMetaObject.className(), 127);
318     strncat(new_name, ">", 127);
319     new_stringdata[0].size = strlen(new_name);
320     new_stringdata[0].offset = ((qptrdiff)&new_name - (qptrdiff)&new_stringdata[0]);
321 
322     //const QByteArrayDataPtr first = { const_cast<QByteArrayData*>(&new_stringdata[0]) };
323     //qDebug() << "new_stringdata[0]" << (QByteArray)first;
324 
325     return new_stringdata;
326 }
327 
328 template<typename T, typename B> const QMetaObject QmlSharedPointer<T, B>::staticMetaObject = { {
329     &B::staticMetaObject,
330     QmlSharedPointer<T, B>::gen_stringdata(),
331     T::staticMetaObject.d.data,
332     QmlSharedPointer<T, B>::qt_static_metacall,
333     T::staticMetaObject.d.relatedMetaObjects,
334     T::staticMetaObject.d.extradata,
335 } };
336