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