diff options
author | Christian Tismer <tismer@stackless.com> | 2021-03-22 17:10:51 +0100 |
---|---|---|
committer | Christian Tismer <tismer@stackless.com> | 2021-03-26 16:27:52 +0100 |
commit | 773e1105251a03131cbbdf4abcb0580cb425ff60 (patch) | |
tree | 0d9185db4d3087c75476da116ce560804a0e1465 /sources | |
parent | b89b8daeeac7b2b22c3887fa83fe0b986f9baec7 (diff) |
Nuitka: Allow for compiled functions and methods too
The author of this patch is Kay Hayen. He writes:
* Add support for compiled methods to the bindings manager.
* For slots, implement lookup of code objects for compiled methods
and functions with a new function avoiding code duplication.
* Look up attributes of slots, e.g. code objects that Nuitka also has
as well, methods have "im_func" and "im_self".
* Sometimes calling "tp_descr_get" Python object slot is the
universal thing to do, covering all types.
* Detect compiled methods as receiver targets too.
Task-number: PYSIDE-1523
Change-Id: I0277b583840710476198ed5e1ccaaccd672e7638
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
Diffstat (limited to 'sources')
-rw-r--r-- | sources/pyside6/PySide6/glue/qtcore.cpp | 42 | ||||
-rw-r--r-- | sources/pyside6/libpyside/globalreceiverv2.cpp | 28 | ||||
-rw-r--r-- | sources/pyside6/libpyside/pysidesignal.cpp | 111 | ||||
-rw-r--r-- | sources/pyside6/libpyside/pysidestaticstrings.cpp | 3 | ||||
-rw-r--r-- | sources/pyside6/libpyside/pysidestaticstrings.h | 3 | ||||
-rw-r--r-- | sources/shiboken6/generator/shiboken/cppgenerator.cpp | 12 | ||||
-rw-r--r-- | sources/shiboken6/libshiboken/bindingmanager.cpp | 42 | ||||
-rw-r--r-- | sources/shiboken6/libshiboken/sbkstaticstrings.cpp | 2 | ||||
-rw-r--r-- | sources/shiboken6/libshiboken/sbkstaticstrings.h | 2 |
9 files changed, 213 insertions, 32 deletions
diff --git a/sources/pyside6/PySide6/glue/qtcore.cpp b/sources/pyside6/PySide6/glue/qtcore.cpp index 0d70391cf..8989b631c 100644 --- a/sources/pyside6/PySide6/glue/qtcore.cpp +++ b/sources/pyside6/PySide6/glue/qtcore.cpp @@ -316,27 +316,61 @@ PyModule_AddStringConstant(module, "__version__", qVersion()); // @snippet qt-version // @snippet qobject-connect -static bool isDecorator(PyObject *method, PyObject *self) +static bool isMethodDecorator(PyObject *method, bool is_pymethod, PyObject *self) { Shiboken::AutoDecRef methodName(PyObject_GetAttr(method, Shiboken::PyMagicName::name())); if (!PyObject_HasAttr(self, methodName)) return true; Shiboken::AutoDecRef otherMethod(PyObject_GetAttr(self, methodName)); - return PyMethod_GET_FUNCTION(otherMethod.object()) != PyMethod_GET_FUNCTION(method); + + PyObject *function1, *function2; + + // PYSIDE-1523: Each could be a compiled method or a normal method here, for the + // compiled ones we can use the attributes. + if (PyMethod_Check(otherMethod.object())) { + function1 = PyMethod_GET_FUNCTION(otherMethod.object()); + } else { + function1 = PyObject_GetAttr(otherMethod.object(), Shiboken::PyName::im_func()); + Py_DECREF(function1); + // Not retaining a reference inline with what PyMethod_GET_FUNCTION does. + } + + if (is_pymethod) { + function2 = PyMethod_GET_FUNCTION(method); + } else { + function2 = PyObject_GetAttr(method, Shiboken::PyName::im_func()); + Py_DECREF(function2); + // Not retaining a reference inline with what PyMethod_GET_FUNCTION does. + } + + return function1 != function2; } -static bool getReceiver(QObject *source, const char *signal, PyObject *callback, QObject **receiver, PyObject **self, QByteArray *callbackSig) +static bool getReceiver(QObject *source, + const char *signal, + PyObject *callback, + QObject **receiver, + PyObject **self, + QByteArray *callbackSig) { bool forceGlobalReceiver = false; if (PyMethod_Check(callback)) { *self = PyMethod_GET_SELF(callback); if (%CHECKTYPE[QObject *](*self)) *receiver = %CONVERTTOCPP[QObject *](*self); - forceGlobalReceiver = isDecorator(callback, *self); + forceGlobalReceiver = isMethodDecorator(callback, true, *self); } else if (PyCFunction_Check(callback)) { *self = PyCFunction_GET_SELF(callback); if (*self && %CHECKTYPE[QObject *](*self)) *receiver = %CONVERTTOCPP[QObject *](*self); + } else if (PyObject_HasAttr(callback, Shiboken::PyName::im_func()) + && PyObject_HasAttr(callback, Shiboken::PyName::im_self())) { + *self = PyObject_GetAttr(callback, Shiboken::PyName::im_self()); + Py_DECREF(*self); + + if (%CHECKTYPE[QObject *](*self)) + *receiver = %CONVERTTOCPP[QObject *](*self); + forceGlobalReceiver = isMethodDecorator(callback, false, *self); } else if (PyCallable_Check(callback)) { // Ok, just a callable object *receiver = nullptr; diff --git a/sources/pyside6/libpyside/globalreceiverv2.cpp b/sources/pyside6/libpyside/globalreceiverv2.cpp index 1bbafb4ec..c8a6e9f99 100644 --- a/sources/pyside6/libpyside/globalreceiverv2.cpp +++ b/sources/pyside6/libpyside/globalreceiverv2.cpp @@ -40,6 +40,7 @@ #include "globalreceiverv2.h" #include "dynamicqmetaobject_p.h" #include "pysideweakref.h" +#include "pysidestaticstrings.h" #include "signalmanager.h" #include <autodecref.h> @@ -101,21 +102,35 @@ class DynamicSlotDataV2 using namespace PySide; DynamicSlotDataV2::DynamicSlotDataV2(PyObject *callback, GlobalReceiverV2 *parent) : - m_isMethod(PyMethod_Check(callback)), m_parent(parent) { Shiboken::GilState gil; - if (m_isMethod) { - //Can not store calback pointe because this will be destroyed at the end of the scope - //To avoid increment intance reference keep the callback information + if (PyMethod_Check(callback)) { + m_isMethod = true; + // To avoid increment instance reference keep the callback information m_callback = PyMethod_GET_FUNCTION(callback); + Py_INCREF(m_callback); m_pythonSelf = PyMethod_GET_SELF(callback); //monitor class from method lifetime m_weakRef = WeakRef::create(m_pythonSelf, DynamicSlotDataV2::onCallbackDestroyed, this); + } else if (PyObject_HasAttr(callback, PySide::PyName::im_func()) + && PyObject_HasAttr(callback, PySide::PyName::im_self())) { + // PYSIDE-1523: PyMethod_Check is not accepting compiled form, we just go by attributes. + m_isMethod = true; + + m_callback = PyObject_GetAttr(callback, PySide::PyName::im_func()); + Py_DECREF(m_callback); + + m_pythonSelf = PyObject_GetAttr(callback, PySide::PyName::im_self()); + Py_DECREF(m_pythonSelf); + //monitor class from method lifetime + m_weakRef = WeakRef::create(m_pythonSelf, DynamicSlotDataV2::onCallbackDestroyed, this); } else { + m_isMethod = false; + m_callback = callback; Py_INCREF(m_callback); } @@ -137,7 +152,7 @@ PyObject *DynamicSlotDataV2::callback() //create a callback based on method data if (m_isMethod) - callback = PyMethod_New(m_callback, m_pythonSelf); + callback = Py_TYPE(m_callback)->tp_descr_get(m_callback, m_pythonSelf, nullptr); else Py_INCREF(callback); @@ -174,8 +189,7 @@ DynamicSlotDataV2::~DynamicSlotDataV2() Py_XDECREF(m_weakRef); m_weakRef = nullptr; - if (!m_isMethod) - Py_DECREF(m_callback); + Py_DECREF(m_callback); } GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, GlobalReceiverV2MapPtr map) : diff --git a/sources/pyside6/libpyside/pysidesignal.cpp b/sources/pyside6/libpyside/pysidesignal.cpp index 0eedae39c..faa6fdce1 100644 --- a/sources/pyside6/libpyside/pysidesignal.cpp +++ b/sources/pyside6/libpyside/pysidesignal.cpp @@ -309,6 +309,82 @@ static void signalInstanceFree(void *self) Py_TYPE(pySelf)->tp_base->tp_free(self); } +// PYSIDE-1523: PyFunction_Check is not accepting compiled functions and +// PyMethod_Check is not allowing compiled methods, therefore also lookup +// "im_func" and "__code__" attributes, we allow for that with a dedicated +// function handling both. +static void extractFunctionArgumentsFromSlot(PyObject *slot, + PyObject *& function, + PepCodeObject *& objCode, + bool &isMethod, + QByteArray *functionName) +{ + isMethod = PyMethod_Check(slot); + bool isFunction = PyFunction_Check(slot); + + function = nullptr; + objCode = nullptr; + + if (isMethod || isFunction) { + function = isMethod ? PyMethod_GET_FUNCTION(slot) : slot; + objCode = reinterpret_cast<PepCodeObject *>(PyFunction_GET_CODE(function)); + + if (functionName != nullptr) { + *functionName = Shiboken::String::toCString(PepFunction_GetName(function)); + } + } else if (PyObject_HasAttr(slot, PySide::PyName::im_func())) { + // PYSIDE-1523: PyFunction_Check and PyMethod_Check are not accepting compiled forms, we + // just go by attributes. + isMethod = true; + + function = PyObject_GetAttr(slot, PySide::PyName::im_func()); + + // Not retaining a reference inline with what PyMethod_GET_FUNCTION does. + Py_DECREF(function); + + if (functionName != nullptr) { + PyObject *name = PyObject_GetAttr(function, PySide::PyMagicName::name()); + *functionName = Shiboken::String::toCString(name); + // Not retaining a reference inline with what PepFunction_GetName does. + Py_DECREF(name); + } + + objCode = reinterpret_cast<PepCodeObject *>( + PyObject_GetAttr(function, PySide::PyMagicName::code())); + // Not retaining a reference inline with what PyFunction_GET_CODE does. + Py_XDECREF(objCode); + + if (objCode == nullptr) { + // Should not happen, but lets handle it gracefully, maybe Nuitka one day + // makes these optional, or somebody defined a type named like it without + // it being actually being that. + function = nullptr; + } + } else if (strcmp(Py_TYPE(slot)->tp_name, "compiled_function") == 0) { + isMethod = false; + function = slot; + + if (functionName != nullptr) { + PyObject *name = PyObject_GetAttr(function, PySide::PyMagicName::name()); + *functionName = Shiboken::String::toCString(name); + // Not retaining a reference inline with what PepFunction_GetName does. + Py_DECREF(name); + } + + objCode = reinterpret_cast<PepCodeObject *>( + PyObject_GetAttr(function, PySide::PyMagicName::code())); + // Not retaining a reference inline with what PyFunction_GET_CODE does. + Py_XDECREF(objCode); + + if (objCode == nullptr) { + // Should not happen, but lets handle it gracefully, maybe Nuitka one day + // makes these optional, or somebody defined a type named like it without + // it being actually being that. + function = nullptr; + } + } +} + static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject *kwds) { PyObject *slot = nullptr; @@ -349,17 +425,17 @@ static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject } else { // Check signature of the slot (method or function) to match signal int slotArgs = -1; - bool useSelf = false; - bool isMethod = PyMethod_Check(slot); - bool isFunction = PyFunction_Check(slot); bool matchedSlot = false; PySideSignalInstance *it = source; - if (isMethod || isFunction) { - PyObject *function = isMethod ? PyMethod_GET_FUNCTION(slot) : slot; - auto *objCode = reinterpret_cast<PepCodeObject *>(PyFunction_GET_CODE(function)); - useSelf = isMethod; + PyObject *function = nullptr; + PepCodeObject *objCode = nullptr; + bool useSelf = false; + + extractFunctionArgumentsFromSlot(slot, function, objCode, useSelf, nullptr); + + if (function != nullptr) { slotArgs = PepCode_GET_FLAGS(objCode) & CO_VARARGS ? -1 : PepCode_GET_ARGCOUNT(objCode); if (useSelf) slotArgs -= 1; @@ -942,15 +1018,14 @@ QString getCallbackSignature(const char *signal, QObject *receiver, PyObject *ca { QByteArray functionName; int numArgs = -1; + + PyObject *function = nullptr; + PepCodeObject *objCode = nullptr; bool useSelf = false; - bool isMethod = PyMethod_Check(callback); - bool isFunction = PyFunction_Check(callback); - if (isMethod || isFunction) { - PyObject *function = isMethod ? PyMethod_GET_FUNCTION(callback) : callback; - auto objCode = reinterpret_cast<PepCodeObject *>(PyFunction_GET_CODE(function)); - functionName = Shiboken::String::toCString(PepFunction_GetName(function)); - useSelf = isMethod; + extractFunctionArgumentsFromSlot(callback, function, objCode, useSelf, &functionName); + + if (function != nullptr) { numArgs = PepCode_GET_FLAGS(objCode) & CO_VARARGS ? -1 : PepCode_GET_ARGCOUNT(objCode); } else if (PyCFunction_Check(callback)) { const PyCFunctionObject *funcObj = reinterpret_cast<const PyCFunctionObject *>(callback); @@ -1025,6 +1100,14 @@ QString codeCallbackName(PyObject *callback, const QString &funcName) PyObject *func = PyMethod_GET_FUNCTION(callback); return funcName + QString::number(quint64(self), 16) + QString::number(quint64(func), 16); } + // PYSIDE-1523: Handle the compiled case. + if (PyObject_HasAttr(callback, PySide::PyName::im_func()) + && PyObject_HasAttr(callback, PySide::PyName::im_self())) { + // Not retaining references inline with what PyMethod_GET_(SELF|FUNC) does. + Shiboken::AutoDecRef self(PyObject_GetAttr(callback, PySide::PyName::im_self())); + Shiboken::AutoDecRef func(PyObject_GetAttr(callback, PySide::PyName::im_func())); + return funcName + QString::number(quint64(self), 16) + QString::number(quint64(func), 16); + } return funcName + QString::number(quint64(callback), 16); } diff --git a/sources/pyside6/libpyside/pysidestaticstrings.cpp b/sources/pyside6/libpyside/pysidestaticstrings.cpp index 7705fd058..8f74cd19f 100644 --- a/sources/pyside6/libpyside/pysidestaticstrings.cpp +++ b/sources/pyside6/libpyside/pysidestaticstrings.cpp @@ -56,12 +56,15 @@ STATIC_STRING_IMPL(qtConnect, "connect") STATIC_STRING_IMPL(qtDisconnect, "disconnect") STATIC_STRING_IMPL(qtEmit, "emit") STATIC_STRING_IMPL(dict_ring, "dict_ring") +STATIC_STRING_IMPL(im_func, "im_func") +STATIC_STRING_IMPL(im_self, "im_self") STATIC_STRING_IMPL(name, "name") STATIC_STRING_IMPL(property, "property") STATIC_STRING_IMPL(select_id, "select_id") } // namespace PyName namespace PyMagicName { +STATIC_STRING_IMPL(code, "__code__") STATIC_STRING_IMPL(doc, "__doc__") STATIC_STRING_IMPL(func, "__func__") STATIC_STRING_IMPL(name, "__name__") diff --git a/sources/pyside6/libpyside/pysidestaticstrings.h b/sources/pyside6/libpyside/pysidestaticstrings.h index 6774e936a..86e2515dc 100644 --- a/sources/pyside6/libpyside/pysidestaticstrings.h +++ b/sources/pyside6/libpyside/pysidestaticstrings.h @@ -51,12 +51,15 @@ PyObject *qtConnect(); PyObject *qtDisconnect(); PyObject *qtEmit(); PyObject *dict_ring(); +PyObject *im_func(); +PyObject *im_self(); PyObject *name(); PyObject *property(); PyObject *select_id(); } // namespace PyName namespace PyMagicName { +PyObject *code(); PyObject *doc(); PyObject *func(); PyObject *name(); diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp index 3843ec46f..f6b0f47c5 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp +++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp @@ -5768,11 +5768,19 @@ void CppGenerator::writeGetattroFunction(TextStream &s, AttroCheck attroCheck, Indentation indent(s); // PYSIDE-772: Perform optimized name mangling. s << "Shiboken::AutoDecRef tmp(_Pep_PrivateMangle(self, name));\n" - << "if (auto meth = PyDict_GetItem(Py_TYPE(self)->tp_dict, tmp))\n"; + << "if (auto meth = PyDict_GetItem(Py_TYPE(self)->tp_dict, tmp)) {\n"; { Indentation indent(s); - s << "return PyFunction_Check(meth) ? SBK_PyMethod_New(meth, self) : " << getattrFunc << ";\n"; + // PYSIDE-1523: PyFunction_Check is not accepting compiled functions. + s << "if (strcmp(Py_TYPE(meth)->tp_name, \"compiled_function\") == 0)\n"; + { + Indentation indent(s); + s << "return Py_TYPE(meth)->tp_descr_get(meth, self, nullptr);\n"; + } + s << "return PyFunction_Check(meth) ? SBK_PyMethod_New(meth, self)\n" + << " : " << getattrFunc << ";\n"; } + s << "}\n"; } s << "}\n"; diff --git a/sources/shiboken6/libshiboken/bindingmanager.cpp b/sources/shiboken6/libshiboken/bindingmanager.cpp index e4c6c2320..bf448ac4a 100644 --- a/sources/shiboken6/libshiboken/bindingmanager.cpp +++ b/sources/shiboken6/libshiboken/bindingmanager.cpp @@ -317,8 +317,39 @@ PyObject *BindingManager::getOverride(const void *cptr, PyObject *method = PyObject_GetAttr(reinterpret_cast<PyObject *>(wrapper), pyMethodName); - if (method && PyMethod_Check(method) - && PyMethod_GET_SELF(method) == reinterpret_cast<PyObject *>(wrapper)) { + PyObject *function = nullptr; + + // PYSIDE-1523: PyMethod_Check is not accepting compiled methods, we do this rather + // crude check for them. + if (method) { + if (PyMethod_Check(method)) { + if (PyMethod_GET_SELF(method) == reinterpret_cast<PyObject *>(wrapper)) { + function = PyMethod_GET_FUNCTION(method); + } else { + Py_DECREF(method); + method = nullptr; + } + } else if (PyObject_HasAttr(method, PyName::im_self()) + && PyObject_HasAttr(method, PyName::im_func())) { + PyObject *im_self = PyObject_GetAttr(method, PyName::im_self()); + // Not retaining a reference inline with what PyMethod_GET_SELF does. + Py_DECREF(im_self); + + if (im_self == reinterpret_cast<PyObject *>(wrapper)) { + function = PyObject_GetAttr(method, PyName::im_func()); + // Not retaining a reference inline with what PyMethod_GET_FUNCTION does. + Py_DECREF(function); + } else { + Py_DECREF(method); + method = nullptr; + } + } else { + Py_DECREF(method); + method = nullptr; + } + } + + if (method != nullptr) { PyObject *defaultMethod; PyObject *mro = Py_TYPE(wrapper)->tp_mro; @@ -329,13 +360,14 @@ PyObject *BindingManager::getOverride(const void *cptr, auto *parent = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx)); if (parent->tp_dict) { defaultMethod = PyDict_GetItem(parent->tp_dict, pyMethodName); - if (defaultMethod && PyMethod_GET_FUNCTION(method) != defaultMethod) + if (defaultMethod && function != defaultMethod) return method; } } - } else { - Py_XDECREF(method); + + Py_DECREF(method); } + return nullptr; } diff --git a/sources/shiboken6/libshiboken/sbkstaticstrings.cpp b/sources/shiboken6/libshiboken/sbkstaticstrings.cpp index fd34c0eeb..7b2ddfb3e 100644 --- a/sources/shiboken6/libshiboken/sbkstaticstrings.cpp +++ b/sources/shiboken6/libshiboken/sbkstaticstrings.cpp @@ -55,6 +55,8 @@ namespace PyName { STATIC_STRING_IMPL(dumps, "dumps") STATIC_STRING_IMPL(fget, "fget") STATIC_STRING_IMPL(fset, "fset") +STATIC_STRING_IMPL(im_func, "im_func") +STATIC_STRING_IMPL(im_self, "im_self") STATIC_STRING_IMPL(loads, "loads") STATIC_STRING_IMPL(multi, "multi") STATIC_STRING_IMPL(name, "name") diff --git a/sources/shiboken6/libshiboken/sbkstaticstrings.h b/sources/shiboken6/libshiboken/sbkstaticstrings.h index 4aaef814f..773ff4e6c 100644 --- a/sources/shiboken6/libshiboken/sbkstaticstrings.h +++ b/sources/shiboken6/libshiboken/sbkstaticstrings.h @@ -54,6 +54,8 @@ LIBSHIBOKEN_API PyObject *fget(); LIBSHIBOKEN_API PyObject *fset(); LIBSHIBOKEN_API PyObject *f_code(); LIBSHIBOKEN_API PyObject *f_lineno(); +LIBSHIBOKEN_API PyObject *im_func(); +LIBSHIBOKEN_API PyObject *im_self(); LIBSHIBOKEN_API PyObject *loads(); LIBSHIBOKEN_API PyObject *multi(); LIBSHIBOKEN_API PyObject *name(); |