aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/libshiboken/signature
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/libshiboken/signature')
-rw-r--r--sources/shiboken6/libshiboken/signature/signature.cpp645
-rw-r--r--sources/shiboken6/libshiboken/signature/signature_extend.cpp228
-rw-r--r--sources/shiboken6/libshiboken/signature/signature_globals.cpp260
-rw-r--r--sources/shiboken6/libshiboken/signature/signature_helper.cpp392
4 files changed, 1525 insertions, 0 deletions
diff --git a/sources/shiboken6/libshiboken/signature/signature.cpp b/sources/shiboken6/libshiboken/signature/signature.cpp
new file mode 100644
index 000000000..45269844e
--- /dev/null
+++ b/sources/shiboken6/libshiboken/signature/signature.cpp
@@ -0,0 +1,645 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+////////////////////////////////////////////////////////////////////////////
+//
+// signature.cpp
+// -------------
+//
+// This is the main file of the signature module.
+// It contains the most important functions and avoids confusion
+// by moving many helper functions elsewhere.
+//
+// General documentation can be found in `signature_doc.rst`.
+//
+
+#include "signature.h"
+#include "signature_p.h"
+
+#include "basewrapper.h"
+#include "autodecref.h"
+#include "sbkstring.h"
+#include "sbkstaticstrings.h"
+#include "sbkstaticstrings_p.h"
+#include "sbkfeature_base.h"
+
+#include <structmember.h>
+
+using namespace Shiboken;
+
+extern "C"
+{
+
+static PyObject *CreateSignature(PyObject *props, PyObject *key)
+{
+ /*
+ * Here is the new function to create all signatures. It simply calls
+ * into Python and creates a signature object directly.
+ * This is so much simpler than using all the attributes explicitly
+ * to support '_signature_is_functionlike()'.
+ */
+ return PyObject_CallFunction(pyside_globals->create_signature_func,
+ "(OO)", props, key);
+}
+
+PyObject *GetClassOrModOf(PyObject *ob)
+{
+ /*
+ * Return the type or module of a function or type.
+ * The purpose is finally to use the name of the object.
+ */
+ if (PyType_Check(ob)) {
+ // PySide-928: The type case must do refcounting like the others as well.
+ Py_INCREF(ob);
+ return ob;
+ }
+#ifdef PYPY_VERSION
+ // PYSIDE-535: PyPy has a special builtin method that acts almost like PyCFunction.
+ if (Py_TYPE(ob) == PepBuiltinMethod_TypePtr)
+ return _get_class_of_bm(ob);
+#endif
+ if (PyType_IsSubtype(Py_TYPE(ob), &PyCFunction_Type))
+ return _get_class_of_cf(ob);
+ if (Py_TYPE(ob) == PepStaticMethod_TypePtr)
+ return _get_class_of_sm(ob);
+ if (Py_TYPE(ob) == PepMethodDescr_TypePtr)
+ return _get_class_of_descr(ob);
+ if (Py_TYPE(ob) == &PyWrapperDescr_Type)
+ return _get_class_of_descr(ob);
+ Py_FatalError("unexpected type in GetClassOrModOf");
+ return nullptr;
+}
+
+PyObject *GetTypeKey(PyObject *ob)
+{
+ assert(PyType_Check(ob) || PyModule_Check(ob));
+ /*
+ * Obtain a unique key using the module name and the type name.
+ *
+ * PYSIDE-1286: We use correct __module__ and __qualname__, now.
+ */
+ AutoDecRef module_name(PyObject_GetAttr(ob, PyMagicName::module()));
+ if (module_name.isNull()) {
+ // We have no module_name because this is a module ;-)
+ PyErr_Clear();
+ module_name.reset(PyObject_GetAttr(ob, PyMagicName::name()));
+ return Py_BuildValue("O", module_name.object());
+ }
+ AutoDecRef class_name(PyObject_GetAttr(ob, PyMagicName::qualname()));
+ if (class_name.isNull()) {
+ Py_FatalError("Signature: missing class name in GetTypeKey");
+ return nullptr;
+ }
+ return Py_BuildValue("(OO)", module_name.object(), class_name.object());
+}
+
+static PyObject *empty_dict = nullptr;
+
+PyObject *TypeKey_to_PropsDict(PyObject *type_key)
+{
+ PyObject *dict = PyDict_GetItem(pyside_globals->arg_dict, type_key);
+ if (dict == nullptr) {
+ if (empty_dict == nullptr)
+ empty_dict = PyDict_New();
+ dict = empty_dict;
+ }
+ if (!PyDict_Check(dict))
+ dict = PySide_BuildSignatureProps(type_key);
+ return dict;
+}
+
+static PyObject *_GetSignature_Cached(PyObject *props, PyObject *func_kind, PyObject *modifier)
+{
+ // Special case: We want to know the func_kind.
+ if (modifier) {
+ PyUnicode_InternInPlace(&modifier);
+ if (modifier == PyMagicName::func_kind())
+ return Py_BuildValue("O", func_kind);
+ }
+
+ AutoDecRef key(modifier == nullptr ? Py_BuildValue("O", func_kind)
+ : Py_BuildValue("(OO)", func_kind, modifier));
+ PyObject *value = PyDict_GetItem(props, key);
+ if (value == nullptr) {
+ // we need to compute a signature object
+ value = CreateSignature(props, key);
+ if (value != nullptr) {
+ if (PyDict_SetItem(props, key, value) < 0)
+ // this is an error
+ return nullptr;
+ }
+ else {
+ // key not found
+ Py_RETURN_NONE;
+ }
+ }
+ return Py_INCREF(value), value;
+}
+
+#ifdef PYPY_VERSION
+PyObject *GetSignature_Method(PyObject *obfunc, PyObject *modifier)
+{
+ AutoDecRef obtype_mod(GetClassOrModOf(obfunc));
+ AutoDecRef type_key(GetTypeKey(obtype_mod));
+ if (type_key.isNull())
+ Py_RETURN_NONE;
+ PyObject *dict = TypeKey_to_PropsDict(type_key);
+ if (dict == nullptr)
+ return nullptr;
+ AutoDecRef func_name(PyObject_GetAttr(obfunc, PyMagicName::name()));
+ PyObject *props = !func_name.isNull() ? PyDict_GetItem(dict, func_name) : nullptr;
+ if (props == nullptr)
+ Py_RETURN_NONE;
+ return _GetSignature_Cached(props, PyName::method(), modifier);
+}
+#endif
+
+PyObject *GetSignature_Function(PyObject *obfunc, PyObject *modifier)
+{
+ // make sure that we look into PyCFunction, only...
+ if (Py_TYPE(obfunc) == PepFunction_TypePtr)
+ Py_RETURN_NONE;
+ AutoDecRef obtype_mod(GetClassOrModOf(obfunc));
+ AutoDecRef type_key(GetTypeKey(obtype_mod));
+ if (type_key.isNull())
+ Py_RETURN_NONE;
+ PyObject *dict = TypeKey_to_PropsDict(type_key);
+ if (dict == nullptr)
+ return nullptr;
+ AutoDecRef func_name(PyObject_GetAttr(obfunc, PyMagicName::name()));
+ PyObject *props = !func_name.isNull() ? PyDict_GetItem(dict, func_name) : nullptr;
+ if (props == nullptr)
+ Py_RETURN_NONE;
+
+ int flags = PyCFunction_GET_FLAGS(obfunc);
+ PyObject *func_kind;
+ if (PyModule_Check(obtype_mod.object()))
+ func_kind = PyName::function();
+ else if (flags & METH_CLASS)
+ func_kind = PyName::classmethod();
+ else if (flags & METH_STATIC)
+ func_kind = PyName::staticmethod();
+ else
+ func_kind = PyName::method();
+ return _GetSignature_Cached(props, func_kind, modifier);
+}
+
+PyObject *GetSignature_Wrapper(PyObject *ob, PyObject *modifier)
+{
+ AutoDecRef func_name(PyObject_GetAttr(ob, PyMagicName::name()));
+ AutoDecRef objclass(PyObject_GetAttr(ob, PyMagicName::objclass()));
+ AutoDecRef class_key(GetTypeKey(objclass));
+ if (func_name.isNull() || objclass.isNull() || class_key.isNull())
+ return nullptr;
+ PyObject *dict = TypeKey_to_PropsDict(class_key);
+ if (dict == nullptr)
+ return nullptr;
+ PyObject *props = PyDict_GetItem(dict, func_name);
+ if (props == nullptr) {
+ // handle `__init__` like the class itself
+ if (PyUnicode_CompareWithASCIIString(func_name, "__init__") == 0)
+ return GetSignature_TypeMod(objclass, modifier);
+ Py_RETURN_NONE;
+ }
+ return _GetSignature_Cached(props, PyName::method(), modifier);
+}
+
+PyObject *GetSignature_TypeMod(PyObject *ob, PyObject *modifier)
+{
+ AutoDecRef ob_name(PyObject_GetAttr(ob, PyMagicName::name()));
+ AutoDecRef ob_key(GetTypeKey(ob));
+
+ PyObject *dict = TypeKey_to_PropsDict(ob_key);
+ if (dict == nullptr)
+ return nullptr;
+ PyObject *props = PyDict_GetItem(dict, ob_name);
+ if (props == nullptr)
+ Py_RETURN_NONE;
+ return _GetSignature_Cached(props, PyName::method(), modifier);
+}
+
+////////////////////////////////////////////////////////////////////////////
+//
+// get_signature -- providing a superior interface
+//
+// Additional to the interface via `__signature__`, we also provide
+// a general function, which allows for different signature layouts.
+// The `modifier` argument is a string that is passed in from `loader.py`.
+// Configuration what the modifiers mean is completely in Python.
+//
+// PYSIDE-2101: The __signature__ attribute is gone due to rlcompleter.
+//
+
+PyObject *get_signature_intern(PyObject *ob, PyObject *modifier)
+{
+#ifdef PYPY_VERSION
+ // PYSIDE-535: PyPy has a special builtin method that acts almost like PyCFunction.
+ if (Py_TYPE(ob) == PepBuiltinMethod_TypePtr) {
+ return pyside_bm_get___signature__(ob, modifier);
+ }
+#endif
+ if (PyType_IsSubtype(Py_TYPE(ob), &PyCFunction_Type))
+ return pyside_cf_get___signature__(ob, modifier);
+ if (Py_TYPE(ob) == PepStaticMethod_TypePtr)
+ return pyside_sm_get___signature__(ob, modifier);
+ if (Py_TYPE(ob) == PepMethodDescr_TypePtr)
+ return pyside_md_get___signature__(ob, modifier);
+ if (PyType_Check(ob))
+ return pyside_tp_get___signature__(ob, modifier);
+ if (Py_TYPE(ob) == &PyWrapperDescr_Type)
+ return pyside_wd_get___signature__(ob, modifier);
+ // For classmethods we use the simple wrapper description implementation.
+ if (Py_TYPE(ob) == &PyClassMethodDescr_Type)
+ return pyside_wd_get___signature__(ob, modifier);
+ return nullptr;
+}
+
+static PyObject *get_signature(PyObject * /* self */, PyObject *args)
+{
+ PyObject *ob;
+ PyObject *modifier = nullptr;
+
+ if (!PyArg_ParseTuple(args, "O|O", &ob, &modifier))
+ return nullptr;
+ if (Py_TYPE(ob) == PepFunction_TypePtr)
+ Py_RETURN_NONE;
+ PyObject *ret = get_signature_intern(ob, modifier);
+ if (ret != nullptr)
+ return ret;
+ Py_RETURN_NONE;
+}
+
+////////////////////////////////////////////////////////////////////////////
+//
+// feature_import -- special handling for `from __feature__ import ...`
+//
+// The actual function is implemented in Python.
+// When no features are involved, we redirect to the original import.
+// This avoids an extra function level in tracebacks that is irritating.
+//
+
+static PyObject *feature_import(PyObject * /* self */, PyObject *args, PyObject *kwds)
+{
+ PyObject *ret = PyObject_Call(pyside_globals->feature_import_func, args, kwds);
+ if (ret != Py_None)
+ return ret;
+ // feature_import did not handle it, so call the normal import.
+ Py_DECREF(ret);
+ static PyObject *builtins = PyEval_GetBuiltins();
+ PyObject *import_func = PyDict_GetItemString(builtins, "__orig_import__");
+ if (import_func == nullptr) {
+ Py_FatalError("builtins has no \"__orig_import__\" function");
+ }
+ ret = PyObject_Call(import_func, args, kwds);
+ if (ret) {
+ // PYSIDE-2029: Intercept after the import to search for PySide usage.
+ PyObject *post = PyObject_CallFunctionObjArgs(pyside_globals->feature_imported_func,
+ ret, nullptr);
+ Py_XDECREF(post);
+ if (post == nullptr) {
+ Py_DECREF(ret);
+ return nullptr;
+ }
+ }
+ return ret;
+}
+
+PyMethodDef signature_methods[] = {
+ {"__feature_import__", (PyCFunction)feature_import, METH_VARARGS | METH_KEYWORDS, nullptr},
+ {"get_signature", (PyCFunction)get_signature, METH_VARARGS,
+ "get the signature, passing an optional string parameter"},
+ {nullptr, nullptr, 0, nullptr}
+};
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Argument Handling
+// -----------------
+//
+// * PySide_BuildSignatureArgs
+//
+// Called during class or module initialization.
+// The signature strings from the C modules are stored in a dict for
+// later use.
+//
+// * PySide_BuildSignatureProps
+//
+// Called on demand during signature retieval. This function calls all the way
+// through `parser.py` and prepares all properties for the functions of the class.
+// The parsed properties can then be used to create signature objects.
+//
+
+static int PySide_BuildSignatureArgs(PyObject *obtype_mod, const char *signatures[])
+{
+ AutoDecRef type_key(GetTypeKey(obtype_mod));
+ /*
+ * PYSIDE-996: Avoid string overflow in MSVC, which has a limit of
+ * 2**15 unicode characters (64 K memory).
+ * Instead of one huge string, we take a ssize_t that is the
+ * address of a string array. It will not be turned into a real
+ * string list until really used by Python. This is quite optimal.
+ */
+ AutoDecRef numkey(Py_BuildValue("n", signatures));
+ if (type_key.isNull() || numkey.isNull()
+ || PyDict_SetItem(pyside_globals->arg_dict, type_key, numkey) < 0)
+ return -1;
+ /*
+ * We record also a mapping from type key to type/module. This helps to
+ * lazily initialize the Py_LIMITED_API in name_key_to_func().
+ */
+ return PyDict_SetItem(pyside_globals->map_dict, type_key, obtype_mod) == 0 ? 0 : -1;
+}
+
+PyObject *PySide_BuildSignatureProps(PyObject *type_key)
+{
+ /*
+ * Here is the second part of the function.
+ * This part will be called on-demand when needed by some attribute.
+ * We simply pick up the arguments that we stored here and replace
+ * them by the function result.
+ */
+ if (type_key == nullptr)
+ return nullptr;
+ PyObject *numkey = PyDict_GetItem(pyside_globals->arg_dict, type_key);
+ AutoDecRef strings(_address_to_stringlist(numkey));
+ if (strings.isNull())
+ return nullptr;
+ AutoDecRef arg_tup(Py_BuildValue("(OO)", type_key, strings.object()));
+ if (arg_tup.isNull())
+ return nullptr;
+ PyObject *dict = PyObject_CallObject(pyside_globals->pyside_type_init_func, arg_tup);
+ if (dict == nullptr) {
+ if (PyErr_Occurred())
+ return nullptr;
+ // No error: return an empty dict.
+ if (empty_dict == nullptr)
+ empty_dict = PyDict_New();
+ return empty_dict;
+ }
+ // PYSIDE-1019: Build snake case versions of the functions.
+ if (insert_snake_case_variants(dict) < 0)
+ return nullptr;
+ // We replace the arguments by the result dict.
+ if (PyDict_SetItem(pyside_globals->arg_dict, type_key, dict) < 0)
+ return nullptr;
+ return dict;
+}
+//
+////////////////////////////////////////////////////////////////////////////
+
+#ifdef PYPY_VERSION
+static bool get_lldebug_flag()
+{
+ auto *dic = PySys_GetObject("pypy_translation_info");
+ int lldebug = PyObject_IsTrue(PyDict_GetItemString(dic, "translation.lldebug"));
+ int lldebug0 = PyObject_IsTrue(PyDict_GetItemString(dic, "translation.lldebug0"));
+ return lldebug || lldebug0;
+}
+
+#endif
+
+static int PySide_FinishSignatures(PyObject *module, const char *signatures[])
+{
+#ifdef PYPY_VERSION
+ static const bool have_problem = get_lldebug_flag();
+ if (have_problem)
+ return 0; // crash with lldebug at `PyDict_Next`
+#endif
+ /*
+ * Initialization of module functions and resolving of static methods.
+ */
+ const char *name = PyModule_GetName(module);
+ if (name == nullptr)
+ return -1;
+
+ // we abuse the call for types, since they both have a __name__ attribute.
+ if (PySide_BuildSignatureArgs(module, signatures) < 0)
+ return -1;
+
+ /*
+ * Note: This function crashed when called from PySide_BuildSignatureArgs.
+ * Probably this was an import timing problem.
+ *
+ * Pep384: We need to switch this always on since we have no access
+ * to the PyCFunction attributes. Therefore I simplified things
+ * and always use our own mapping.
+ */
+ PyObject *key{};
+ PyObject *func{};
+ PyObject *obdict = PyModule_GetDict(module);
+ Py_ssize_t pos = 0;
+
+ while (PyDict_Next(obdict, &pos, &key, &func))
+ if (PyCFunction_Check(func))
+ if (PyDict_SetItem(pyside_globals->map_dict, func, module) < 0)
+ return -1;
+ // The finish_import function will not work the first time since phase 2
+ // was not yet run. But that is ok, because the first import is always for
+ // the shiboken module (or a test module).
+ if (pyside_globals->finish_import_func == nullptr) {
+ assert(strncmp(name, "PySide6.", 8) != 0);
+ return 0;
+ }
+ AutoDecRef ret(PyObject_CallFunction(
+ pyside_globals->finish_import_func, "(O)", module));
+ return ret.isNull() ? -1 : 0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+//
+// External functions interface
+//
+// These are exactly the supported functions from `signature.h`.
+//
+
+int InitSignatureStrings(PyTypeObject *type, const char *signatures[])
+{
+ // PYSIDE-2404: This function now also builds the mapping for static methods.
+ // It was one missing spot to let Lazy import work.
+ init_shibokensupport_module();
+ auto *ob_type = reinterpret_cast<PyObject *>(type);
+ int ret = PySide_BuildSignatureArgs(ob_type, signatures);
+ if (ret < 0 || _build_func_to_type(ob_type) < 0) {
+ PyErr_Print();
+ PyErr_SetNone(PyExc_ImportError);
+ }
+ return ret;
+}
+
+void FinishSignatureInitialization(PyObject *module, const char *signatures[])
+{
+ /*
+ * This function is called at the very end of a module initialization.
+ * We now patch certain types to support the __signature__ attribute,
+ * initialize module functions and resolve static methods.
+ *
+ * Still, it is not possible to call init phase 2 from here,
+ * because the import is still running. Do it from Python!
+ */
+ init_shibokensupport_module();
+
+#ifndef PYPY_VERSION
+ static const bool patch_types = true;
+#else
+ // PYSIDE-535: On PyPy we cannot patch builtin types. This can be
+ // re-implemented later. For now, we use `get_signature`, instead.
+ static const bool patch_types = false;
+#endif
+
+ if ((patch_types && PySide_PatchTypes() < 0)
+ || PySide_FinishSignatures(module, signatures) < 0) {
+ PyErr_Print();
+ PyErr_SetNone(PyExc_ImportError);
+ }
+}
+
+static PyObject *adjustFuncName(const char *func_name)
+{
+ /*
+ * PYSIDE-1019: Modify the function name expression according to feature.
+ *
+ * - snake_case
+ * The function name must be converted.
+ * - full_property
+ * The property name must be used and "fset" appended.
+ *
+ * modname.subname.classsname.propname.fset
+ *
+ * Class properties must use the expression
+ *
+ * modname.subname.classsname.__dict__['propname'].fset
+ *
+ * Note that fget is impossible because there are no parameters.
+ */
+ static const char mapping_name[] = "shibokensupport.signature.mapping";
+ static PyObject *sys_modules = PySys_GetObject("modules");
+ static PyObject *mapping = PyDict_GetItemString(sys_modules, mapping_name);
+ static PyObject *ns = PyModule_GetDict(mapping);
+
+ char _path[200 + 1] = {};
+ const char *_name = strrchr(func_name, '.');
+ strncat(_path, func_name, _name - func_name);
+ ++_name;
+
+ // This is a very cheap call into `mapping.py`.
+ PyObject *update_mapping = PyDict_GetItemString(ns, "update_mapping");
+ AutoDecRef res(PyObject_CallFunctionObjArgs(update_mapping, nullptr));
+ if (res.isNull())
+ return nullptr;
+
+ // Run `eval` on the type string to get the object.
+ // PYSIDE-1710: If the eval does not work, return the given string.
+ AutoDecRef obtype(PyRun_String(_path, Py_eval_input, ns, ns));
+ if (obtype.isNull())
+ return String::fromCString(func_name);
+
+ if (PyModule_Check(obtype.object())) {
+ // This is a plain function. Return the unmangled name.
+ return String::fromCString(func_name);
+ }
+ assert(PyType_Check(obtype)); // This was not true for __init__!
+
+ // Find the feature flags
+ auto *type = reinterpret_cast<PyTypeObject *>(obtype.object());
+ AutoDecRef dict(PepType_GetDict(type));
+ int id = currentSelectId(type);
+ id = id < 0 ? 0 : id; // if undefined, set to zero
+ auto lower = id & 0x01;
+ auto is_prop = id & 0x02;
+ bool is_class_prop = false;
+
+ // Compute all needed info.
+ PyObject *name = String::getSnakeCaseName(_name, lower);
+ PyObject *prop_name{};
+ if (is_prop) {
+ PyObject *prop_methods = PyDict_GetItem(dict, PyMagicName::property_methods());
+ prop_name = PyDict_GetItem(prop_methods, name);
+ if (prop_name != nullptr) {
+ PyObject *prop = PyDict_GetItem(dict, prop_name);
+ is_class_prop = Py_TYPE(prop) != &PyProperty_Type;
+ }
+ }
+
+ // Finally, generate the correct path expression.
+ char _buf[250 + 1] = {};
+ if (prop_name) {
+ auto _prop_name = String::toCString(prop_name);
+ if (is_class_prop)
+ snprintf(_buf, sizeof(_buf), "%s.__dict__['%s'].fset", _path, _prop_name);
+ else
+ snprintf(_buf, sizeof(_buf), "%s.%s.fset", _path, _prop_name);
+ }
+ else {
+ auto _name = String::toCString(name);
+ snprintf(_buf, sizeof(_buf), "%s.%s", _path, _name);
+ }
+ return String::fromCString(_buf);
+}
+
+void SetError_Argument(PyObject *args, const char *func_name, PyObject *info)
+{
+ init_shibokensupport_module();
+ /*
+ * This function replaces the type error construction with extra
+ * overloads parameter in favor of using the signature module.
+ * Error messages are rare, so we do it completely in Python.
+ */
+
+ // PYSIDE-1305: Handle errors set by fillQtProperties.
+ if (PyErr_Occurred()) {
+ PyObject *e{};
+ PyObject *v{};
+ PyObject *t{};
+ // Note: These references are all borrowed.
+ PyErr_Fetch(&e, &v, &t);
+ Py_DECREF(e);
+ info = v;
+ Py_XDECREF(t);
+ }
+ // PYSIDE-1019: Modify the function name expression according to feature.
+ AutoDecRef new_func_name(adjustFuncName(func_name));
+ if (new_func_name.isNull()) {
+ PyErr_Print();
+ Py_FatalError("seterror_argument failed to call update_mapping");
+ }
+ if (info == nullptr)
+ info = Py_None;
+ AutoDecRef res(PyObject_CallFunctionObjArgs(pyside_globals->seterror_argument_func,
+ args, new_func_name.object(), info, nullptr));
+ if (res.isNull()) {
+ PyErr_Print();
+ Py_FatalError("seterror_argument did not receive a result");
+ }
+ PyObject *err{};
+ PyObject *msg{};
+ if (!PyArg_UnpackTuple(res, func_name, 2, 2, &err, &msg)) {
+ PyErr_Print();
+ Py_FatalError("unexpected failure in seterror_argument");
+ }
+ PyErr_SetObject(err, msg);
+}
+
+/*
+ * Support for the metatype SbkObjectType_Type's tp_getset.
+ *
+ * This was not necessary for __signature__, because PyType_Type inherited it.
+ * But the __doc__ attribute existed already by inheritance, and calling
+ * PyType_Modified() is not supported. So we added the getsets explicitly
+ * to the metatype.
+ *
+ * PYSIDE-2101: The __signature__ attribute is gone due to rlcompleter.
+ */
+
+PyObject *Sbk_TypeGet___doc__(PyObject *ob)
+{
+ init_shibokensupport_module();
+ return pyside_tp_get___doc__(ob);
+}
+
+PyObject *GetFeatureDict()
+{
+ init_shibokensupport_module();
+ return pyside_globals->feature_dict;
+}
+
+} //extern "C"
diff --git a/sources/shiboken6/libshiboken/signature/signature_extend.cpp b/sources/shiboken6/libshiboken/signature/signature_extend.cpp
new file mode 100644
index 000000000..3ce9e21e3
--- /dev/null
+++ b/sources/shiboken6/libshiboken/signature/signature_extend.cpp
@@ -0,0 +1,228 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+////////////////////////////////////////////////////////////////////////////
+//
+// signature_extend.cpp
+// --------------------
+//
+// This file contains the additions and changes to the following
+// Python types:
+//
+// PyMethodDescr_Type
+// PyCFunction_Type
+// PyStaticMethod_Type
+// (*) PyType_Type
+// PyWrapperDescr_Type
+//
+// Their `tp_getset` fields are modified to support the `__signature__`
+// attribute and additions to the `__doc__` attribute.
+//
+// PYSIDE-535: PyType_Type patching is removed,
+// Shiboken.ObjectType and Shiboken.EnumMeta have new getsets, instead.
+
+#include "autodecref.h"
+#include "sbkstring.h"
+#include "sbkstaticstrings.h"
+#include "sbkstaticstrings_p.h"
+
+#include "signature_p.h"
+
+using namespace Shiboken;
+
+extern "C" {
+
+using signaturefunc = PyObject *(*)(PyObject *, PyObject *);
+
+static PyObject *_get_written_signature(signaturefunc sf, PyObject *ob, PyObject *modifier)
+{
+ /*
+ * Be a writable Attribute, but have a computed value.
+ *
+ * If a signature has not been written, call the signature function.
+ * If it has been written, return the written value.
+ * After __del__ was called, the function value re-appears.
+ *
+ * Note: This serves also for the new version that does not allow any
+ * assignment if we have a computed value. We only need to check if
+ * a computed value exists and then forbid writing.
+ * See pyside_set___signature
+ */
+ PyObject *ret = PyDict_GetItem(pyside_globals->value_dict, ob);
+ if (ret == nullptr)
+ return ob == nullptr ? nullptr : sf(ob, modifier);
+ Py_INCREF(ret);
+ return ret;
+}
+
+#ifdef PYPY_VERSION
+PyObject *pyside_bm_get___signature__(PyObject *func, PyObject *modifier)
+{
+ return _get_written_signature(GetSignature_Method, func, modifier);
+}
+#endif
+
+PyObject *pyside_cf_get___signature__(PyObject *func, PyObject *modifier)
+{
+ return _get_written_signature(GetSignature_Function, func, modifier);
+}
+
+PyObject *pyside_sm_get___signature__(PyObject *sm, PyObject *modifier)
+{
+ AutoDecRef func(PyObject_GetAttr(sm, PyMagicName::func()));
+ return _get_written_signature(GetSignature_Function, func, modifier);
+}
+
+PyObject *pyside_md_get___signature__(PyObject *ob_md, PyObject *modifier)
+{
+ AutoDecRef func(name_key_to_func(ob_md));
+ if (func.object() == Py_None)
+ Py_RETURN_NONE;
+ if (func.isNull())
+ Py_FatalError("missing mapping in MethodDescriptor");
+ return pyside_cf_get___signature__(func, modifier);
+}
+
+PyObject *pyside_wd_get___signature__(PyObject *ob, PyObject *modifier)
+{
+ return _get_written_signature(GetSignature_Wrapper, ob, modifier);
+}
+
+PyObject *pyside_tp_get___signature__(PyObject *obtype_mod, PyObject *modifier)
+{
+ return _get_written_signature(GetSignature_TypeMod, obtype_mod, modifier);
+}
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Augmenting builtin types with a __signature__ attribute.
+//
+// This is a harmless change to Python, similar like __text_signature__.
+// We could avoid it, but then we would need to copy quite some module
+// initialization functions which are pretty version- and word size
+// dependent. I think this little patch is the lesser of the two evils.
+//
+// Please note that in fact we are modifying 'type', the metaclass of all
+// objects, because we add new functionality.
+//
+// Addendum 2019-01-12: We now also compute a docstring from the signature.
+//
+
+// keep the original __doc__ functions
+static PyObject *old_cf_doc_descr = nullptr;
+static PyObject *old_sm_doc_descr = nullptr;
+static PyObject *old_md_doc_descr = nullptr;
+static PyObject *old_tp_doc_descr = nullptr;
+static PyObject *old_wd_doc_descr = nullptr;
+
+static int handle_doc_in_progress = 0;
+
+static PyObject *handle_doc(PyObject *ob, PyObject *old_descr)
+{
+ AutoDecRef ob_type_mod(GetClassOrModOf(ob));
+ bool isModule = PyModule_Check(ob_type_mod.object());
+ const char *name = isModule
+ ? PyModule_GetName(ob_type_mod.object())
+ : reinterpret_cast<PyTypeObject *>(ob_type_mod.object())->tp_name;
+ PyObject *res{};
+
+ if (handle_doc_in_progress || name == nullptr
+ || (isModule && strncmp(name, "PySide6.", 8) != 0)) {
+ res = PyObject_CallMethodObjArgs(old_descr, PyMagicName::get(), ob, nullptr);
+ } else {
+ handle_doc_in_progress++;
+ res = PyObject_CallFunction(pyside_globals->make_helptext_func, "(O)", ob);
+ handle_doc_in_progress--;
+ }
+
+ if (res)
+ return res;
+
+ PyErr_Clear();
+ Py_RETURN_NONE;
+}
+
+static PyObject *pyside_cf_get___doc__(PyObject *cf)
+{
+ return handle_doc(cf, old_cf_doc_descr);
+}
+
+static PyObject *pyside_sm_get___doc__(PyObject *sm)
+{
+ return handle_doc(sm, old_sm_doc_descr);
+}
+
+static PyObject *pyside_md_get___doc__(PyObject *md)
+{
+ return handle_doc(md, old_md_doc_descr);
+}
+
+PyObject *pyside_tp_get___doc__(PyObject *tp)
+{
+ return handle_doc(tp, old_tp_doc_descr);
+}
+
+static PyObject *pyside_wd_get___doc__(PyObject *wd)
+{
+ return handle_doc(wd, old_wd_doc_descr);
+}
+
+// PYSIDE-535: We cannot patch types easily in PyPy.
+// Let's use the `get_signature` function, instead.
+static PyGetSetDef new_PyCFunction_getsets[] = {
+ {const_cast<char *>("__doc__"), reinterpret_cast<getter>(pyside_cf_get___doc__),
+ nullptr, nullptr, nullptr},
+ {nullptr, nullptr, nullptr, nullptr, nullptr}
+};
+
+static PyGetSetDef new_PyStaticMethod_getsets[] = {
+ {const_cast<char *>("__doc__"), reinterpret_cast<getter>(pyside_sm_get___doc__),
+ nullptr, nullptr, nullptr},
+ {nullptr, nullptr, nullptr, nullptr, nullptr}
+};
+
+static PyGetSetDef new_PyMethodDescr_getsets[] = {
+ {const_cast<char *>("__doc__"), reinterpret_cast<getter>(pyside_md_get___doc__),
+ nullptr, nullptr, nullptr},
+ {nullptr, nullptr, nullptr, nullptr, nullptr}
+};
+
+static PyGetSetDef new_PyWrapperDescr_getsets[] = {
+ {const_cast<char *>("__doc__"), reinterpret_cast<getter>(pyside_wd_get___doc__),
+ nullptr, nullptr, nullptr},
+ {nullptr, nullptr, nullptr, nullptr, nullptr}
+};
+
+int PySide_PatchTypes(void)
+{
+ static int init_done = 0;
+
+ if (!init_done) {
+ AutoDecRef meth_descr(PyObject_GetAttrString(
+ reinterpret_cast<PyObject *>(&PyUnicode_Type), "split"));
+ AutoDecRef wrap_descr(PyObject_GetAttrString(
+ reinterpret_cast<PyObject *>(Py_TYPE(Py_True)), "__add__"));
+ // abbreviations for readability
+ auto *md_gs = new_PyMethodDescr_getsets;
+ auto *md_doc = &old_md_doc_descr;
+ auto *cf_gs = new_PyCFunction_getsets;
+ auto *cf_doc = &old_cf_doc_descr;
+ auto *sm_gs = new_PyStaticMethod_getsets;
+ auto *sm_doc = &old_sm_doc_descr;
+ auto *wd_gs = new_PyWrapperDescr_getsets;
+ auto *wd_doc = &old_wd_doc_descr;
+
+ if (meth_descr.isNull() || wrap_descr.isNull()
+ || PyType_Ready(Py_TYPE(meth_descr)) < 0
+ || add_more_getsets(PepMethodDescr_TypePtr, md_gs, md_doc) < 0
+ || add_more_getsets(&PyCFunction_Type, cf_gs, cf_doc) < 0
+ || add_more_getsets(PepStaticMethod_TypePtr, sm_gs, sm_doc) < 0
+ || add_more_getsets(Py_TYPE(wrap_descr), wd_gs, wd_doc) < 0
+ )
+ return -1;
+ init_done = 1;
+ }
+ return 0;
+}
+
+} // extern "C"
diff --git a/sources/shiboken6/libshiboken/signature/signature_globals.cpp b/sources/shiboken6/libshiboken/signature/signature_globals.cpp
new file mode 100644
index 000000000..0a08309cc
--- /dev/null
+++ b/sources/shiboken6/libshiboken/signature/signature_globals.cpp
@@ -0,0 +1,260 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+////////////////////////////////////////////////////////////////////////////
+//
+// signature_global.cpp
+//
+// This file contains the global data structures and init code.
+//
+
+#include "autodecref.h"
+#include "sbkstring.h"
+#include "sbkstaticstrings.h"
+#include "sbkstaticstrings_p.h"
+#include "sbkenum.h"
+
+#include "signature_p.h"
+
+using namespace Shiboken;
+
+extern "C" {
+
+static const char *PySide_CompressedSignaturePackage[] = {
+#include "embed/signature_inc.h"
+ };
+
+static const unsigned char PySide_SignatureLoader[] = {
+#include "embed/signature_bootstrap_inc.h"
+ };
+
+static safe_globals_struc *init_phase_1()
+{
+ do {
+ auto *p = reinterpret_cast<safe_globals_struc *>
+ (malloc(sizeof(safe_globals_struc)));
+ if (p == nullptr)
+ break;
+ /*
+ * Initializing module signature_bootstrap.
+ * Since we now have an embedding script, we can do this without any
+ * Python strings in the C code.
+ */
+#if defined(Py_LIMITED_API) || defined(SHIBOKEN_NO_EMBEDDING_PYC)
+ // We must work for multiple versions or we are cross-building for a different
+ // Python version interpreter, so use source code.
+#else
+ AutoDecRef marshal_module(PyImport_Import(PyName::marshal())); // builtin
+ AutoDecRef loads(PyObject_GetAttr(marshal_module, PyName::loads()));
+ if (loads.isNull())
+ break;
+#endif
+ char *bytes_cast = reinterpret_cast<char *>(
+ const_cast<unsigned char *>(PySide_SignatureLoader));
+ AutoDecRef bytes(PyBytes_FromStringAndSize(bytes_cast, sizeof(PySide_SignatureLoader)));
+ if (bytes.isNull())
+ break;
+#if defined(Py_LIMITED_API) || defined(SHIBOKEN_NO_EMBEDDING_PYC)
+ PyObject *builtins = PyEval_GetBuiltins();
+ PyObject *compile = PyDict_GetItem(builtins, PyName::compile());
+ if (compile == nullptr)
+ break;
+ AutoDecRef code_obj(PyObject_CallFunction(compile, "Oss",
+ bytes.object(), "signature_bootstrap.py", "exec"));
+#else
+ AutoDecRef code_obj(PyObject_CallFunctionObjArgs(
+ loads, bytes.object(), nullptr));
+#endif
+ if (code_obj.isNull())
+ break;
+ p->helper_module = PyImport_ExecCodeModule("signature_bootstrap", code_obj);
+ if (p->helper_module == nullptr)
+ break;
+ // Initialize the module
+ PyObject *mdict = PyModule_GetDict(p->helper_module);
+ if (PyDict_SetItem(mdict, PyMagicName::builtins(), PyEval_GetBuiltins()) < 0)
+ break;
+
+ /*********************************************************************
+ *
+ * Attention!
+ * ----------
+ *
+ * We are unpacking an embedded ZIP file with more signature modules.
+ * They will be loaded later with the zipimporter.
+ * The file `signature_bootstrap.py` does the unpacking and starts the
+ * loader. See `init_phase_2`.
+ *
+ * Due to MSVC's limitation to 64k strings, we needed to assemble pieces.
+ */
+ auto **block_ptr = reinterpret_cast<const char **>(PySide_CompressedSignaturePackage);
+ PyObject *piece{};
+ AutoDecRef zipped_string_sequence(PyList_New(0));
+ for (; **block_ptr != 0; ++block_ptr) {
+ // we avoid the string/unicode dilemma by not using PyString_XXX:
+ piece = Py_BuildValue("s", *block_ptr);
+ if (piece == nullptr || PyList_Append(zipped_string_sequence, piece) < 0)
+ break;
+ }
+ if (PyDict_SetItemString(mdict, "zipstring_sequence", zipped_string_sequence) < 0)
+ break;
+
+ // build a dict for diverse mappings
+ p->map_dict = PyDict_New();
+
+ // build a dict for the prepared arguments
+ p->arg_dict = PyDict_New();
+ if (PyObject_SetAttrString(p->helper_module, "pyside_arg_dict", p->arg_dict) < 0)
+ break;
+
+ // build a dict for assigned signature values
+ p->value_dict = PyDict_New();
+
+ // PYSIDE-1019: build a __feature__ dict
+ p->feature_dict = PyDict_New();
+ if (PyObject_SetAttrString(p->helper_module, "pyside_feature_dict", p->feature_dict) < 0)
+ break;
+
+ // This function will be disabled until phase 2 is done.
+ p->finish_import_func = nullptr;
+
+ return p;
+
+ } while (false);
+
+ PyErr_Print();
+ Py_FatalError("could not initialize part 1");
+ return nullptr;
+}
+
+static int init_phase_2(safe_globals_struc *p, PyMethodDef *methods)
+{
+ do {
+ // The single function to be called, but maybe more to come.
+ for (PyMethodDef *ml = methods; ml->ml_name != nullptr; ++ml) {
+ PyObject *v = PyCFunction_NewEx(ml, nullptr, nullptr);
+ if (v == nullptr
+ || PyObject_SetAttrString(p->helper_module, ml->ml_name, v) != 0)
+ break;
+ Py_DECREF(v);
+ }
+ // The first entry is __feature_import__, add documentation.
+ PyObject *builtins = PyEval_GetBuiltins();
+ PyObject *imp_func = PyDict_GetItemString(builtins, "__import__");
+ PyObject *imp_doc = PyObject_GetAttrString(imp_func, "__doc__");
+ signature_methods[0].ml_doc = String::toCString(imp_doc);
+
+ PyObject *bootstrap_func = PyObject_GetAttrString(p->helper_module, "bootstrap");
+ if (bootstrap_func == nullptr)
+ break;
+
+ /*********************************************************************
+ *
+ * Attention!
+ * ----------
+ *
+ * This is the entry point where everything in folder
+ * `shibokensupport` becomes initialized. It starts with
+ * `signature_bootstrap.py` and continues from there to `loader.py`.
+ *
+ * The return value of the bootstrap function is the loader module.
+ */
+ PyObject *loader = PyObject_CallFunctionObjArgs(bootstrap_func, nullptr);
+ if (loader == nullptr)
+ break;
+
+ // now the loader should be initialized
+ p->pyside_type_init_func = PyObject_GetAttrString(loader, "pyside_type_init");
+ if (p->pyside_type_init_func == nullptr)
+ break;
+ p->create_signature_func = PyObject_GetAttrString(loader, "create_signature");
+ if (p->create_signature_func == nullptr)
+ break;
+ p->seterror_argument_func = PyObject_GetAttrString(loader, "seterror_argument");
+ if (p->seterror_argument_func == nullptr)
+ break;
+ p->make_helptext_func = PyObject_GetAttrString(loader, "make_helptext");
+ if (p->make_helptext_func == nullptr)
+ break;
+ p->finish_import_func = PyObject_GetAttrString(loader, "finish_import");
+ if (p->finish_import_func == nullptr)
+ break;
+ p->feature_import_func = PyObject_GetAttrString(loader, "feature_import");
+ if (p->feature_import_func == nullptr)
+ break;
+ p->feature_imported_func = PyObject_GetAttrString(loader, "feature_imported");
+ if (p->feature_imported_func == nullptr)
+ break;
+
+ // We call stuff like the feature initialization late,
+ // after all the function pointers are in place.
+ PyObject *post_init_func = PyObject_GetAttrString(loader, "post_init");
+ if (post_init_func == nullptr)
+ break;
+ PyObject *ret = PyObject_CallFunctionObjArgs(post_init_func, nullptr);
+ if (ret == nullptr)
+ break;
+
+ return 0;
+
+ } while (0);
+
+ PyErr_Print();
+ Py_FatalError("could not initialize part 2");
+ return -1;
+}
+
+#ifndef _WIN32
+////////////////////////////////////////////////////////////////////////////
+// a stack trace for linux-like platforms
+#include <cstdio>
+#if defined(__GLIBC__)
+# include <execinfo.h>
+#endif
+#include <signal.h>
+#include <cstdlib>
+#include <unistd.h>
+
+static void handler(int sig) {
+#if defined(__GLIBC__)
+ void *array[30];
+ // get void *'s for all entries on the stack
+ const int size = backtrace(array, 30);
+
+ // print out all the frames to stderr
+#endif
+ std::fprintf(stderr, "Error: signal %d:\n", sig);
+#if defined(__GLIBC__)
+ backtrace_symbols_fd(array, size, STDERR_FILENO);
+#endif
+ exit(1);
+}
+
+////////////////////////////////////////////////////////////////////////////
+#endif // _WIN32
+
+safe_globals_struc *pyside_globals = nullptr;
+
+void init_shibokensupport_module(void)
+{
+ static int init_done = 0;
+
+ if (!init_done) {
+ pyside_globals = init_phase_1();
+ if (pyside_globals != nullptr)
+ init_done = 1;
+
+#ifndef _WIN32
+ // We enable the stack trace in CI, only.
+ const char *testEnv = getenv("QTEST_ENVIRONMENT");
+ if (testEnv && strstr(testEnv, "ci"))
+ signal(SIGSEGV, handler); // install our handler
+#endif // _WIN32
+
+ init_phase_2(pyside_globals, signature_methods);
+ // Enum must be initialized when signatures exist, not earlier.
+ init_enum();
+ }
+}
+
+} // extern "C"
diff --git a/sources/shiboken6/libshiboken/signature/signature_helper.cpp b/sources/shiboken6/libshiboken/signature/signature_helper.cpp
new file mode 100644
index 000000000..9aab7a6a9
--- /dev/null
+++ b/sources/shiboken6/libshiboken/signature/signature_helper.cpp
@@ -0,0 +1,392 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+////////////////////////////////////////////////////////////////////////////
+//
+// signature_helper.cpp
+// --------------------
+//
+// This file contains assoerted helper functions that are needed,
+// but it is not helpful to see them all the time.
+//
+
+#include "autodecref.h"
+#include "sbkstring.h"
+#include "sbkstaticstrings.h"
+#include "sbkstaticstrings_p.h"
+
+#include "signature_p.h"
+
+#include <cstring>
+
+using namespace Shiboken;
+
+extern "C" {
+
+static int _fixup_getset(PyTypeObject *type, const char *name, PyGetSetDef *new_gsp)
+{
+ /*
+ * This function pre-fills all fields of the new gsp. We then
+ * insert the changed values.
+ */
+ PyGetSetDef *gsp = type->tp_getset;
+ if (gsp != nullptr) {
+ for (; gsp->name != nullptr; gsp++) {
+ if (strcmp(gsp->name, name) == 0) {
+ new_gsp->set = gsp->set;
+ new_gsp->doc = gsp->doc;
+ new_gsp->closure = gsp->closure;
+ return 1; // success
+ }
+ }
+ }
+ PyMemberDef *md = type->tp_members;
+ if (md != nullptr)
+ for (; md->name != nullptr; md++)
+ if (strcmp(md->name, name) == 0)
+ return 1;
+ return 0;
+}
+
+int add_more_getsets(PyTypeObject *type, PyGetSetDef *gsp, PyObject **doc_descr)
+{
+ /*
+ * This function is used to assign a new `__signature__` attribute,
+ * and also to override a `__doc__` or `__name__` attribute.
+ *
+ * PYSIDE-2101: The __signature__ attribute is gone due to rlcompleter.
+ */
+ assert(PyType_Check(type));
+ PyType_Ready(type);
+ AutoDecRef tpDict(PepType_GetDict(type));
+ auto *dict = tpDict.object();
+ for (; gsp->name != nullptr; gsp++) {
+ PyObject *have_descr = PyDict_GetItemString(dict, gsp->name);
+ if (have_descr != nullptr) {
+ Py_INCREF(have_descr);
+ if (strcmp(gsp->name, "__doc__") == 0)
+ *doc_descr = have_descr;
+ else
+ assert(false);
+ if (!_fixup_getset(type, gsp->name, gsp))
+ continue;
+ }
+ AutoDecRef descr(PyDescr_NewGetSet(type, gsp));
+ if (descr.isNull())
+ return -1;
+ // PYSIDE-535: We cannot set the attribute. For simplicity, we use
+ // get_signature in PyPy, instead. This can be re-implemented
+ // later by deriving extra heap types.
+ if (PyDict_SetItemString(dict, gsp->name, descr) < 0)
+ return -1;
+ }
+ PyType_Modified(type);
+ return 0;
+}
+
+static PyObject *get_funcname(PyObject *ob)
+{
+ PyObject *func = ob;
+ if (Py_TYPE(ob) == PepStaticMethod_TypePtr)
+ func = PyObject_GetAttr(ob, PyMagicName::func());
+ else
+ Py_INCREF(func);
+ PyObject *func_name = PyObject_GetAttr(func, PyMagicName::name());
+ Py_DECREF(func);
+ if (func_name == nullptr)
+ Py_FatalError("unexpected name problem in compute_name_key");
+ return func_name;
+}
+
+static PyObject *compute_name_key(PyObject *ob)
+{
+ if (PyType_Check(ob))
+ return GetTypeKey(ob);
+ AutoDecRef func_name(get_funcname(ob));
+ AutoDecRef type_key(GetTypeKey(GetClassOrModOf(ob)));
+ return Py_BuildValue("(OO)", type_key.object(), func_name.object());
+}
+
+static PyObject *_func_with_new_name(PyTypeObject *type,
+ PyMethodDef *meth,
+ const char *new_name)
+{
+ /*
+ * Create a function with a lower case name.
+ * Note: This is similar to feature_select's methodWithNewName,
+ * but does not create a descriptor.
+ * XXX Maybe we can get rid of this, completely?
+ */
+ auto *obtype = reinterpret_cast<PyObject *>(type);
+ const size_t len = std::strlen(new_name);
+ auto *name = new char[len + 1];
+ std::strcpy(name, new_name);
+ auto *new_meth = new PyMethodDef;
+ new_meth->ml_name = name;
+ new_meth->ml_meth = meth->ml_meth;
+ new_meth->ml_flags = meth->ml_flags;
+ new_meth->ml_doc = meth->ml_doc;
+ return PyCFunction_NewEx(new_meth, obtype, nullptr);
+}
+
+static int build_name_key_to_func(PyObject *obtype)
+{
+ auto *type = reinterpret_cast<PyTypeObject *>(obtype);
+ PyMethodDef *meth = type->tp_methods;
+
+ if (meth == nullptr)
+ return 0;
+
+ AutoDecRef type_key(GetTypeKey(obtype));
+ for (; meth->ml_name != nullptr; meth++) {
+ AutoDecRef func(PyCFunction_NewEx(meth, obtype, nullptr));
+ AutoDecRef func_name(get_funcname(func));
+ AutoDecRef name_key(Py_BuildValue("(OO)", type_key.object(), func_name.object()));
+ if (func.isNull() || name_key.isNull()
+ || PyDict_SetItem(pyside_globals->map_dict, name_key, func) < 0)
+ return -1;
+ }
+ // PYSIDE-1019: Now we repeat the same for snake case names.
+ meth = type->tp_methods;
+ for (; meth->ml_name != nullptr; meth++) {
+ const char *name = String::toCString(String::getSnakeCaseName(meth->ml_name, true));
+ AutoDecRef func(_func_with_new_name(type, meth, name));
+ AutoDecRef func_name(get_funcname(func));
+ AutoDecRef name_key(Py_BuildValue("(OO)", type_key.object(), func_name.object()));
+ if (func.isNull() || name_key.isNull()
+ || PyDict_SetItem(pyside_globals->map_dict, name_key, func) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+PyObject *name_key_to_func(PyObject *ob)
+{
+ /*
+ * We build a mapping from name_key to function.
+ * This could also be computed directly, but the Limited API
+ * makes this impossible. So we always build our own mapping.
+ */
+ AutoDecRef name_key(compute_name_key(ob));
+ if (name_key.isNull())
+ Py_RETURN_NONE;
+
+ PyObject *ret = PyDict_GetItem(pyside_globals->map_dict, name_key);
+ if (ret == nullptr) {
+ // do a lazy initialization
+ AutoDecRef type_key(GetTypeKey(GetClassOrModOf(ob)));
+ PyObject *type = PyDict_GetItem(pyside_globals->map_dict,
+ type_key);
+ if (type == nullptr)
+ Py_RETURN_NONE;
+ assert(PyType_Check(type));
+ if (build_name_key_to_func(type) < 0)
+ return nullptr;
+ ret = PyDict_GetItem(pyside_globals->map_dict, name_key);
+ }
+ Py_XINCREF(ret);
+ return ret;
+}
+
+static PyObject *_build_new_entry(PyObject *new_name, PyObject *value)
+{
+ PyObject *new_value = PyDict_Copy(value);
+ PyObject *multi = PyDict_GetItem(value, PyName::multi());
+ if (multi != nullptr && Py_TYPE(multi) == &PyList_Type) {
+ Py_ssize_t len = PyList_Size(multi);
+ AutoDecRef list(PyList_New(len));
+ if (list.isNull())
+ return nullptr;
+ for (int idx = 0; idx < len; ++idx) {
+ auto *multi_entry = PyList_GetItem(multi, idx);
+ auto *dup = PyDict_Copy(multi_entry);
+ if (PyDict_SetItem(dup, PyName::name(), new_name) < 0)
+ return nullptr;
+ if (PyList_SetItem(list, idx, dup) < 0)
+ return nullptr;
+ }
+ if (PyDict_SetItem(new_value, PyName::multi(), list) < 0)
+ return nullptr;
+ } else {
+ if (PyDict_SetItem(new_value, PyName::name(), new_name) < 0)
+ return nullptr;
+ }
+ return new_value;
+}
+
+int insert_snake_case_variants(PyObject *dict)
+{
+ AutoDecRef snake_dict(PyDict_New());
+ PyObject *key{};
+ PyObject *value{};
+ Py_ssize_t pos = 0;
+ while (PyDict_Next(dict, &pos, &key, &value)) {
+ AutoDecRef name(String::getSnakeCaseName(key, true));
+ AutoDecRef new_value(_build_new_entry(name, value));
+ if (PyDict_SetItem(snake_dict, name, new_value) < 0)
+ return -1;
+ }
+ return PyDict_Merge(dict, snake_dict, 0);
+}
+
+#ifdef PYPY_VERSION
+PyObject *_get_class_of_bm(PyObject *ob_bm)
+{
+ AutoDecRef self(PyObject_GetAttr(ob_bm, PyMagicName::self()));
+ auto *klass = PyObject_GetAttr(self, PyMagicName::class_());
+ return klass;
+}
+#endif
+
+PyObject *_get_class_of_cf(PyObject *ob_cf)
+{
+ PyObject *selftype = PyCFunction_GET_SELF(ob_cf);
+ if (selftype == nullptr) {
+ selftype = PyDict_GetItem(pyside_globals->map_dict, ob_cf);
+ if (selftype == nullptr) {
+ // This must be an overloaded function that we handled special.
+ AutoDecRef special(Py_BuildValue("(OO)", ob_cf, PyName::overload()));
+ selftype = PyDict_GetItem(pyside_globals->map_dict, special);
+ if (selftype == nullptr) {
+ // This is probably a module function. We will return type(None).
+ selftype = Py_None;
+ }
+ }
+ }
+
+ PyObject *obtype_mod = (PyType_Check(selftype) || PyModule_Check(selftype))
+ ? selftype
+ : reinterpret_cast<PyObject *>(Py_TYPE(selftype));
+ Py_INCREF(obtype_mod);
+ return obtype_mod;
+}
+
+PyObject *_get_class_of_sm(PyObject *ob_sm)
+{
+ AutoDecRef func(PyObject_GetAttr(ob_sm, PyMagicName::func()));
+ return _get_class_of_cf(func);
+}
+
+PyObject *_get_class_of_descr(PyObject *ob)
+{
+ return PyObject_GetAttr(ob, PyMagicName::objclass());
+}
+
+PyObject *_address_to_stringlist(PyObject *numkey)
+{
+ /*
+ * This is a tiny optimization that saves initialization time.
+ * Instead of creating all Python strings during the call to
+ * `PySide_BuildSignatureArgs`, we store the address of the stringlist.
+ * When needed in `PySide_BuildSignatureProps`, the strings are
+ * finally materialized.
+ */
+ Py_ssize_t address = PyNumber_AsSsize_t(numkey, PyExc_ValueError);
+ if (address == -1 && PyErr_Occurred())
+ return nullptr;
+ char **sig_strings = reinterpret_cast<char **>(address);
+ PyObject *res_list = PyList_New(0);
+ if (res_list == nullptr)
+ return nullptr;
+ for (; *sig_strings != nullptr; ++sig_strings) {
+ char *sig_str = *sig_strings;
+ AutoDecRef pystr(Py_BuildValue("s", sig_str));
+ if (pystr.isNull() || PyList_Append(res_list, pystr) < 0)
+ return nullptr;
+ }
+ return res_list;
+}
+
+int _build_func_to_type(PyObject *obtype)
+{
+ /*
+ * There is no general way to directly get the type of a static method.
+ * On Python 3, the type is hidden in an unused pointer in the
+ * PyCFunction structure, but the Limited API does not allow to access
+ * this, either.
+ *
+ * In the end, it was easier to avoid such tricks and build an explicit
+ * mapping from function to type.
+ *
+ * We walk through the method list of the type
+ * and record the mapping from static method to this type in a dict.
+ * We also check for hidden methods, see below.
+ */
+ auto *type = reinterpret_cast<PyTypeObject *>(obtype);
+ AutoDecRef tpDict(PepType_GetDict(type));
+ auto *dict = tpDict.object();
+
+ // PYSIDE-2404: Get the original dict for late initialization.
+ // The dict might have been switched before signature init.
+ static const auto *pyTypeType_tp_dict = PepType_GetDict(&PyType_Type);
+ if (Py_TYPE(dict) != Py_TYPE(pyTypeType_tp_dict)) {
+ tpDict.reset(PyObject_GetAttr(dict, PyName::orig_dict()));
+ dict = tpDict.object();
+ }
+
+ PyMethodDef *meth = type->tp_methods;
+
+ if (meth == nullptr)
+ return 0;
+
+ for (; meth->ml_name != nullptr; meth++) {
+ /*
+ * It is possible that a method is overwritten by another
+ * attribute with the same name. This case was obviously provoked
+ * explicitly in "testbinding.TestObject.staticMethodDouble",
+ * where instead of the method a "PySide6.QtCore.Signal" object
+ * was in the dict.
+ * This overlap is also found in regular PySide under
+ * "PySide6.QtCore.QProcess.error" where again a signal object is
+ * returned. These hidden methods will be opened for the
+ * signature module by adding them under the name
+ * "{name}.overload".
+ */
+ PyObject *descr = PyDict_GetItemString(dict, meth->ml_name);
+ PyObject *look_attr = meth->ml_flags & METH_STATIC ? PyMagicName::func()
+ : PyMagicName::name();
+ int check_name = meth->ml_flags & METH_STATIC ? 0 : 1;
+ if (descr == nullptr)
+ return -1;
+
+ // We first check all methods if one is hidden by something else.
+ AutoDecRef look(PyObject_GetAttr(descr, look_attr));
+ AutoDecRef given(Py_BuildValue("s", meth->ml_name));
+ if (look.isNull()
+ || (check_name && PyObject_RichCompareBool(look, given, Py_EQ) != 1)) {
+ PyErr_Clear();
+ AutoDecRef cfunc(PyCFunction_NewEx(
+ meth, reinterpret_cast<PyObject *>(type), nullptr));
+ if (cfunc.isNull())
+ return -1;
+ if (meth->ml_flags & METH_STATIC)
+ descr = PyStaticMethod_New(cfunc);
+ else
+ descr = PyDescr_NewMethod(type, meth);
+ if (descr == nullptr)
+ return -1;
+ char mangled_name[200];
+ std::strcpy(mangled_name, meth->ml_name);
+ std::strcat(mangled_name, ".overload");
+ if (PyDict_SetItemString(dict, mangled_name, descr) < 0)
+ return -1;
+ if (meth->ml_flags & METH_STATIC) {
+ // This is the special case where a static method is hidden.
+ AutoDecRef special(Py_BuildValue("(Os)", cfunc.object(), "overload"));
+ if (PyDict_SetItem(pyside_globals->map_dict, special, obtype) < 0)
+ return -1;
+ }
+ if (PyDict_SetItemString(pyside_globals->map_dict, mangled_name, obtype) < 0)
+ return -1;
+ continue;
+ }
+ // Then we insert the mapping for static methods.
+ if (meth->ml_flags & METH_STATIC) {
+ if (PyDict_SetItem(pyside_globals->map_dict, look, obtype) < 0)
+ return -1;
+ }
+ }
+ return 0;
+}
+
+} // extern "C"