1 // This contains the main implementation of qmlRegisterType.
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 <Python.h>
22 
23 #include <qqmlprivate.h>
24 #include <QByteArray>
25 #include <QString>
26 #include <QQmlListProperty>
27 #include <QQmlParserStatus>
28 #include <QQmlPropertyValueSource>
29 
30 #include "qpyqml_api.h"
31 #include "qpyqmlobject.h"
32 #include "qpyqmlvalidator.h"
33 
34 #include "sipAPIQtQml.h"
35 
36 
37 class QQmlPropertyValueInterceptor;
38 
39 
40 // Forward declarations.
41 static QQmlPrivate::RegisterType *init_type(PyTypeObject *py_type, bool ctor,
42         int revision, PyTypeObject *attached);
43 static void complete_init(QQmlPrivate::RegisterType *rt, int revision);
44 static int register_type(QQmlPrivate::RegisterType *rt);
45 
46 
47 // The registration data for the QValidator proxy types.
48 const int NrOfQValidatorTypes = 10;
49 static QQmlPrivate::RegisterType validator_proxy_types[NrOfQValidatorTypes];
50 
51 // The registration data for the QObject/QAbstractItemModel proxy types.
52 const int NrOfQObjectTypes = 60;
53 static QQmlPrivate::RegisterType object_proxy_types[NrOfQObjectTypes];
54 
55 
56 // Register a Python type.
qpyqml_register_type(PyTypeObject * py_type,PyTypeObject * attached)57 int qpyqml_register_type(PyTypeObject *py_type, PyTypeObject *attached)
58 {
59     // Initialise the registration data structure.
60     QQmlPrivate::RegisterType *rt = init_type(py_type, false, -1, attached);
61 
62     if (!rt)
63         return -1;
64 
65     return register_type(rt);
66 }
67 
68 
69 // Register a library Python type.
qpyqml_register_library_type(PyTypeObject * py_type,const char * uri,int major,int minor,const char * qml_name,int revision,PyTypeObject * attached)70 int qpyqml_register_library_type(PyTypeObject *py_type, const char *uri,
71         int major, int minor, const char *qml_name, int revision,
72         PyTypeObject *attached)
73 {
74     // Initialise the registration data structure.
75     QQmlPrivate::RegisterType *rt = init_type(py_type, true, revision,
76             attached);
77 
78     if (!rt)
79         return -1;
80 
81     rt->uri = uri;
82     rt->versionMajor = major;
83     rt->versionMinor = minor;
84     rt->elementName = qml_name;
85 
86     return register_type(rt);
87 }
88 
89 
90 // Register an uncreatable library Python type.
qpyqml_register_uncreatable_type(PyTypeObject * py_type,const char * uri,int major,int minor,const char * qml_name,const QString & reason,int revision)91 int qpyqml_register_uncreatable_type(PyTypeObject *py_type, const char *uri,
92         int major, int minor, const char *qml_name, const QString &reason,
93         int revision)
94 {
95     // Initialise the registration data structure.
96     QQmlPrivate::RegisterType *rt = init_type(py_type, false, revision, 0);
97 
98     if (!rt)
99         return -1;
100 
101     rt->noCreationReason = reason;
102     rt->uri = uri;
103     rt->versionMajor = major;
104     rt->versionMinor = minor;
105     rt->elementName = qml_name;
106 
107     return register_type(rt);
108 }
109 
110 
111 // Register the proxy type with QML.
register_type(QQmlPrivate::RegisterType * rt)112 static int register_type(QQmlPrivate::RegisterType *rt)
113 {
114     int type_id = QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, rt);
115 
116     if (type_id < 0)
117     {
118         PyErr_SetString(PyExc_RuntimeError,
119                 "unable to register type with QML");
120         return -1;
121     }
122 
123     return type_id;
124 }
125 
126 
127 #define QPYQML_TYPE_INIT(t, n) \
128     case n##U: \
129         QPyQml##t##n::staticMetaObject = *mo; \
130         QPyQml##t##n::attachedPyType = attached; \
131         rt->typeId = qRegisterNormalizedMetaType<QPyQml##t##n *>(ptr_name); \
132         rt->listId = qRegisterNormalizedMetaType<QQmlListProperty<QPyQml##t##n> >(list_name); \
133         rt->objectSize = ctor ? sizeof(QPyQml##t##n) : 0; \
134         if (ctor) rt->create = QQmlPrivate::createInto<QPyQml##t##n>; else rt->create = 0; \
135         rt->attachedPropertiesFunction = attached_mo ? QPyQml##t##n::attachedProperties : 0; \
136         rt->parserStatusCast = is_parser_status ? QQmlPrivate::StaticCastSelector<QPyQml##t##n,QQmlParserStatus>::cast() : -1; \
137         rt->valueSourceCast = is_value_source ? QQmlPrivate::StaticCastSelector<QPyQml##t##n,QQmlPropertyValueSource>::cast() : -1; \
138         rt->valueInterceptorCast = QQmlPrivate::StaticCastSelector<QPyQml##t##n,QQmlPropertyValueInterceptor>::cast(); \
139         break
140 
141 
142 // This is needed for GCC v4.6 and earlier.
143 #define QPYQML_TYPE_IMPL(t, n) \
144     template void QQmlPrivate::createInto<QPyQml##t##n>(void *)
145 
146 
147 QPYQML_TYPE_IMPL(Validator, 0);
148 QPYQML_TYPE_IMPL(Validator, 1);
149 QPYQML_TYPE_IMPL(Validator, 2);
150 QPYQML_TYPE_IMPL(Validator, 3);
151 QPYQML_TYPE_IMPL(Validator, 4);
152 QPYQML_TYPE_IMPL(Validator, 5);
153 QPYQML_TYPE_IMPL(Validator, 6);
154 QPYQML_TYPE_IMPL(Validator, 7);
155 QPYQML_TYPE_IMPL(Validator, 8);
156 QPYQML_TYPE_IMPL(Validator, 9);
157 
158 QPYQML_TYPE_IMPL(Object, 0);
159 QPYQML_TYPE_IMPL(Object, 1);
160 QPYQML_TYPE_IMPL(Object, 2);
161 QPYQML_TYPE_IMPL(Object, 3);
162 QPYQML_TYPE_IMPL(Object, 4);
163 QPYQML_TYPE_IMPL(Object, 5);
164 QPYQML_TYPE_IMPL(Object, 6);
165 QPYQML_TYPE_IMPL(Object, 7);
166 QPYQML_TYPE_IMPL(Object, 8);
167 QPYQML_TYPE_IMPL(Object, 9);
168 QPYQML_TYPE_IMPL(Object, 10);
169 QPYQML_TYPE_IMPL(Object, 11);
170 QPYQML_TYPE_IMPL(Object, 12);
171 QPYQML_TYPE_IMPL(Object, 13);
172 QPYQML_TYPE_IMPL(Object, 14);
173 QPYQML_TYPE_IMPL(Object, 15);
174 QPYQML_TYPE_IMPL(Object, 16);
175 QPYQML_TYPE_IMPL(Object, 17);
176 QPYQML_TYPE_IMPL(Object, 18);
177 QPYQML_TYPE_IMPL(Object, 19);
178 QPYQML_TYPE_IMPL(Object, 20);
179 QPYQML_TYPE_IMPL(Object, 21);
180 QPYQML_TYPE_IMPL(Object, 22);
181 QPYQML_TYPE_IMPL(Object, 23);
182 QPYQML_TYPE_IMPL(Object, 24);
183 QPYQML_TYPE_IMPL(Object, 25);
184 QPYQML_TYPE_IMPL(Object, 26);
185 QPYQML_TYPE_IMPL(Object, 27);
186 QPYQML_TYPE_IMPL(Object, 28);
187 QPYQML_TYPE_IMPL(Object, 29);
188 QPYQML_TYPE_IMPL(Object, 30);
189 QPYQML_TYPE_IMPL(Object, 31);
190 QPYQML_TYPE_IMPL(Object, 32);
191 QPYQML_TYPE_IMPL(Object, 33);
192 QPYQML_TYPE_IMPL(Object, 34);
193 QPYQML_TYPE_IMPL(Object, 35);
194 QPYQML_TYPE_IMPL(Object, 36);
195 QPYQML_TYPE_IMPL(Object, 37);
196 QPYQML_TYPE_IMPL(Object, 38);
197 QPYQML_TYPE_IMPL(Object, 39);
198 QPYQML_TYPE_IMPL(Object, 40);
199 QPYQML_TYPE_IMPL(Object, 41);
200 QPYQML_TYPE_IMPL(Object, 42);
201 QPYQML_TYPE_IMPL(Object, 43);
202 QPYQML_TYPE_IMPL(Object, 44);
203 QPYQML_TYPE_IMPL(Object, 45);
204 QPYQML_TYPE_IMPL(Object, 46);
205 QPYQML_TYPE_IMPL(Object, 47);
206 QPYQML_TYPE_IMPL(Object, 48);
207 QPYQML_TYPE_IMPL(Object, 49);
208 QPYQML_TYPE_IMPL(Object, 50);
209 QPYQML_TYPE_IMPL(Object, 51);
210 QPYQML_TYPE_IMPL(Object, 52);
211 QPYQML_TYPE_IMPL(Object, 53);
212 QPYQML_TYPE_IMPL(Object, 54);
213 QPYQML_TYPE_IMPL(Object, 55);
214 QPYQML_TYPE_IMPL(Object, 56);
215 QPYQML_TYPE_IMPL(Object, 57);
216 QPYQML_TYPE_IMPL(Object, 58);
217 QPYQML_TYPE_IMPL(Object, 59);
218 
219 
220 // Return a pointer to the initialised registration structure for a type.
init_type(PyTypeObject * py_type,bool ctor,int revision,PyTypeObject * attached)221 static QQmlPrivate::RegisterType *init_type(PyTypeObject *py_type, bool ctor,
222         int revision, PyTypeObject *attached)
223 {
224     PyTypeObject *qobject_type = sipTypeAsPyTypeObject(sipType_QObject);
225 
226     // Check the type is derived from QObject and get its meta-object.
227     if (!PyType_IsSubtype(py_type, qobject_type))
228     {
229         PyErr_SetString(PyExc_TypeError,
230                 "type being registered must be a sub-type of QObject");
231         return 0;
232     }
233 
234     const QMetaObject *mo = pyqt5_qtqml_get_qmetaobject(py_type);
235 
236     // See if the type is a parser status.
237     bool is_parser_status = PyType_IsSubtype(py_type,
238             sipTypeAsPyTypeObject(sipType_QQmlParserStatus));
239 
240     // See if the type is a property value source.
241     bool is_value_source = PyType_IsSubtype(py_type,
242             sipTypeAsPyTypeObject(sipType_QQmlPropertyValueSource));
243 
244     // Check any attached type is derived from QObject and get its meta-object.
245     const QMetaObject *attached_mo;
246 
247     if (attached)
248     {
249         if (!PyType_IsSubtype(attached, qobject_type))
250         {
251             PyErr_SetString(PyExc_TypeError,
252                     "attached properties type must be a sub-type of QObject");
253             return 0;
254         }
255 
256         attached_mo = pyqt5_qtqml_get_qmetaobject(attached);
257 
258         Py_INCREF((PyObject *)attached);
259     }
260     else
261     {
262         attached_mo = 0;
263     }
264 
265     QByteArray ptr_name(sipPyTypeName(py_type));
266     ptr_name.append('*');
267 
268     QByteArray list_name(sipPyTypeName(py_type));
269     list_name.prepend("QQmlListProperty<");
270     list_name.append('>');
271 
272     QQmlPrivate::RegisterType *rt;
273 
274     // See if we have the QQuickItem registation helper from the QtQuick
275     // module.  Check each time because it could be imported at any point.
276 
277     typedef sipErrorState (*QQuickItemRegisterFn)(PyTypeObject *, const QMetaObject *, const QByteArray &, const QByteArray &, QQmlPrivate::RegisterType **);
278 
279     static QQuickItemRegisterFn qquickitem_register = 0;
280 
281     if (!qquickitem_register)
282         qquickitem_register = (QQuickItemRegisterFn)sipImportSymbol(
283                 "qtquick_register_item");
284 
285     if (qquickitem_register)
286     {
287         sipErrorState estate = qquickitem_register(py_type, mo, ptr_name,
288                 list_name, &rt);
289 
290         if (estate == sipErrorFail)
291             return 0;
292 
293         if (estate == sipErrorNone)
294         {
295             complete_init(rt, revision);
296             return rt;
297         }
298     }
299 
300     // Initialise the specific type.
301 
302     static const sipTypeDef *qvalidator_td = 0;
303 
304     if (!qvalidator_td)
305         qvalidator_td = sipFindType("QValidator");
306 
307     if (qvalidator_td && PyType_IsSubtype(py_type, sipTypeAsPyTypeObject(qvalidator_td)))
308     {
309         int type_nr = QPyQmlValidatorProxy::addType(py_type);
310 
311         if (type_nr >= NrOfQValidatorTypes)
312         {
313             PyErr_Format(PyExc_TypeError,
314                     "a maximum of %d QValidator types may be registered with QML",
315                     NrOfQValidatorTypes);
316             return 0;
317         }
318 
319         rt = &validator_proxy_types[type_nr];
320 
321         // Initialise those members that depend on the C++ type.
322         switch (type_nr)
323         {
324             QPYQML_TYPE_INIT(Validator, 0);
325             QPYQML_TYPE_INIT(Validator, 1);
326             QPYQML_TYPE_INIT(Validator, 2);
327             QPYQML_TYPE_INIT(Validator, 3);
328             QPYQML_TYPE_INIT(Validator, 4);
329             QPYQML_TYPE_INIT(Validator, 5);
330             QPYQML_TYPE_INIT(Validator, 6);
331             QPYQML_TYPE_INIT(Validator, 7);
332             QPYQML_TYPE_INIT(Validator, 8);
333             QPYQML_TYPE_INIT(Validator, 9);
334         }
335     }
336     else
337     {
338         int type_nr = QPyQmlObjectProxy::addType(py_type);
339 
340         if (type_nr >= NrOfQObjectTypes)
341         {
342             PyErr_Format(PyExc_TypeError,
343                     "a maximum of %d types may be registered with QML",
344                     NrOfQObjectTypes);
345             return 0;
346         }
347 
348         rt = &object_proxy_types[type_nr];
349 
350         // Initialise those members that depend on the C++ type.
351         switch (type_nr)
352         {
353             QPYQML_TYPE_INIT(Object, 0);
354             QPYQML_TYPE_INIT(Object, 1);
355             QPYQML_TYPE_INIT(Object, 2);
356             QPYQML_TYPE_INIT(Object, 3);
357             QPYQML_TYPE_INIT(Object, 4);
358             QPYQML_TYPE_INIT(Object, 5);
359             QPYQML_TYPE_INIT(Object, 6);
360             QPYQML_TYPE_INIT(Object, 7);
361             QPYQML_TYPE_INIT(Object, 8);
362             QPYQML_TYPE_INIT(Object, 9);
363             QPYQML_TYPE_INIT(Object, 10);
364             QPYQML_TYPE_INIT(Object, 11);
365             QPYQML_TYPE_INIT(Object, 12);
366             QPYQML_TYPE_INIT(Object, 13);
367             QPYQML_TYPE_INIT(Object, 14);
368             QPYQML_TYPE_INIT(Object, 15);
369             QPYQML_TYPE_INIT(Object, 16);
370             QPYQML_TYPE_INIT(Object, 17);
371             QPYQML_TYPE_INIT(Object, 18);
372             QPYQML_TYPE_INIT(Object, 19);
373             QPYQML_TYPE_INIT(Object, 20);
374             QPYQML_TYPE_INIT(Object, 21);
375             QPYQML_TYPE_INIT(Object, 22);
376             QPYQML_TYPE_INIT(Object, 23);
377             QPYQML_TYPE_INIT(Object, 24);
378             QPYQML_TYPE_INIT(Object, 25);
379             QPYQML_TYPE_INIT(Object, 26);
380             QPYQML_TYPE_INIT(Object, 27);
381             QPYQML_TYPE_INIT(Object, 28);
382             QPYQML_TYPE_INIT(Object, 29);
383             QPYQML_TYPE_INIT(Object, 30);
384             QPYQML_TYPE_INIT(Object, 31);
385             QPYQML_TYPE_INIT(Object, 32);
386             QPYQML_TYPE_INIT(Object, 33);
387             QPYQML_TYPE_INIT(Object, 34);
388             QPYQML_TYPE_INIT(Object, 35);
389             QPYQML_TYPE_INIT(Object, 36);
390             QPYQML_TYPE_INIT(Object, 37);
391             QPYQML_TYPE_INIT(Object, 38);
392             QPYQML_TYPE_INIT(Object, 39);
393             QPYQML_TYPE_INIT(Object, 40);
394             QPYQML_TYPE_INIT(Object, 41);
395             QPYQML_TYPE_INIT(Object, 42);
396             QPYQML_TYPE_INIT(Object, 43);
397             QPYQML_TYPE_INIT(Object, 44);
398             QPYQML_TYPE_INIT(Object, 45);
399             QPYQML_TYPE_INIT(Object, 46);
400             QPYQML_TYPE_INIT(Object, 47);
401             QPYQML_TYPE_INIT(Object, 48);
402             QPYQML_TYPE_INIT(Object, 49);
403             QPYQML_TYPE_INIT(Object, 50);
404             QPYQML_TYPE_INIT(Object, 51);
405             QPYQML_TYPE_INIT(Object, 52);
406             QPYQML_TYPE_INIT(Object, 53);
407             QPYQML_TYPE_INIT(Object, 54);
408             QPYQML_TYPE_INIT(Object, 55);
409             QPYQML_TYPE_INIT(Object, 56);
410             QPYQML_TYPE_INIT(Object, 57);
411             QPYQML_TYPE_INIT(Object, 58);
412             QPYQML_TYPE_INIT(Object, 59);
413         }
414     }
415 
416     rt->metaObject = mo;
417     rt->attachedPropertiesMetaObject = attached_mo;
418 
419     complete_init(rt, revision);
420 
421     return rt;
422 }
423 
424 
425 // Complete the initialisation of a type registration structure.
complete_init(QQmlPrivate::RegisterType * rt,int revision)426 static void complete_init(QQmlPrivate::RegisterType *rt, int revision)
427 {
428     if (revision < 0)
429     {
430         rt->version = 0;
431         rt->revision = 0;
432     }
433     else
434     {
435         rt->version = 1;
436         rt->revision = revision;
437     }
438 
439     rt->uri = 0;
440     rt->versionMajor = 0;
441     rt->versionMinor = 0;
442     rt->elementName = 0;
443     rt->extensionObjectCreate = 0;
444     rt->extensionMetaObject = 0;
445     rt->customParser = 0;
446 }
447 
448 
449 // Return the proxy that created an object.  This is called with the GIL.
qpyqml_find_proxy_for(QObject * proxied)450 QObject *qpyqml_find_proxy_for(QObject *proxied)
451 {
452     QSetIterator<QObject *> oit(QPyQmlObjectProxy::proxies);
453 
454     while (oit.hasNext())
455     {
456         QPyQmlObjectProxy *proxy = static_cast<QPyQmlObjectProxy *>(oit.next());
457 
458         if (proxy->proxied.data() == proxied)
459             return proxy;
460     }
461 
462     QSetIterator<QObject *> vit(QPyQmlValidatorProxy::proxies);
463 
464     while (vit.hasNext())
465     {
466         QPyQmlValidatorProxy *proxy = static_cast<QPyQmlValidatorProxy *>(vit.next());
467 
468         if (proxy->proxied.data() == proxied)
469             return proxy;
470     }
471 
472     PyErr_Format(PyExc_TypeError,
473             "QObject instance at %p was not created from QML", proxied);
474 
475     return 0;
476 }
477