aboutsummaryrefslogtreecommitdiffstats
path: root/sources
diff options
context:
space:
mode:
authorChristian Tismer <tismer@stackless.com>2021-03-22 17:10:51 +0100
committerChristian Tismer <tismer@stackless.com>2021-03-26 16:27:52 +0100
commit773e1105251a03131cbbdf4abcb0580cb425ff60 (patch)
tree0d9185db4d3087c75476da116ce560804a0e1465 /sources
parentb89b8daeeac7b2b22c3887fa83fe0b986f9baec7 (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.cpp42
-rw-r--r--sources/pyside6/libpyside/globalreceiverv2.cpp28
-rw-r--r--sources/pyside6/libpyside/pysidesignal.cpp111
-rw-r--r--sources/pyside6/libpyside/pysidestaticstrings.cpp3
-rw-r--r--sources/pyside6/libpyside/pysidestaticstrings.h3
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator.cpp12
-rw-r--r--sources/shiboken6/libshiboken/bindingmanager.cpp42
-rw-r--r--sources/shiboken6/libshiboken/sbkstaticstrings.cpp2
-rw-r--r--sources/shiboken6/libshiboken/sbkstaticstrings.h2
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();