1 // This implements the helpers for QObject.
2 //
3 // Copyright (c) 2021 Riverbank Computing Limited <info@riverbankcomputing.com>
4 //
5 // This file is part of PyQt5.
6 //
7 // This file may be used under the terms of the GNU General Public License
8 // version 3.0 as published by the Free Software Foundation and appearing in
9 // the file LICENSE included in the packaging of this file.  Please review the
10 // following information to ensure the GNU General Public License version 3.0
11 // requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 //
13 // If you do not wish to use this file under the terms of the GPL version 3.0
14 // then you may purchase a commercial license.  For more information contact
15 // info@riverbankcomputing.com.
16 //
17 // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
18 // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 
20 
21 #include <ctype.h>
22 
23 #include <Python.h>
24 
25 #include <QMetaObject>
26 #include <QMetaType>
27 #include <QObject>
28 #include <QVariant>
29 
30 #include "qpycore_api.h"
31 #include "qpycore_chimera.h"
32 #include "qpycore_misc.h"
33 #include "qpycore_objectified_strings.h"
34 #include "qpycore_pyqtboundsignal.h"
35 #include "qpycore_pyqtproperty.h"
36 #include "qpycore_pyqtpyobject.h"
37 #include "qpycore_pyqtslot.h"
38 #include "qpycore_qobject_helpers.h"
39 #include "qpycore_types.h"
40 
41 #include "sipAPIQtCore.h"
42 
43 
44 // Forward declarations.
45 static int qt_metacall_worker(sipSimpleWrapper *pySelf, PyTypeObject *pytype,
46         sipTypeDef *base, QMetaObject::Call _c, int _id, void **_a);
47 
48 
49 // This is the helper for all implementations of QObject::metaObject().
qpycore_qobject_metaobject(sipSimpleWrapper * pySelf,sipTypeDef * base)50 const QMetaObject *qpycore_qobject_metaobject(sipSimpleWrapper *pySelf,
51         sipTypeDef *base)
52 {
53     sipWrapperType *wt = (pySelf ? (sipWrapperType *)Py_TYPE(pySelf) : 0);
54 
55     return qpycore_get_qmetaobject(wt, base);
56 }
57 
58 
59 // This is the helper for all implementations of QObject::qt_metacall().
qpycore_qobject_qt_metacall(sipSimpleWrapper * pySelf,sipTypeDef * base,QMetaObject::Call _c,int _id,void ** _a)60 int qpycore_qobject_qt_metacall(sipSimpleWrapper *pySelf, sipTypeDef *base,
61         QMetaObject::Call _c, int _id, void **_a)
62 {
63     // Check if the Python object has gone.
64     if (!pySelf)
65         return -1;
66 
67     return qt_metacall_worker(pySelf, Py_TYPE(pySelf), base, _c, _id, _a);
68 }
69 
70 
71 // This does the real work for all implementations of QObject::qt_metacall().
qt_metacall_worker(sipSimpleWrapper * pySelf,PyTypeObject * pytype,sipTypeDef * base,QMetaObject::Call _c,int _id,void ** _a)72 static int qt_metacall_worker(sipSimpleWrapper *pySelf, PyTypeObject *pytype,
73         sipTypeDef *base, QMetaObject::Call _c, int _id, void **_a)
74 {
75     // See if this is a wrapped C++ type rather than a Python sub-type.
76     if (pytype == sipTypeAsPyTypeObject(base))
77         return _id;
78 
79     PyTypeObject *tp_base;
80 
81 #if PY_VERSION_HEX >= 0x03040000
82     tp_base = reinterpret_cast<PyTypeObject *>(
83             PyType_GetSlot(pytype, Py_tp_base));
84 #else
85     tp_base = pytype->tp_base;
86 #endif
87 
88     _id = qt_metacall_worker(pySelf, tp_base, base, _c, _id, _a);
89 
90     if (_id < 0)
91         return _id;
92 
93     qpycore_metaobject *qo = reinterpret_cast<qpycore_metaobject *>(
94             sipGetTypeUserData((sipWrapperType *)pytype));
95 
96     bool ok = true;
97 
98     if (_c == QMetaObject::InvokeMetaMethod)
99     {
100         if (_id < qo->nr_signals + qo->pslots.count())
101         {
102             if (_id < qo->nr_signals)
103             {
104                 QObject *qthis = reinterpret_cast<QObject *>(sipGetCppPtr(pySelf, sipType_QObject));
105 
106                 Py_BEGIN_ALLOW_THREADS
107                 QMetaObject::activate(qthis, qo->mo, _id, _a);
108                 Py_END_ALLOW_THREADS
109             }
110             else
111             {
112                 PyQtSlot *slot = qo->pslots.at(_id - qo->nr_signals);
113 
114                 ok = slot->invoke(_a, (PyObject *)pySelf, _a[0]);
115             }
116         }
117 
118         _id -= qo->nr_signals + qo->pslots.count();
119     }
120     else if (_c == QMetaObject::ReadProperty)
121     {
122         if (_id < qo->pprops.count())
123         {
124             qpycore_pyqtProperty *prop = qo->pprops.at(_id);
125 
126             if (prop->pyqtprop_get)
127             {
128                 PyObject *py = PyObject_CallFunction(prop->pyqtprop_get,
129                         const_cast<char *>("O"), pySelf);
130 
131                 if (py)
132                 {
133                     ok = prop->pyqtprop_parsed_type->fromPyObject(py, _a[0]);
134                     Py_DECREF(py);
135                 }
136                 else
137                 {
138                     ok = false;
139                 }
140             }
141         }
142 
143         _id -= qo->pprops.count();
144     }
145     else if (_c == QMetaObject::WriteProperty)
146     {
147         if (_id < qo->pprops.count())
148         {
149             qpycore_pyqtProperty *prop = qo->pprops.at(_id);
150 
151             if (prop->pyqtprop_set)
152             {
153                 PyObject *py = prop->pyqtprop_parsed_type->toPyObject(_a[0]);
154 
155                 if (py)
156                 {
157                     PyObject *res = PyObject_CallFunction(prop->pyqtprop_set,
158                             const_cast<char *>("OO"), pySelf, py);
159 
160                     if (res)
161                         Py_DECREF(res);
162                     else
163                         ok = false;
164 
165                     Py_DECREF(py);
166                 }
167                 else
168                 {
169                     ok = false;
170                 }
171             }
172         }
173 
174         _id -= qo->pprops.count();
175     }
176     else if (_c == QMetaObject::ResetProperty)
177     {
178         if (_id < qo->pprops.count())
179         {
180             qpycore_pyqtProperty *prop = qo->pprops.at(_id);
181 
182             if (prop->pyqtprop_reset)
183             {
184                 PyObject *py = PyObject_CallFunction(prop->pyqtprop_reset,
185                         const_cast<char *>("O"), pySelf);
186 
187                 if (py)
188                     Py_DECREF(py);
189                 else
190                     ok = false;
191             }
192         }
193 
194         _id -= qo->pprops.count();
195     }
196     else if (_c == QMetaObject::QueryPropertyDesignable)
197         _id -= qo->pprops.count();
198     else if (_c == QMetaObject::QueryPropertyScriptable)
199         _id -= qo->pprops.count();
200     else if (_c == QMetaObject::QueryPropertyStored)
201         _id -= qo->pprops.count();
202     else if (_c == QMetaObject::QueryPropertyEditable)
203         _id -= qo->pprops.count();
204     else if (_c == QMetaObject::QueryPropertyUser)
205         _id -= qo->pprops.count();
206 
207     // Handle any Python errors.
208     if (!ok)
209     {
210         pyqt5_err_print();
211         return -1;
212     }
213 
214     return _id;
215 }
216 
217 
218 // This is the helper for all implementations of QObject::qt_metacast().
qpycore_qobject_qt_metacast(sipSimpleWrapper * pySelf,const sipTypeDef * base,const char * _clname,void ** sipCpp)219 bool qpycore_qobject_qt_metacast(sipSimpleWrapper *pySelf,
220         const sipTypeDef *base, const char *_clname, void **sipCpp)
221 {
222     *sipCpp = 0;
223 
224     if (!_clname)
225         return true;
226 
227     // Check if the Python object has gone.
228     if (!pySelf)
229         return true;
230 
231     bool is_py_class = false;
232 
233     SIP_BLOCK_THREADS
234 
235     PyTypeObject *base_pytype = sipTypeAsPyTypeObject(base);
236 
237     // We only need to look at the MRO if the object is a wrapped type (rather
238     // than a user sub-class).
239     if (base_pytype != Py_TYPE(pySelf))
240     {
241         PyObject *mro;
242 
243 #if PY_VERSION_HEX >= 0x03040000
244         mro = PyObject_GetAttr((PyObject *)Py_TYPE(pySelf),
245                 qpycore_dunder_mro);
246         Q_ASSERT(mro);
247 #else
248         mro = Py_TYPE(pySelf)->tp_mro;
249 #endif
250 
251         for (Py_ssize_t i = 0; i < PyTuple_Size(mro); ++i)
252         {
253             PyTypeObject *pytype = (PyTypeObject *)PyTuple_GetItem(mro, i);
254 
255             const sipTypeDef *td = sipTypeFromPyTypeObject(pytype);
256 
257             if (!td || !qpycore_is_pyqt_class(td))
258                 continue;
259 
260             if (qstrcmp(sipPyTypeName(pytype), _clname) == 0)
261             {
262                 // The generated type definitions represent the C++ (rather
263                 // than Python) hierachy.  If the C++ hierachy doesn't match
264                 // then the super-type must be provided by a mixin.  Note that
265                 // we check the type in both "directions" as we may be either
266                 // "side" of the base type in the MRO.
267                 if (PyType_IsSubtype(pytype, base_pytype) || PyType_IsSubtype(base_pytype, pytype))
268                     *sipCpp = sipGetAddress(pySelf);
269                 else
270                     *sipCpp = sipGetMixinAddress(pySelf, td);
271 
272                 is_py_class = true;
273                 break;
274             }
275 
276             const char *iface = reinterpret_cast<const pyqt5ClassPluginDef *>(
277                     sipTypePluginData(td))->qt_interface;
278 
279             if (iface && qstrcmp(iface, _clname) == 0)
280             {
281                 *sipCpp = sipGetMixinAddress(pySelf, td);
282                 is_py_class = true;
283                 break;
284             }
285         }
286 
287 #if PY_VERSION_HEX >= 0x03040000
288         Py_DECREF(mro);
289 #endif
290     }
291 
292     SIP_UNBLOCK_THREADS
293 
294     return is_py_class;
295 }
296 
297 
298 // This is a helper for QObject.staticMetaObject %GetCode.
qpycore_qobject_staticmetaobject(PyObject * type_obj)299 PyObject *qpycore_qobject_staticmetaobject(PyObject *type_obj)
300 {
301     const QMetaObject *mo = qpycore_get_qmetaobject((sipWrapperType *)type_obj);
302 
303     if (!mo)
304     {
305         // We assume that this is a side effect of a wrapped class not being
306         // fully ready until sip's meta-class's __init__() has run (rather than
307         // after its __new__() method as might be expected).
308         PyErr_SetString(PyExc_AttributeError,
309                 "staticMetaObject isn't available until the meta-class's __init__ returns");
310 
311         return 0;
312     }
313 
314     return sipConvertFromType(const_cast<QMetaObject *>(mo),
315             sipType_QMetaObject, 0);
316 }
317