1 /****************************************************************************
2 **
3 ** Copyright (C) 2020 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt for Python.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 ////////////////////////////////////////////////////////////////////////////
41 //
42 // signature_helper.cpp
43 // --------------------
44 //
45 // This file contains assoerted helper functions that are needed,
46 // but it is not helpful to see them all the time.
47 //
48 
49 #include "autodecref.h"
50 #include "sbkstring.h"
51 #include "sbkstaticstrings.h"
52 #include "sbkstaticstrings_p.h"
53 
54 #include "signature_p.h"
55 
56 using namespace Shiboken;
57 
58 extern "C" {
59 
60 // Helper for __qualname__ which might not always exist in Python 2 (type).
_get_qualname(PyObject * ob)61 PyObject *_get_qualname(PyObject *ob)
62 {
63     // We support __qualname__ for types, only.
64     assert(PyType_Check(ob));
65     PyObject *name = PyObject_GetAttr(ob, PyMagicName::qualname());
66     if (name == nullptr) {
67         PyErr_Clear();
68         name = PyObject_GetAttr(ob, PyMagicName::name());
69     }
70     return name;
71 }
72 
_fixup_getset(PyTypeObject * type,const char * name,PyGetSetDef * new_gsp)73 static int _fixup_getset(PyTypeObject *type, const char *name, PyGetSetDef *new_gsp)
74 {
75     /*
76      * This function pre-fills all fields of the new gsp. We then
77      * insert the changed values.
78      */
79     PyGetSetDef *gsp = type->tp_getset;
80     if (gsp != nullptr) {
81         for (; gsp->name != nullptr; gsp++) {
82             if (strcmp(gsp->name, name) == 0) {
83                 new_gsp->set = gsp->set;
84                 new_gsp->doc = gsp->doc;
85                 new_gsp->closure = gsp->closure;
86                 return 1;  // success
87             }
88         }
89     }
90     PyMemberDef *md = type->tp_members;
91     if (md != nullptr)
92         for (; md->name != nullptr; md++)
93             if (strcmp(md->name, name) == 0)
94                 return 1;
95     // staticmethod has just a `__doc__` in the class
96     assert(strcmp(type->tp_name, "staticmethod") == 0 && strcmp(name, "__doc__") == 0);
97     return 0;
98 }
99 
add_more_getsets(PyTypeObject * type,PyGetSetDef * gsp,PyObject ** doc_descr)100 int add_more_getsets(PyTypeObject *type, PyGetSetDef *gsp, PyObject **doc_descr)
101 {
102     /*
103      * This function is used to assign a new `__signature__` attribute,
104      * and also to override a `__doc__` or `__name__` attribute.
105      */
106     assert(PyType_Check(type));
107     PyType_Ready(type);
108     PyObject *dict = type->tp_dict;
109     for (; gsp->name != nullptr; gsp++) {
110         PyObject *have_descr = PyDict_GetItemString(dict, gsp->name);
111         if (have_descr != nullptr) {
112             Py_INCREF(have_descr);
113             if (strcmp(gsp->name, "__doc__") == 0)
114                 *doc_descr = have_descr;
115             else
116                 assert(false);
117             if (!_fixup_getset(type, gsp->name, gsp))
118                 continue;
119         }
120         AutoDecRef descr(PyDescr_NewGetSet(type, gsp));
121         if (descr.isNull())
122             return -1;
123         if (PyDict_SetItemString(dict, gsp->name, descr) < 0)
124             return -1;
125     }
126     PyType_Modified(type);
127     return 0;
128 }
129 
get_funcname(PyObject * ob)130 static PyObject *get_funcname(PyObject *ob)
131 {
132     PyObject *func = ob;
133     if (Py_TYPE(ob) == PepStaticMethod_TypePtr)
134         func = PyObject_GetAttr(ob, PyMagicName::func());
135     else
136         Py_INCREF(func);
137     PyObject *func_name = PyObject_GetAttr(func, PyMagicName::name());
138     Py_DECREF(func);
139     if (func_name == nullptr)
140         Py_FatalError("unexpected name problem in compute_name_key");
141     return func_name;
142 }
143 
compute_name_key(PyObject * ob)144 static PyObject *compute_name_key(PyObject *ob)
145 {
146     if (PyType_Check(ob))
147         return GetTypeKey(ob);
148     AutoDecRef func_name(get_funcname(ob));
149     AutoDecRef type_key(GetTypeKey(GetClassOrModOf(ob)));
150     return Py_BuildValue("(OO)", type_key.object(), func_name.object());
151 }
152 
_func_with_new_name(PyTypeObject * type,PyMethodDef * meth,const char * new_name)153 static PyObject *_func_with_new_name(PyTypeObject *type,
154                                      PyMethodDef *meth,
155                                      const char *new_name)
156 {
157     /*
158      * Create a function with a lower case name.
159      * Note: This is similar to feature_select's methodWithNewName,
160      * but does not create a descriptor.
161      * XXX Maybe we can get rid of this, completely?
162      */
163     auto obtype = reinterpret_cast<PyObject *>(type);
164     int len = strlen(new_name);
165     auto name = new char[len + 1];
166     strcpy(name, new_name);
167     auto new_meth = new PyMethodDef;
168     new_meth->ml_name = name;
169     new_meth->ml_meth = meth->ml_meth;
170     new_meth->ml_flags = meth->ml_flags;
171     new_meth->ml_doc = meth->ml_doc;
172     return PyCFunction_NewEx(new_meth, obtype, nullptr);
173 }
174 
build_name_key_to_func(PyObject * obtype)175 static int build_name_key_to_func(PyObject *obtype)
176 {
177     auto *type = reinterpret_cast<PyTypeObject *>(obtype);
178     PyMethodDef *meth = type->tp_methods;
179 
180     if (meth == nullptr)
181         return 0;
182 
183     AutoDecRef type_key(GetTypeKey(obtype));
184     for (; meth->ml_name != nullptr; meth++) {
185         AutoDecRef func(PyCFunction_NewEx(meth, obtype, nullptr));
186         AutoDecRef func_name(get_funcname(func));
187         AutoDecRef name_key(Py_BuildValue("(OO)", type_key.object(), func_name.object()));
188         if (func.isNull() || name_key.isNull()
189             || PyDict_SetItem(pyside_globals->map_dict, name_key, func) < 0)
190             return -1;
191     }
192     // PYSIDE-1019: Now we repeat the same for snake case names.
193     meth = type->tp_methods;
194     for (; meth->ml_name != nullptr; meth++) {
195         const char *name = String::toCString(String::getSnakeCaseName(meth->ml_name, true));
196         AutoDecRef func(_func_with_new_name(type, meth, name));
197         AutoDecRef func_name(get_funcname(func));
198         AutoDecRef name_key(Py_BuildValue("(OO)", type_key.object(), func_name.object()));
199         if (func.isNull() || name_key.isNull()
200             || PyDict_SetItem(pyside_globals->map_dict, name_key, func) < 0)
201             return -1;
202     }
203     return 0;
204 }
205 
name_key_to_func(PyObject * ob)206 PyObject *name_key_to_func(PyObject *ob)
207 {
208     /*
209      * We build a mapping from name_key to function.
210      * This could also be computed directly, but the Limited API
211      * makes this impossible. So we always build our own mapping.
212      */
213     AutoDecRef name_key(compute_name_key(ob));
214     if (name_key.isNull())
215         Py_RETURN_NONE;
216 
217     PyObject *ret = PyDict_GetItem(pyside_globals->map_dict, name_key);
218     if (ret == nullptr) {
219         // do a lazy initialization
220         AutoDecRef type_key(GetTypeKey(GetClassOrModOf(ob)));
221         PyObject *type = PyDict_GetItem(pyside_globals->map_dict,
222                                         type_key);
223         if (type == nullptr)
224             Py_RETURN_NONE;
225         assert(PyType_Check(type));
226         if (build_name_key_to_func(type) < 0)
227             return nullptr;
228         ret = PyDict_GetItem(pyside_globals->map_dict, name_key);
229     }
230     Py_XINCREF(ret);
231     return ret;
232 }
233 
_build_new_entry(PyObject * new_name,PyObject * value)234 static PyObject *_build_new_entry(PyObject *new_name, PyObject *value)
235 {
236     PyObject *new_value = PyDict_Copy(value);
237     PyObject *multi = PyDict_GetItem(value, PyName::multi());
238     if (multi != nullptr && Py_TYPE(multi) == &PyList_Type) {
239         ssize_t len = PyList_Size(multi);
240         AutoDecRef list(PyList_New(len));
241         if (list.isNull())
242             return nullptr;
243         for (int idx = 0; idx < len; ++idx) {
244             auto multi_entry = PyList_GetItem(multi, idx);
245             auto dup = PyDict_Copy(multi_entry);
246             if (PyDict_SetItem(dup, PyName::name(), new_name) < 0)
247                 return nullptr;
248             if (PyList_SetItem(list, idx, dup) < 0)
249                 return nullptr;
250         }
251         if (PyDict_SetItem(new_value, PyName::multi(), list) < 0)
252             return nullptr;
253     } else {
254         if (PyDict_SetItem(new_value, PyName::name(), new_name) < 0)
255             return nullptr;
256     }
257     return new_value;
258 }
259 
insert_snake_case_variants(PyObject * dict)260 int insert_snake_case_variants(PyObject *dict)
261 {
262     AutoDecRef snake_dict(PyDict_New());
263     PyObject *key, *value;
264     Py_ssize_t pos = 0;
265     while (PyDict_Next(dict, &pos, &key, &value)) {
266         AutoDecRef name(String::getSnakeCaseName(key, true));
267         AutoDecRef new_value(_build_new_entry(name, value));
268         if (PyDict_SetItem(snake_dict, name, new_value) < 0)
269             return -1;
270     }
271     return PyDict_Merge(dict, snake_dict, 0);
272 }
273 
_get_class_of_cf(PyObject * ob_cf)274 PyObject *_get_class_of_cf(PyObject *ob_cf)
275 {
276     PyObject *selftype = PyCFunction_GET_SELF(ob_cf);
277     if (selftype == nullptr) {
278         selftype = PyDict_GetItem(pyside_globals->map_dict, ob_cf);
279         if (selftype == nullptr) {
280             // This must be an overloaded function that we handled special.
281             AutoDecRef special(Py_BuildValue("(OO)", ob_cf, PyName::overload()));
282             selftype = PyDict_GetItem(pyside_globals->map_dict, special);
283             if (selftype == nullptr) {
284                 // This is probably a module function. We will return type(None).
285                 selftype = Py_None;
286             }
287         }
288     }
289 
290     PyObject *obtype_mod = (PyType_Check(selftype) || PyModule_Check(selftype))
291                            ? selftype
292                            : reinterpret_cast<PyObject *>(Py_TYPE(selftype));
293     Py_INCREF(obtype_mod);
294     return obtype_mod;
295 }
296 
_get_class_of_sm(PyObject * ob_sm)297 PyObject *_get_class_of_sm(PyObject *ob_sm)
298 {
299     AutoDecRef func(PyObject_GetAttr(ob_sm, PyMagicName::func()));
300     return _get_class_of_cf(func);
301 }
302 
_get_class_of_descr(PyObject * ob)303 PyObject *_get_class_of_descr(PyObject *ob)
304 {
305     return PyObject_GetAttr(ob, PyMagicName::objclass());
306 }
307 
_address_to_stringlist(PyObject * numkey)308 PyObject *_address_to_stringlist(PyObject *numkey)
309 {
310     /*
311      * This is a tiny optimization that saves initialization time.
312      * Instead of creating all Python strings during the call to
313      * `PySide_BuildSignatureArgs`, we store the address of the stringlist.
314      * When needed in `PySide_BuildSignatureProps`, the strings are
315      * finally materialized.
316      */
317     ssize_t address = PyNumber_AsSsize_t(numkey, PyExc_ValueError);
318     if (address == -1 && PyErr_Occurred())
319         return nullptr;
320     char **sig_strings = reinterpret_cast<char **>(address);
321     PyObject *res_list = PyList_New(0);
322     if (res_list == nullptr)
323         return nullptr;
324     for (; *sig_strings != nullptr; ++sig_strings) {
325         char *sig_str = *sig_strings;
326         AutoDecRef pystr(Py_BuildValue("s", sig_str));
327         if (pystr.isNull() || PyList_Append(res_list, pystr) < 0)
328             return nullptr;
329     }
330     return res_list;
331 }
332 
_build_func_to_type(PyObject * obtype)333 static int _build_func_to_type(PyObject *obtype)
334 {
335     /*
336      * There is no general way to directly get the type of a static method.
337      * On Python 3, the type is hidden in an unused pointer in the
338      * PyCFunction structure, but the Limited API does not allow to access
339      * this, either.
340      *
341      * In the end, it was easier to avoid such tricks and build an explicit
342      * mapping from function to type.
343      *
344      * We walk through the method list of the type
345      * and record the mapping from static method to this type in a dict.
346      * We also check for hidden methods, see below.
347      */
348     auto *type = reinterpret_cast<PyTypeObject *>(obtype);
349     PyObject *dict = type->tp_dict;
350     PyMethodDef *meth = type->tp_methods;
351 
352     if (meth == nullptr)
353         return 0;
354 
355     for (; meth->ml_name != nullptr; meth++) {
356         /*
357          * It is possible that a method is overwritten by another
358          * attribute with the same name. This case was obviously provoked
359          * explicitly in "testbinding.TestObject.staticMethodDouble",
360          * where instead of the method a "PySide2.QtCore.Signal" object
361          * was in the dict.
362          * This overlap is also found in regular PySide under
363          * "PySide2.QtCore.QProcess.error" where again a signal object is
364          * returned. These hidden methods will be opened for the
365          * signature module by adding them under the name
366          * "{name}.overload".
367          */
368         PyObject *descr = PyDict_GetItemString(dict, meth->ml_name);
369         PyObject *look_attr = meth->ml_flags & METH_STATIC ? PyMagicName::func()
370                                                            : PyMagicName::name();
371         int check_name = meth->ml_flags & METH_STATIC ? 0 : 1;
372         if (descr == nullptr)
373             return -1;
374 
375         // We first check all methods if one is hidden by something else.
376         AutoDecRef look(PyObject_GetAttr(descr, look_attr));
377         AutoDecRef given(Py_BuildValue("s", meth->ml_name));
378         if (look.isNull()
379             || (check_name && PyObject_RichCompareBool(look, given, Py_EQ) != 1)) {
380             PyErr_Clear();
381             AutoDecRef cfunc(PyCFunction_NewEx(
382                                 meth, reinterpret_cast<PyObject *>(type), nullptr));
383             if (cfunc.isNull())
384                 return -1;
385             if (meth->ml_flags & METH_STATIC)
386                 descr = PyStaticMethod_New(cfunc);
387             else
388                 descr = PyDescr_NewMethod(type, meth);
389             if (descr == nullptr)
390                 return -1;
391             char mangled_name[200];
392             strcpy(mangled_name, meth->ml_name);
393             strcat(mangled_name, ".overload");
394             if (PyDict_SetItemString(dict, mangled_name, descr) < 0)
395                 return -1;
396             if (meth->ml_flags & METH_STATIC) {
397                 // This is the special case where a static method is hidden.
398                 AutoDecRef special(Py_BuildValue("(Os)", cfunc.object(), "overload"));
399                 if (PyDict_SetItem(pyside_globals->map_dict, special, obtype) < 0)
400                     return -1;
401             }
402             if (PyDict_SetItemString(pyside_globals->map_dict, mangled_name, obtype) < 0)
403                 return -1;
404             continue;
405         }
406         // Then we insert the mapping for static methods.
407         if (meth->ml_flags & METH_STATIC) {
408             if (PyDict_SetItem(pyside_globals->map_dict, look, obtype) < 0)
409                 return -1;
410         }
411     }
412     return 0;
413 }
414 
_finish_nested_classes(PyObject * obdict)415 int _finish_nested_classes(PyObject *obdict)
416 {
417     PyObject *key, *value, *obtype;
418     PyTypeObject *subtype;
419     Py_ssize_t pos = 0;
420 
421     if (obdict == nullptr)
422         return -1;
423     while (PyDict_Next(obdict, &pos, &key, &value)) {
424         if (PyType_Check(value)) {
425             obtype = value;
426             if (_build_func_to_type(obtype) < 0)
427                 return -1;
428             // now continue with nested cases
429             subtype = reinterpret_cast<PyTypeObject *>(obtype);
430             if (_finish_nested_classes(subtype->tp_dict) < 0)
431                 return -1;
432         }
433     }
434     return 0;
435 }
436 
437 } // extern "C"
438