diff options
Diffstat (limited to 'sources/pyside6/libpyside/pysidesignal.cpp')
-rw-r--r-- | sources/pyside6/libpyside/pysidesignal.cpp | 1175 |
1 files changed, 693 insertions, 482 deletions
diff --git a/sources/pyside6/libpyside/pysidesignal.cpp b/sources/pyside6/libpyside/pysidesignal.cpp index 29a0baa5f..774837e5b 100644 --- a/sources/pyside6/libpyside/pysidesignal.cpp +++ b/sources/pyside6/libpyside/pysidesignal.cpp @@ -1,61 +1,75 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 #include <sbkpython.h> #include "pysidesignal.h" #include "pysidesignal_p.h" +#include "pysideqobject.h" +#include "pysideutils.h" #include "pysidestaticstrings.h" +#include "pysideweakref.h" #include "signalmanager.h" #include <shiboken.h> +#include <QtCore/QByteArray> +#include <QtCore/QDebug> +#include <QtCore/QHash> #include <QtCore/QObject> #include <QtCore/QMetaMethod> #include <QtCore/QMetaObject> +#include <pep384ext.h> #include <signature.h> #include <algorithm> +#include <optional> #include <utility> #include <cstring> #define QT_SIGNAL_SENTINEL '2' +using namespace Qt::StringLiterals; + +QDebug operator<<(QDebug debug, const PySideSignalData::Signature &s) +{ + QDebugStateSaver saver(debug); + debug.noquote(); + debug.nospace(); + debug << "Signature(\"" << s.signature << '"'; + if (s.attributes) + debug << ", attributes=" << s.attributes; + debug << ')'; + return debug; +} + +QDebug operator<<(QDebug debug, const PySideSignalData &d) +{ + QDebugStateSaver saver(debug); + debug.noquote(); + debug.nospace(); + debug << "PySideSignalData(\"" << d.signalName << "\", " + << d.signatures; + if (!d.signalArguments.isEmpty()) + debug << ", signalArguments=" << d.signalArguments; + debug << ')'; + return debug; +} + +QDebug operator<<(QDebug debug, const PySideSignalInstancePrivate &d) +{ + QDebugStateSaver saver(debug); + debug.noquote(); + debug.nospace(); + debug << "PySideSignalInstancePrivate(\"" << d.signalName + << "\", \"" << d.signature << '"'; + if (d.attributes) + debug << ", attributes=" << d.attributes; + if (d.homonymousMethod) + debug << ", homonymousMethod=" << d.homonymousMethod; + debug << ')'; + return debug; +} + static bool connection_Check(PyObject *o) { if (o == nullptr || o == Py_None) @@ -66,29 +80,35 @@ static bool connection_Check(PyObject *o) return std::strcmp(o->ob_type->tp_name, typeName.constData()) == 0; } -namespace PySide { -namespace Signal { - //aux - class SignalSignature { - public: - SignalSignature() = default; - explicit SignalSignature(QByteArray parameterTypes) : - m_parameterTypes(std::move(parameterTypes)) {} - explicit SignalSignature(QByteArray parameterTypes, QMetaMethod::Attributes attributes) : - m_parameterTypes(std::move(parameterTypes)), - m_attributes(attributes) {} - - QByteArray m_parameterTypes; - QMetaMethod::Attributes m_attributes = QMetaMethod::Compatibility; - }; +static std::optional<QByteArrayList> parseArgumentNames(PyObject *argArguments) +{ + QByteArrayList result; + if (argArguments == nullptr) + return result; + // Prevent a string from being split into a sequence of characters + if (PySequence_Check(argArguments) == 0 || PyUnicode_Check(argArguments) != 0) + return std::nullopt; + const Py_ssize_t argumentSize = PySequence_Size(argArguments); + result.reserve(argumentSize); + for (Py_ssize_t i = 0; i < argumentSize; ++i) { + Shiboken::AutoDecRef item(PySequence_GetItem(argArguments, i)); + if (PyUnicode_Check(item.object()) == 0) + return std::nullopt; + Shiboken::AutoDecRef strObj(PyUnicode_AsUTF8String(item)); + const char *s = PyBytes_AsString(strObj); + if (s == nullptr) + return std::nullopt; + result.append(QByteArray(s)); + } + return result; +} +namespace PySide::Signal { static QByteArray buildSignature(const QByteArray &, const QByteArray &); - static void appendSignature(PySideSignal *, const SignalSignature &); static void instanceInitialize(PySideSignalInstance *, PyObject *, PySideSignal *, PyObject *, int); - static QByteArray parseSignature(PyObject *); + static PySideSignalData::Signature parseSignature(PyObject *); static PyObject *buildQtCompatible(const QByteArray &); -} -} +} // PySide::Signal extern "C" { @@ -98,6 +118,7 @@ static int signalTpInit(PyObject *, PyObject *, PyObject *); static void signalFree(void *); static void signalInstanceFree(void *); static PyObject *signalGetItem(PyObject *self, PyObject *key); +static PyObject *signalGetAttr(PyObject *self, PyObject *name); static PyObject *signalToString(PyObject *self); static PyObject *signalDescrGet(PyObject *self, PyObject *obj, PyObject *type); @@ -113,74 +134,82 @@ static PyObject *signalCall(PyObject *, PyObject *, PyObject *); static PyObject *metaSignalCheck(PyObject *, PyObject *); -static PyMethodDef MetaSignal_methods[] = { +static PyMethodDef MetaSignal_tp_methods[] = { {"__instancecheck__", reinterpret_cast<PyCFunction>(metaSignalCheck), METH_O|METH_STATIC, nullptr}, {nullptr, nullptr, 0, nullptr} }; -static PyType_Slot PySideMetaSignalType_slots[] = { - {Py_tp_methods, reinterpret_cast<void *>(MetaSignal_methods)}, - {Py_tp_base, reinterpret_cast<void *>(&PyType_Type)}, - {Py_tp_free, reinterpret_cast<void *>(PyObject_GC_Del)}, - {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)}, - {0, nullptr} -}; -static PyType_Spec PySideMetaSignalType_spec = { - "2:PySide6.QtCore.MetaSignal", - 0, - // sizeof(PyHeapTypeObject) is filled in by SbkType_FromSpecWithBases - // which calls PyType_Ready which calls inherit_special. - 0, - Py_TPFLAGS_DEFAULT, - PySideMetaSignalType_slots, -}; +static PyTypeObject *createMetaSignalType() +{ + PyType_Slot PySideMetaSignalType_slots[] = { + {Py_tp_methods, reinterpret_cast<void *>(MetaSignal_tp_methods)}, + {Py_tp_base, reinterpret_cast<void *>(&PyType_Type)}, + {Py_tp_free, reinterpret_cast<void *>(PyObject_GC_Del)}, + {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)}, + {0, nullptr} + }; + PyType_Spec PySideMetaSignalType_spec = { + "2:PySide6.QtCore.MetaSignal", + 0, + // sizeof(PyHeapTypeObject) is filled in by SbkType_FromSpec + // which calls PyType_Ready which calls inherit_special. + 0, + Py_TPFLAGS_DEFAULT, + PySideMetaSignalType_slots, + }; -static PyTypeObject *PySideMetaSignalTypeF(void) + return SbkType_FromSpec(&PySideMetaSignalType_spec); +} + +static PyTypeObject *PySideMetaSignal_TypeF(void) { - static PyTypeObject *type = nullptr; - if (!type) { - PyObject *bases = Py_BuildValue("(O)", &PyType_Type); - type = (PyTypeObject *)SbkType_FromSpecWithBases(&PySideMetaSignalType_spec, bases); - Py_XDECREF(bases); - } + static auto *type = createMetaSignalType(); return type; } -static PyType_Slot PySideSignalType_slots[] = { - {Py_mp_subscript, reinterpret_cast<void *>(signalGetItem)}, - {Py_tp_descr_get, reinterpret_cast<void *>(signalDescrGet)}, - {Py_tp_call, reinterpret_cast<void *>(signalCall)}, - {Py_tp_str, reinterpret_cast<void *>(signalToString)}, - {Py_tp_init, reinterpret_cast<void *>(signalTpInit)}, - {Py_tp_new, reinterpret_cast<void *>(PyType_GenericNew)}, - {Py_tp_free, reinterpret_cast<void *>(signalFree)}, - {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)}, - {0, nullptr} -}; -static PyType_Spec PySideSignalType_spec = { - "2:PySide6.QtCore.Signal", - sizeof(PySideSignal), - 0, - Py_TPFLAGS_DEFAULT, - PySideSignalType_slots, -}; +static PyTypeObject *createSignalType() +{ + PyType_Slot PySideSignalType_slots[] = { + {Py_mp_subscript, reinterpret_cast<void *>(signalGetItem)}, + {Py_tp_getattro, reinterpret_cast<void *>(signalGetAttr)}, + {Py_tp_descr_get, reinterpret_cast<void *>(signalDescrGet)}, + {Py_tp_call, reinterpret_cast<void *>(signalCall)}, + {Py_tp_str, reinterpret_cast<void *>(signalToString)}, + {Py_tp_init, reinterpret_cast<void *>(signalTpInit)}, + {Py_tp_new, reinterpret_cast<void *>(PyType_GenericNew)}, + {Py_tp_free, reinterpret_cast<void *>(signalFree)}, + {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)}, + {0, nullptr} + }; + PyType_Spec PySideSignalType_spec = { + "2:PySide6.QtCore.Signal", + sizeof(PySideSignal), + 0, + Py_TPFLAGS_DEFAULT, + PySideSignalType_slots, + }; + + return SbkType_FromSpecWithMeta(&PySideSignalType_spec, PySideMetaSignal_TypeF()); +} -PyTypeObject *PySideSignalTypeF(void) +PyTypeObject *PySideSignal_TypeF(void) { - static PyTypeObject *type = nullptr; - if (!type) { - type = reinterpret_cast<PyTypeObject *>(SbkType_FromSpec(&PySideSignalType_spec)); - PyTypeObject *hold = Py_TYPE(type); - Py_TYPE(type) = PySideMetaSignalTypeF(); - Py_INCREF(Py_TYPE(type)); - Py_DECREF(hold); - } + static auto *type = createSignalType(); return type; } +static PyObject *signalInstanceRepr(PyObject *obSelf) +{ + auto *self = reinterpret_cast<PySideSignalInstance *>(obSelf); + auto *typeName = Py_TYPE(obSelf)->tp_name; + return Shiboken::String::fromFormat("<%s %s at %p>", typeName, + self->d ? self->d->signature.constData() + : "(no signature)", obSelf); +} + static PyMethodDef SignalInstance_methods[] = { {"connect", reinterpret_cast<PyCFunction>(signalInstanceConnect), METH_VARARGS|METH_KEYWORDS, nullptr}, @@ -189,211 +218,259 @@ static PyMethodDef SignalInstance_methods[] = { {nullptr, nullptr, 0, nullptr} /* Sentinel */ }; -static PyType_Slot PySideSignalInstanceType_slots[] = { - {Py_mp_subscript, reinterpret_cast<void *>(signalInstanceGetItem)}, - {Py_tp_call, reinterpret_cast<void *>(signalInstanceCall)}, - {Py_tp_methods, reinterpret_cast<void *>(SignalInstance_methods)}, - {Py_tp_new, reinterpret_cast<void *>(PyType_GenericNew)}, - {Py_tp_free, reinterpret_cast<void *>(signalInstanceFree)}, - {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)}, - {0, nullptr} -}; -static PyType_Spec PySideSignalInstanceType_spec = { - "2:PySide6.QtCore.SignalInstance", - sizeof(PySideSignalInstance), - 0, - Py_TPFLAGS_DEFAULT, - PySideSignalInstanceType_slots, -}; +static PyTypeObject *createSignalInstanceType() +{ + PyType_Slot PySideSignalInstanceType_slots[] = { + {Py_mp_subscript, reinterpret_cast<void *>(signalInstanceGetItem)}, + {Py_tp_call, reinterpret_cast<void *>(signalInstanceCall)}, + {Py_tp_methods, reinterpret_cast<void *>(SignalInstance_methods)}, + {Py_tp_repr, reinterpret_cast<void *>(signalInstanceRepr)}, + {Py_tp_new, reinterpret_cast<void *>(PyType_GenericNew)}, + {Py_tp_free, reinterpret_cast<void *>(signalInstanceFree)}, + {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)}, + {0, nullptr} + }; + PyType_Spec PySideSignalInstanceType_spec = { + "2:PySide6.QtCore.SignalInstance", + sizeof(PySideSignalInstance), + 0, + Py_TPFLAGS_DEFAULT, + PySideSignalInstanceType_slots, + }; -PyTypeObject *PySideSignalInstanceTypeF(void) + return SbkType_FromSpec(&PySideSignalInstanceType_spec); +} + +PyTypeObject *PySideSignalInstance_TypeF(void) { - static PyTypeObject *type = - reinterpret_cast<PyTypeObject *>(SbkType_FromSpec(&PySideSignalInstanceType_spec)); + static auto *type = createSignalInstanceType(); return type; } -static int signalTpInit(PyObject *self, PyObject *args, PyObject *kwds) +static int signalTpInit(PyObject *obSelf, PyObject *args, PyObject *kwds) { - static PyObject *emptyTuple = nullptr; + static PyObject * const emptyTuple = PyTuple_New(0); static const char *kwlist[] = {"name", "arguments", nullptr}; char *argName = nullptr; PyObject *argArguments = nullptr; - if (emptyTuple == nullptr) - emptyTuple = PyTuple_New(0); - if (!PyArg_ParseTupleAndKeywords(emptyTuple, kwds, - "|sO:QtCore.Signal", const_cast<char **>(kwlist), &argName, &argArguments)) + "|sO:QtCore.Signal{name, arguments}", + const_cast<char **>(kwlist), &argName, &argArguments)) return -1; bool tupledArgs = false; - PySideSignal *data = reinterpret_cast<PySideSignal *>(self); - if (!data->data) - data->data = new PySideSignalData; + PySideSignal *self = reinterpret_cast<PySideSignal *>(obSelf); + if (!self->data) + self->data = new PySideSignalData; if (argName) - data->data->signalName = argName; - - data->data->signalArguments = new QByteArrayList(); - if (argArguments && PySequence_Check(argArguments)) { - Py_ssize_t argument_size = PySequence_Size(argArguments); - for (Py_ssize_t i = 0; i < argument_size; ++i) { - PyObject *item = PySequence_GetItem(argArguments, i); - PyObject *strObj = PyUnicode_AsUTF8String(item); - char *s = PyBytes_AsString(strObj); - Py_DECREF(strObj); - Py_DECREF(item); - if (s != nullptr) - data->data->signalArguments->append(QByteArray(s)); - } + self->data->signalName = argName; + + auto argumentNamesOpt = parseArgumentNames(argArguments); + if (!argumentNamesOpt.has_value()) { + PyErr_SetString(PyExc_TypeError, "'arguments' must be a sequence of strings."); + return -1; } + self->data->signalArguments = argumentNamesOpt.value(); for (Py_ssize_t i = 0, i_max = PyTuple_Size(args); i < i_max; i++) { PyObject *arg = PyTuple_GET_ITEM(args, i); if (PySequence_Check(arg) && !Shiboken::String::check(arg) && !PyEnumMeta_Check(arg)) { tupledArgs = true; - const auto sig = PySide::Signal::parseSignature(arg); - PySide::Signal::appendSignature( - data, - PySide::Signal::SignalSignature(sig)); + self->data->signatures.append(PySide::Signal::parseSignature(arg)); } } - if (!tupledArgs) { - const auto sig = PySide::Signal::parseSignature(args); - PySide::Signal::appendSignature( - data, - PySide::Signal::SignalSignature(sig)); - } + if (!tupledArgs) + self->data->signatures.append(PySide::Signal::parseSignature(args)); return 0; } -static void signalFree(void *self) +static void signalFree(void *vself) { - auto pySelf = reinterpret_cast<PyObject *>(self); - auto data = reinterpret_cast<PySideSignal *>(self); - delete data->data; - data->data = nullptr; - Py_XDECREF(data->homonymousMethod); - data->homonymousMethod = nullptr; - - Py_TYPE(pySelf)->tp_base->tp_free(self); + auto pySelf = reinterpret_cast<PyObject *>(vself); + auto self = reinterpret_cast<PySideSignal *>(vself); + if (self->data) { + delete self->data; + self->data = nullptr; + } + Py_XDECREF(self->homonymousMethod); + self->homonymousMethod = nullptr; + + PepExt_TypeCallFree(Py_TYPE(pySelf)->tp_base, self); } -static PyObject *signalGetItem(PyObject *self, PyObject *key) +static PyObject *signalGetItem(PyObject *obSelf, PyObject *key) { - auto data = reinterpret_cast<PySideSignal *>(self); + auto self = reinterpret_cast<PySideSignal *>(obSelf); QByteArray sigKey; if (key) { - sigKey = PySide::Signal::parseSignature(key); + sigKey = PySide::Signal::parseSignature(key).signature; } else { - sigKey = data->data == nullptr || data->data->signatures.isEmpty() - ? PySide::Signal::voidType() : data->data->signatures.constFirst().signature; + sigKey = self->data == nullptr || self->data->signatures.isEmpty() + ? PySide::Signal::voidType() : self->data->signatures.constFirst().signature; } - auto sig = PySide::Signal::buildSignature(data->data->signalName, sigKey); + auto sig = PySide::Signal::buildSignature(self->data->signalName, sigKey); return Shiboken::String::fromCString(sig.constData()); } - -static PyObject *signalToString(PyObject *self) +static PyObject *signalToString(PyObject *obSelf) { - return signalGetItem(self, nullptr); + auto self = reinterpret_cast<PySideSignal *>(obSelf); + QByteArray result; + if (self->data == nullptr || self->data->signatures.isEmpty()) { + result = "<invalid>"_ba; + } else { + for (const auto &signature : std::as_const(self->data->signatures)) { + if (!result.isEmpty()) + result += "; "_ba; + result += PySide::Signal::buildSignature(self->data->signalName, + signature.signature); + } + } + return Shiboken::String::fromCString(result.constData()); } -static void signalInstanceFree(void *self) +static PyObject *signalGetAttr(PyObject *obSelf, PyObject *name) { - auto pySelf = reinterpret_cast<PyObject *>(self); - auto data = reinterpret_cast<PySideSignalInstance *>(self); + auto self = reinterpret_cast<PySideSignal *>(obSelf); - PySideSignalInstancePrivate *dataPvt = data->d; + if (PyUnicode_CompareWithASCIIString(name, "signatures") != 0) + return PyObject_GenericGetAttr(obSelf, name); - Py_XDECREF(dataPvt->homonymousMethod); + auto nelems = self->data->signatures.count(); + PyObject *tuple = PyTuple_New(nelems); - if (dataPvt->next) { - Py_DECREF(dataPvt->next); - dataPvt->next = nullptr; + for (Py_ssize_t idx = 0; idx < nelems; ++idx) { + QByteArray sigKey = self->data->signatures.at(idx).signature; + auto sig = PySide::Signal::buildSignature(self->data->signalName, sigKey); + PyObject *entry = Shiboken::String::fromCString(sig.constData()); + PyTuple_SetItem(tuple, idx, entry); } - delete dataPvt; - data->d = nullptr; - Py_TYPE(pySelf)->tp_base->tp_free(self); + return tuple; +} + +static void signalInstanceFree(void *vself) +{ + auto pySelf = reinterpret_cast<PyObject *>(vself); + auto self = reinterpret_cast<PySideSignalInstance *>(vself); + + PySideSignalInstancePrivate *dataPvt = self->d; + if (dataPvt) { + Py_XDECREF(dataPvt->homonymousMethod); + + if (dataPvt->next) { + Py_DECREF(dataPvt->next); + dataPvt->next = nullptr; + } + delete dataPvt; + self->d = nullptr; + } + self->deleted = true; + PepExt_TypeCallFree(Py_TYPE(pySelf)->tp_base, 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) + +struct FunctionArgumentsResult { - isMethod = PyMethod_Check(slot); - bool isFunction = PyFunction_Check(slot); + PyObject *function = nullptr; + PepCodeObject *objCode = nullptr; + PyObject *functionName = nullptr; + bool isMethod = false; +}; - function = nullptr; - objCode = nullptr; +static FunctionArgumentsResult extractFunctionArgumentsFromSlot(PyObject *slot) +{ + FunctionArgumentsResult ret; + ret.isMethod = PyMethod_Check(slot); + const bool isFunction = PyFunction_Check(slot); - if (isMethod || isFunction) { - function = isMethod ? PyMethod_GET_FUNCTION(slot) : slot; - objCode = reinterpret_cast<PepCodeObject *>(PyFunction_GET_CODE(function)); + if (ret.isMethod || isFunction) { + ret.function = ret.isMethod ? PyMethod_GET_FUNCTION(slot) : slot; + ret.objCode = reinterpret_cast<PepCodeObject *>(PyFunction_GET_CODE(ret.function)); + ret.functionName = PepFunction_GetName(ret.function); - if (functionName != nullptr) { - *functionName = Shiboken::String::toCString(PepFunction_GetName(function)); - } - } else if (PyObject_HasAttr(slot, PySide::PyName::im_func())) { + } else if (PySide::isCompiledMethod(slot)) { // 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()); + ret.isMethod = true; + ret.function = PyObject_GetAttr(slot, PySide::PySideName::im_func()); // Not retaining a reference inline with what PyMethod_GET_FUNCTION does. - Py_DECREF(function); + Py_DECREF(ret.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); - } + ret.functionName = PyObject_GetAttr(ret.function, PySide::PySideMagicName::name()); + // Not retaining a reference inline with what PepFunction_GetName does. + Py_DECREF(ret.functionName); - objCode = reinterpret_cast<PepCodeObject *>( - PyObject_GetAttr(function, PySide::PyMagicName::code())); + ret.objCode = reinterpret_cast<PepCodeObject *>( + PyObject_GetAttr(ret.function, PySide::PySideMagicName::code())); // Not retaining a reference inline with what PyFunction_GET_CODE does. - Py_XDECREF(objCode); + Py_XDECREF(ret.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; - } + // 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. + if (ret.objCode == nullptr) + ret.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); - } + ret.isMethod = false; + ret.function = slot; - objCode = reinterpret_cast<PepCodeObject *>( - PyObject_GetAttr(function, PySide::PyMagicName::code())); + ret.functionName = PyObject_GetAttr(ret.function, PySide::PySideMagicName::name()); + // Not retaining a reference inline with what PepFunction_GetName does. + Py_DECREF(ret.functionName); + + ret.objCode = reinterpret_cast<PepCodeObject *>( + PyObject_GetAttr(ret.function, PySide::PySideMagicName::code())); // Not retaining a reference inline with what PyFunction_GET_CODE does. - Py_XDECREF(objCode); + Py_XDECREF(ret.objCode); + + // 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. + if (ret.objCode == nullptr) + ret.function = nullptr; + } + // any other callback + return ret; +} + +struct ArgCount +{ + int min; + int max; +}; - 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; +// Return a pair of minimum / arg count "foo(p1, p2=0)" -> {1, 2} +ArgCount argCount(const FunctionArgumentsResult &args) +{ + Q_ASSERT(args.objCode); + ArgCount result{-1, -1}; + if ((PepCode_GET_FLAGS(args.objCode) & CO_VARARGS) == 0) { + result.min = result.max = PepCode_GET_ARGCOUNT(args.objCode); + if (args.function != nullptr) { + if (auto *defaultArgs = PepFunction_GetDefaults(args.function)) + result.min -= PyTuple_Size(defaultArgs); } } + return result; +} + +// Find Signal Instance for argument count. +static PySideSignalInstance *findSignalInstance(PySideSignalInstance *source, int argCount) +{ + for (auto *si = source; si != nullptr; si = si->d->next) { + if (si->d->argCount == argCount) + return si; + } + return nullptr; } static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject *kwds) @@ -407,10 +484,19 @@ static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject return nullptr; PySideSignalInstance *source = reinterpret_cast<PySideSignalInstance *>(self); + if (!source->d) { + PyErr_Format(PyExc_RuntimeError, "cannot connect uninitialized SignalInstance"); + return nullptr; + } + if (source->deleted) { + PyErr_Format(PyExc_RuntimeError, "Signal source has been deleted"); + return nullptr; + } + Shiboken::AutoDecRef pyArgs(PyList_New(0)); bool match = false; - if (Py_TYPE(slot) == PySideSignalInstanceTypeF()) { + if (Py_TYPE(slot) == PySideSignalInstance_TypeF()) { PySideSignalInstance *sourceWalk = source; //find best match @@ -435,54 +521,32 @@ static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject } } else { // Check signature of the slot (method or function) to match signal - int slotArgs = -1; - bool matchedSlot = false; - - PySideSignalInstance *it = source; - - 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; + const auto args = extractFunctionArgumentsFromSlot(slot); + PySideSignalInstance *matchedSlot = nullptr; + + if (args.function != nullptr) { + auto slotArgRange = argCount(args); + if (args.isMethod) { + slotArgRange.min -= 1; + slotArgRange.max -= 1; + } // Get signature args - bool isShortCircuit = false; - int signatureArgs = 0; - QStringList argsSignature; - - argsSignature = PySide::Signal::getArgsFromSignature(it->d->signature, - &isShortCircuit); - signatureArgs = argsSignature.length(); - // Iterate the possible types of connection for this signal and compare // it with slot arguments - if (signatureArgs != slotArgs) { - while (it->d->next != nullptr) { - it = it->d->next; - argsSignature = PySide::Signal::getArgsFromSignature(it->d->signature, - &isShortCircuit); - signatureArgs = argsSignature.length(); - if (signatureArgs == slotArgs) { - matchedSlot = true; - break; - } - } + for (int slotArgs = slotArgRange.max; + slotArgs >= slotArgRange.min && matchedSlot == nullptr; --slotArgs) { + matchedSlot = findSignalInstance(source, slotArgs); } } // Adding references to pyArgs PyList_Append(pyArgs, source->d->source); - if (matchedSlot) { + if (matchedSlot != nullptr) { // If a slot matching the same number of arguments was found, // include signature to the pyArgs - Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(it->d->signature)); + Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(matchedSlot->d->signature)); PyList_Append(pyArgs, signature); } else { // Try the first by default if the slot was not found @@ -499,7 +563,7 @@ static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject if (match) { Shiboken::AutoDecRef tupleArgs(PyList_AsTuple(pyArgs)); Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->source, - PySide::PyName::qtConnect())); + PySide::PySideName::qtConnect())); if (pyMethod.isNull()) { // PYSIDE-79: check if pyMethod exists. PyErr_SetString(PyExc_RuntimeError, "method 'connect' vanished!"); return nullptr; @@ -523,6 +587,17 @@ static int argCountInSignature(const char *signature) static PyObject *signalInstanceEmit(PyObject *self, PyObject *args) { PySideSignalInstance *source = reinterpret_cast<PySideSignalInstance *>(self); + if (!source->d) { + PyErr_Format(PyExc_RuntimeError, "cannot emit uninitialized SignalInstance"); + return nullptr; + } + + // PYSIDE-2201: Check if the object has vanished meanwhile. + // Tried to revive it without exception, but this gives problems. + if (source->deleted) { + PyErr_Format(PyExc_RuntimeError, "The SignalInstance object was already deleted"); + return nullptr; + } Shiboken::AutoDecRef pyArgs(PyList_New(0)); int numArgsGiven = PySequence_Fast_GET_SIZE(args); @@ -553,35 +628,57 @@ static PyObject *signalInstanceEmit(PyObject *self, PyObject *args) PyList_Append(pyArgs, PyTuple_GetItem(args, i)); Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->source, - PySide::PyName::qtEmit())); + PySide::PySideName::qtEmit())); Shiboken::AutoDecRef tupleArgs(PyList_AsTuple(pyArgs)); - return PyObject_CallObject(pyMethod, tupleArgs); + return PyObject_CallObject(pyMethod.object(), tupleArgs); } static PyObject *signalInstanceGetItem(PyObject *self, PyObject *key) { - auto data = reinterpret_cast<PySideSignalInstance *>(self); - const auto sigName = data->d->signalName; - const auto sigKey = PySide::Signal::parseSignature(key); + auto *firstSignal = reinterpret_cast<PySideSignalInstance *>(self); + const auto &sigName = firstSignal->d->signalName; + const auto sigKey = PySide::Signal::parseSignature(key).signature; const auto sig = PySide::Signal::buildSignature(sigName, sigKey); - while (data) { + for (auto *data = firstSignal; data != nullptr; data = data->d->next) { if (data->d->signature == sig) { PyObject *result = reinterpret_cast<PyObject *>(data); Py_INCREF(result); return result; } - data = data->d->next; } - PyErr_Format(PyExc_IndexError, "Signature %s not found for signal: %s", - sig.constData(), sigName.constData()); + // Build error message with candidates + QByteArray message = "Signature \"" + sig + "\" not found for signal: \"" + + sigName + "\". Available candidates: "; + for (auto *data = firstSignal; data != nullptr; data = data->d->next) { + if (data != firstSignal) + message += ", "; + message += '"' + data->d->signature + '"'; + } + + PyErr_SetString(PyExc_IndexError, message.constData()); return nullptr; } +static inline void warnDisconnectFailed(PyObject *aSlot, const QByteArray &signature) +{ + if (PyErr_Occurred() != nullptr) { // avoid "%S" invoking str() when an error is set. + PyErr_WarnFormat(PyExc_RuntimeWarning, 0, "Failed to disconnect (%s) from signal \"%s\".", + Py_TYPE(aSlot)->tp_name, signature.constData()); + } else { + PyErr_WarnFormat(PyExc_RuntimeWarning, 0, "Failed to disconnect (%S) from signal \"%s\".", + aSlot, signature.constData()); + } +} + static PyObject *signalInstanceDisconnect(PyObject *self, PyObject *args) { auto source = reinterpret_cast<PySideSignalInstance *>(self); + if (!source->d) { + PyErr_Format(PyExc_RuntimeError, "cannot disconnect uninitialized SignalInstance"); + return nullptr; + } Shiboken::AutoDecRef pyArgs(PyList_New(0)); PyObject *slot = Py_None; @@ -589,7 +686,7 @@ static PyObject *signalInstanceDisconnect(PyObject *self, PyObject *args) slot = PyTuple_GET_ITEM(args, 0); bool match = false; - if (Py_TYPE(slot) == PySideSignalInstanceTypeF()) { + if (Py_TYPE(slot) == PySideSignalInstance_TypeF()) { PySideSignalInstance *target = reinterpret_cast<PySideSignalInstance *>(slot); if (QMetaObject::checkConnectArgs(source->d->signature, target->d->signature)) { PyList_Append(pyArgs, source->d->source); @@ -620,16 +717,16 @@ static PyObject *signalInstanceDisconnect(PyObject *self, PyObject *args) if (match) { Shiboken::AutoDecRef tupleArgs(PyList_AsTuple(pyArgs)); Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->source, - PySide::PyName::qtDisconnect())); + PySide::PySideName::qtDisconnect())); PyObject *result = PyObject_CallObject(pyMethod, tupleArgs); - if (!result || result == Py_True) - return result; - Py_DECREF(result); + if (result != Py_True) + warnDisconnectFailed(slot, source->d->signature); + return result; } - PyErr_Format(PyExc_RuntimeError, "Failed to disconnect signal %s.", - source->d->signature.constData()); - return nullptr; + warnDisconnectFailed(slot, source->d->signature); + Py_INCREF(Py_False); + return Py_False; } // PYSIDE-68: Supply the missing __get__ function @@ -637,12 +734,23 @@ static PyObject *signalDescrGet(PyObject *self, PyObject *obj, PyObject * /*type { auto signal = reinterpret_cast<PySideSignal *>(self); // Return the unbound signal if there is nothing to bind it to. - if (obj == nullptr || obj == Py_None) { + if (obj == nullptr || obj == Py_None + || !PySide::isQObjectDerived(Py_TYPE(obj), true)) { Py_INCREF(self); return self; } + + // PYSIDE-68-bis: It is important to respect the already cached instance. Shiboken::AutoDecRef name(Py_BuildValue("s", signal->data->signalName.data())); - return reinterpret_cast<PyObject *>(PySide::Signal::initialize(signal, name, obj)); + auto *dict = SbkObject_GetDict_NoRef(obj); + auto *inst = PyDict_GetItem(dict, name); + if (inst) { + Py_INCREF(inst); + return inst; + } + inst = reinterpret_cast<PyObject *>(PySide::Signal::initialize(signal, name, obj)); + PyObject_SetAttr(obj, name, inst); + return inst; } static PyObject *signalCall(PyObject *self, PyObject *args, PyObject *kw) @@ -658,150 +766,211 @@ static PyObject *signalCall(PyObject *self, PyObject *args, PyObject *kw) return nullptr; } - descrgetfunc getDescriptor = Py_TYPE(signal->homonymousMethod)->tp_descr_get; - // Check if there exists a method with the same name as the signal, which is also a static // method in C++ land. - Shiboken::AutoDecRef homonymousMethod(getDescriptor(signal->homonymousMethod, - nullptr, nullptr)); - if (PyCFunction_Check(homonymousMethod) - && (PyCFunction_GET_FLAGS(homonymousMethod.object()) & METH_STATIC)) { -#if PY_VERSION_HEX >= 0x03090000 + Shiboken::AutoDecRef homonymousMethod(PepExt_Type_CallDescrGet(signal->homonymousMethod, + nullptr, nullptr)); + if (PyCFunction_Check(homonymousMethod.object()) + && (PyCFunction_GET_FLAGS(homonymousMethod.object()) & METH_STATIC)) return PyObject_Call(homonymousMethod, args, kw); -#else - return PyCFunction_Call(homonymousMethod, args, kw); -#endif - } // Assumes homonymousMethod is not a static method. - ternaryfunc callFunc = Py_TYPE(signal->homonymousMethod)->tp_call; + ternaryfunc callFunc = PepExt_Type_GetCallSlot(Py_TYPE(signal->homonymousMethod)); return callFunc(homonymousMethod, args, kw); } +// This function returns a borrowed reference. +static inline PyObject *_getRealCallable(PyObject *func) +{ + static const auto *SignalType = PySideSignal_TypeF(); + static const auto *SignalInstanceType = PySideSignalInstance_TypeF(); + + // If it is a signal, use the (maybe empty) homonymous method. + if (Py_TYPE(func) == SignalType) { + auto *signal = reinterpret_cast<PySideSignal *>(func); + return signal->homonymousMethod; + } + // If it is a signal instance, use the (maybe empty) homonymous method. + if (Py_TYPE(func) == SignalInstanceType) { + auto *signalInstance = reinterpret_cast<PySideSignalInstance *>(func); + return signalInstance->d->homonymousMethod; + } + return func; +} + +// This function returns a borrowed reference. +static PyObject *_getHomonymousMethod(PySideSignalInstance *inst) +{ + if (inst->d->homonymousMethod) + return inst->d->homonymousMethod; + + // PYSIDE-1730: We are searching methods with the same name not only at the same place, + // but walk through the whole mro to find a hidden method with the same name. + auto signalName = inst->d->signalName; + Shiboken::AutoDecRef name(Shiboken::String::fromCString(signalName)); + auto *mro = Py_TYPE(inst->d->source)->tp_mro; + const Py_ssize_t n = PyTuple_GET_SIZE(mro); + + for (Py_ssize_t idx = 0; idx < n; idx++) { + auto *sub_type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx)); + Shiboken::AutoDecRef tpDict(PepType_GetDict(sub_type)); + auto *hom = PyDict_GetItem(tpDict, name); + PyObject *realFunc{}; + if (hom && PyCallable_Check(hom) && (realFunc = _getRealCallable(hom))) + return realFunc; + } + return nullptr; +} + static PyObject *signalInstanceCall(PyObject *self, PyObject *args, PyObject *kw) { - auto PySideSignal = reinterpret_cast<PySideSignalInstance *>(self); - if (!PySideSignal->d->homonymousMethod) { - PyErr_SetString(PyExc_TypeError, "native Qt signal is not callable"); - return 0; + auto *PySideSignal = reinterpret_cast<PySideSignalInstance *>(self); + auto *hom = _getHomonymousMethod(PySideSignal); + if (!hom) { + PyErr_Format(PyExc_TypeError, "native Qt signal instance '%s' is not callable", + PySideSignal->d->signalName.constData()); + return nullptr; } - descrgetfunc getDescriptor = Py_TYPE(PySideSignal->d->homonymousMethod)->tp_descr_get; - Shiboken::AutoDecRef homonymousMethod(getDescriptor(PySideSignal->d->homonymousMethod, - PySideSignal->d->source, nullptr)); -#if PY_VERSION_HEX >= 0x03090000 - return PyObject_Call(homonymousMethod, args, kw); -#else - return PyCFunction_Call(homonymousMethod, args, kw); -#endif + Shiboken::AutoDecRef homonymousMethod(PepExt_Type_CallDescrGet(hom, PySideSignal->d->source, + nullptr)); + return PyObject_Call(homonymousMethod, args, kw); } static PyObject *metaSignalCheck(PyObject * /* klass */, PyObject *arg) { - if (PyType_IsSubtype(Py_TYPE(arg), PySideSignalInstanceTypeF())) + if (PyType_IsSubtype(Py_TYPE(arg), PySideSignalInstance_TypeF())) Py_RETURN_TRUE; Py_RETURN_FALSE; } } // extern "C" -namespace PySide { -namespace Signal { +namespace PySide::Signal { static const char *MetaSignal_SignatureStrings[] = { "PySide6.QtCore.MetaSignal.__instancecheck__(self,object:object)->bool", nullptr}; // Sentinel static const char *Signal_SignatureStrings[] = { - "PySide6.QtCore.Signal(self,*types:type,name:str=nullptr,arguments:str=nullptr)", + "PySide6.QtCore.Signal(self,*types:type,name:str=nullptr,arguments:typing.List[str]=nullptr)", + "1:PySide6.QtCore.Signal.__get__(self,instance:None,owner:Optional[typing.Any])->" + "PySide6.QtCore.Signal", + "0:PySide6.QtCore.Signal.__get__(self,instance:PySide6.QtCore.QObject," + "owner:Optional[typing.Any])->PySide6.QtCore.SignalInstance", nullptr}; // Sentinel static const char *SignalInstance_SignatureStrings[] = { - "PySide6.QtCore.SignalInstance.connect(self,slot:object,type:type=nullptr)", - "PySide6.QtCore.SignalInstance.disconnect(self,slot:object=nullptr)", + "PySide6.QtCore.SignalInstance.connect(self,slot:object," + "type:PySide6.QtCore.Qt.ConnectionType=PySide6.QtCore.Qt.ConnectionType.AutoConnection)" + "->PySide6.QtCore.QMetaObject.Connection", + "PySide6.QtCore.SignalInstance.disconnect(self,slot:object=nullptr)->bool", "PySide6.QtCore.SignalInstance.emit(self,*args:typing.Any)", nullptr}; // Sentinel void init(PyObject *module) { - if (InitSignatureStrings(PySideMetaSignalTypeF(), MetaSignal_SignatureStrings) < 0) + if (InitSignatureStrings(PySideMetaSignal_TypeF(), MetaSignal_SignatureStrings) < 0) return; - Py_INCREF(PySideMetaSignalTypeF()); - PyModule_AddObject(module, "MetaSignal", reinterpret_cast<PyObject *>(PySideMetaSignalTypeF())); + Py_INCREF(PySideMetaSignal_TypeF()); + auto *obMetaSignal_Type = reinterpret_cast<PyObject *>(PySideMetaSignal_TypeF()); + PyModule_AddObject(module, "MetaSignal", obMetaSignal_Type); - if (InitSignatureStrings(PySideSignalTypeF(), Signal_SignatureStrings) < 0) + if (InitSignatureStrings(PySideSignal_TypeF(), Signal_SignatureStrings) < 0) return; - Py_INCREF(PySideSignalTypeF()); - PyModule_AddObject(module, "Signal", reinterpret_cast<PyObject *>(PySideSignalTypeF())); + Py_INCREF(PySideSignal_TypeF()); + auto *obSignal_Type = reinterpret_cast<PyObject *>(PySideSignal_TypeF()); + PyModule_AddObject(module, "Signal", obSignal_Type); - if (InitSignatureStrings(PySideSignalInstanceTypeF(), SignalInstance_SignatureStrings) < 0) + if (InitSignatureStrings(PySideSignalInstance_TypeF(), SignalInstance_SignatureStrings) < 0) return; - Py_INCREF(PySideSignalInstanceTypeF()); - PyModule_AddObject(module, "SignalInstance", reinterpret_cast<PyObject *>(PySideSignalInstanceTypeF())); + Py_INCREF(PySideSignalInstance_TypeF()); + auto *obSignalInstance_Type = reinterpret_cast<PyObject *>(PySideSignalInstance_TypeF()); + PyModule_AddObject(module, "SignalInstance", obSignalInstance_Type); } bool checkType(PyObject *pyObj) { if (pyObj) - return PyType_IsSubtype(Py_TYPE(pyObj), PySideSignalTypeF()); + return PyType_IsSubtype(Py_TYPE(pyObj), PySideSignal_TypeF()); return false; } bool checkInstanceType(PyObject *pyObj) { return pyObj != nullptr - && PyType_IsSubtype(Py_TYPE(pyObj), PySideSignalInstanceTypeF()) != 0; + && PyType_IsSubtype(Py_TYPE(pyObj), PySideSignalInstance_TypeF()) != 0; } void updateSourceObject(PyObject *source) { - PyTypeObject *objType = reinterpret_cast<PyTypeObject *>(PyObject_Type(source)); - - Py_ssize_t pos = 0; - PyObject *value; - PyObject *key; - - while (PyDict_Next(objType->tp_dict, &pos, &key, &value)) { - if (PyObject_TypeCheck(value, PySideSignalTypeF())) { - Shiboken::AutoDecRef signalInstance(reinterpret_cast<PyObject *>(PyObject_New(PySideSignalInstance, PySideSignalInstanceTypeF()))); - instanceInitialize(signalInstance.cast<PySideSignalInstance *>(), key, reinterpret_cast<PySideSignal *>(value), source, 0); - PyObject_SetAttr(source, key, signalInstance); + // TODO: Provide for actual upstream exception handling. + // For now we'll just return early to avoid further issues. + + if (source == nullptr) // Bad input + return; + + Shiboken::AutoDecRef mroIterator(PyObject_GetIter(source->ob_type->tp_mro)); + + if (mroIterator.isNull()) // Not iterable + return; + + Shiboken::AutoDecRef mroItem{}; + auto *dict = SbkObject_GetDict_NoRef(source); + + // PYSIDE-1431: Walk the mro and update. But see PYSIDE-1751 below. + while ((mroItem.reset(PyIter_Next(mroIterator))), mroItem.object()) { + Py_ssize_t pos = 0; + PyObject *key, *value; + auto *type = reinterpret_cast<PyTypeObject *>(mroItem.object()); + Shiboken::AutoDecRef tpDict(PepType_GetDict(type)); + while (PyDict_Next(tpDict, &pos, &key, &value)) { + if (PyObject_TypeCheck(value, PySideSignal_TypeF())) { + // PYSIDE-1751: We only insert an instance into the instance dict, if a signal + // of the same name is in the mro. This is the equivalent action + // as PyObject_SetAttr, but filtered by existing signal names. + if (!PyDict_GetItem(dict, key)) { + auto *inst = PyObject_New(PySideSignalInstance, PySideSignalInstance_TypeF()); + Shiboken::AutoDecRef signalInstance(reinterpret_cast<PyObject *>(inst)); + auto *si = reinterpret_cast<PySideSignalInstance *>(signalInstance.object()); + instanceInitialize(si, key, reinterpret_cast<PySideSignal *>(value), + source, 0); + if (PyDict_SetItem(dict, key, signalInstance) == -1) + return; // An error occurred while setting the attribute + } + } } } - Py_XDECREF(objType); + if (PyErr_Occurred()) // An iteration error occurred + return; } -QByteArray getTypeName(PyObject *type) +QByteArray getTypeName(PyObject *obType) { - if (PyType_Check(type)) { - if (PyType_IsSubtype(reinterpret_cast<PyTypeObject *>(type), - reinterpret_cast<PyTypeObject *>(SbkObject_TypeF()))) { - auto objType = reinterpret_cast<SbkObjectType *>(type); - return Shiboken::ObjectType::getOriginalName(objType); - } - // Translate python types to Qt names - auto objType = reinterpret_cast<PyTypeObject *>(type); - if (Shiboken::String::checkType(objType)) + if (PyType_Check(obType)) { + auto *type = reinterpret_cast<PyTypeObject *>(obType); + if (PyType_IsSubtype(type, SbkObject_TypeF())) + return Shiboken::ObjectType::getOriginalName(type); + // Translate Python types to Qt names + if (Shiboken::String::checkType(type)) return QByteArrayLiteral("QString"); - if (objType == &PyInt_Type) + if (type == &PyLong_Type) return QByteArrayLiteral("int"); - if (objType == &PyLong_Type) - return QByteArrayLiteral("long"); - if (objType == &PyFloat_Type) + if (type == &PyFloat_Type) return QByteArrayLiteral("double"); - if (objType == &PyBool_Type) + if (type == &PyBool_Type) return QByteArrayLiteral("bool"); - if (objType == &PyList_Type) + if (type == &PyList_Type) return QByteArrayLiteral("QVariantList"); - if (Py_TYPE(objType) == SbkEnumType_TypeF()) - return Shiboken::Enum::getCppName(objType); + if (type == &PyDict_Type) + return QByteArrayLiteral("QVariantMap"); return QByteArrayLiteral("PyObject"); } - if (type == Py_None) // Must be checked before as Shiboken::String::check accepts Py_None + if (obType == Py_None) // Must be checked before as Shiboken::String::check accepts Py_None return voidType(); - if (Shiboken::String::check(type)) { - QByteArray result = Shiboken::String::toCString(type); + if (Shiboken::String::check(obType)) { + QByteArray result = Shiboken::String::toCString(obType); if (result == "qreal") result = sizeof(qreal) == sizeof(double) ? "double" : "float"; return result; @@ -814,59 +983,79 @@ static QByteArray buildSignature(const QByteArray &name, const QByteArray &signa return QMetaObject::normalizedSignature(name + '(' + signature + ')'); } -static QByteArray parseSignature(PyObject *args) +static PySideSignalData::Signature parseSignature(PyObject *args) { - if (args && (Shiboken::String::check(args) || !PySequence_Check(args))) - return getTypeName(args); + PySideSignalData::Signature result{{}, QMetaMethod::Compatibility, 0}; + if (args && (Shiboken::String::check(args) || !PyTuple_Check(args))) { + result.signature = getTypeName(args); + result.argCount = 1; + return result; + } - QByteArray signature; for (Py_ssize_t i = 0, i_max = PySequence_Size(args); i < i_max; i++) { Shiboken::AutoDecRef arg(PySequence_GetItem(args, i)); const auto typeName = getTypeName(arg); if (!typeName.isEmpty()) { - if (!signature.isEmpty()) - signature += ','; - signature += typeName; + if (!result.signature.isEmpty()) + result.signature += ','; + result.signature += typeName; + ++result.argCount; } } - return signature; + return result; } -static void appendSignature(PySideSignal *self, const SignalSignature &signature) +static void sourceGone(void *data) { - self->data->signatures.append({signature.m_parameterTypes, signature.m_attributes}); + auto *self = reinterpret_cast<PySideSignalInstance *>(data); + self->deleted = true; } -static void instanceInitialize(PySideSignalInstance *self, PyObject *name, PySideSignal *data, PyObject *source, int index) +static void instanceInitialize(PySideSignalInstance *self, PyObject *name, PySideSignal *signal, PyObject *source, int index) { self->d = new PySideSignalInstancePrivate; + self->deleted = false; PySideSignalInstancePrivate *selfPvt = self->d; selfPvt->next = nullptr; - if (data->data->signalName.isEmpty()) - data->data->signalName = Shiboken::String::toCString(name); - selfPvt->signalName = data->data->signalName; + if (signal->data->signalName.isEmpty()) + signal->data->signalName = Shiboken::String::toCString(name); + selfPvt->signalName = signal->data->signalName; selfPvt->source = source; - const auto &signature = data->data->signatures.at(index); + const auto &signature = signal->data->signatures.at(index); selfPvt->signature = buildSignature(self->d->signalName, signature.signature); + selfPvt->argCount = signature.argCount; selfPvt->attributes = signature.attributes; selfPvt->homonymousMethod = nullptr; - if (data->homonymousMethod) { - selfPvt->homonymousMethod = data->homonymousMethod; + if (signal->homonymousMethod) { + selfPvt->homonymousMethod = signal->homonymousMethod; Py_INCREF(selfPvt->homonymousMethod); } + // PYSIDE-2201: We have no reference to source. Let's take a weakref to get + // notified when source gets deleted. + PySide::WeakRef::create(source, sourceGone, self); + index++; - if (index < data->data->signatures.size()) { - selfPvt->next = PyObject_New(PySideSignalInstance, PySideSignalInstanceTypeF()); - instanceInitialize(selfPvt->next, name, data, source, index); + if (index < signal->data->signatures.size()) { + selfPvt->next = PyObject_New(PySideSignalInstance, PySideSignalInstance_TypeF()); + instanceInitialize(selfPvt->next, name, signal, source, index); } } PySideSignalInstance *initialize(PySideSignal *self, PyObject *name, PyObject *object) { + static PyTypeObject *pyQObjectType = Shiboken::Conversions::getPythonTypeObject("QObject*"); + assert(pyQObjectType); + + if (!PyObject_TypeCheck(object, pyQObjectType)) { + PyErr_Format(PyExc_TypeError, "%s cannot be converted to %s", + Py_TYPE(object)->tp_name, pyQObjectType->tp_name); + return nullptr; + } + PySideSignalInstance *instance = PyObject_New(PySideSignalInstance, - PySideSignalInstanceTypeF()); + PySideSignalInstance_TypeF()); instanceInitialize(instance, name, self, object, 0); auto sbkObj = reinterpret_cast<SbkObject *>(object); if (!Shiboken::Object::wasCreatedByPython(sbkObj)) @@ -877,7 +1066,7 @@ PySideSignalInstance *initialize(PySideSignal *self, PyObject *name, PyObject *o bool connect(PyObject *source, const char *signal, PyObject *callback) { Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source, - PySide::PyName::qtConnect())); + PySide::PySideName::qtConnect())); if (pyMethod.isNull()) return false; @@ -897,7 +1086,7 @@ PySideSignalInstance *newObjectFromMethod(PyObject *source, const QList<QMetaMet PySideSignalInstance *root = nullptr; PySideSignalInstance *previous = nullptr; for (const QMetaMethod &m : methodList) { - PySideSignalInstance *item = PyObject_New(PySideSignalInstance, PySideSignalInstanceTypeF()); + PySideSignalInstance *item = PyObject_New(PySideSignalInstance, PySideSignalInstance_TypeF()); if (!root) root = item; @@ -905,14 +1094,15 @@ PySideSignalInstance *newObjectFromMethod(PyObject *source, const QList<QMetaMet previous->d->next = item; item->d = new PySideSignalInstancePrivate; + item->deleted = false; PySideSignalInstancePrivate *selfPvt = item->d; selfPvt->source = source; - Py_INCREF(selfPvt->source); // PYSIDE-79: an INCREF is missing. QByteArray cppName(m.methodSignature()); cppName.truncate(cppName.indexOf('(')); - // separe SignalName + // separate SignalName selfPvt->signalName = cppName; selfPvt->signature = m.methodSignature(); + selfPvt->argCount = int(m.parameterCount()); selfPvt->attributes = m.attributes(); selfPvt->homonymousMethod = nullptr; selfPvt->next = nullptr; @@ -920,29 +1110,10 @@ PySideSignalInstance *newObjectFromMethod(PyObject *source, const QList<QMetaMet return root; } -template<typename T> -static typename T::value_type join(T t, const char *sep) -{ - typename T::value_type res; - if (t.isEmpty()) - return res; - - typename T::const_iterator it = t.begin(); - typename T::const_iterator end = t.end(); - res += *it; - ++it; - - while (it != end) { - res += sep; - res += *it; - ++it; - } - return res; -} - -static void _addSignalToWrapper(SbkObjectType *wrapperType, const char *signalName, PySideSignal *signal) +static void _addSignalToWrapper(PyTypeObject *wrapperType, const char *signalName, PySideSignal *signal) { - auto typeDict = reinterpret_cast<PyTypeObject *>(wrapperType)->tp_dict; + Shiboken::AutoDecRef tpDict(PepType_GetDict(wrapperType)); + auto typeDict = tpDict.object(); PyObject *homonymousMethod; if ((homonymousMethod = PyDict_GetItemString(typeDict, signalName))) { Py_INCREF(homonymousMethod); @@ -952,9 +1123,10 @@ static void _addSignalToWrapper(SbkObjectType *wrapperType, const char *signalNa } // This function is used by qStableSort to promote empty signatures -static bool compareSignals(const SignalSignature &sig1, const SignalSignature &) +static bool compareSignals(const PySideSignalData::Signature &sig1, + const PySideSignalData::Signature &sig2) { - return sig1.m_parameterTypes.isEmpty(); + return sig1.signature.isEmpty() && !sig2.signature.isEmpty(); } static PyObject *buildQtCompatible(const QByteArray &signature) @@ -963,41 +1135,48 @@ static PyObject *buildQtCompatible(const QByteArray &signature) return Shiboken::String::fromStringAndSize(ba, ba.size()); } -void registerSignals(SbkObjectType *pyObj, const QMetaObject *metaObject) +void registerSignals(PyTypeObject *pyObj, const QMetaObject *metaObject) { - using SignalSigMap = QHash<QByteArray, QList<SignalSignature> >; - SignalSigMap signalsFound; + using Signature = PySideSignalData::Signature; + struct MetaSignal + { + QByteArray methodName; + QList<Signature> signatures; + }; + + QList<MetaSignal> signalsFound; for (int i = metaObject->methodOffset(), max = metaObject->methodCount(); i < max; ++i) { QMetaMethod method = metaObject->method(i); if (method.methodType() == QMetaMethod::Signal) { QByteArray methodName(method.methodSignature()); - methodName.chop(methodName.size() - methodName.indexOf('(')); - SignalSignature signature; - signature.m_parameterTypes = join(method.parameterTypes(), ","); + methodName.truncate(methodName.indexOf('(')); + Signature signature{method.parameterTypes().join(','), {}, + short(method.parameterCount())}; if (method.attributes() & QMetaMethod::Cloned) - signature.m_attributes = QMetaMethod::Cloned; - signalsFound[methodName] << signature; + signature.attributes = QMetaMethod::Cloned; + auto it = std::find_if(signalsFound.begin(), signalsFound.end(), + [methodName](const MetaSignal &ms) + { return ms.methodName == methodName; }); + if (it != signalsFound.end()) + it->signatures << signature; + else + signalsFound.append(MetaSignal{methodName, {signature}}); } } - SignalSigMap::Iterator it = signalsFound.begin(); - SignalSigMap::Iterator end = signalsFound.end(); - for (; it != end; ++it) { - PySideSignal *self = PyObject_New(PySideSignal, PySideSignalTypeF()); + for (const auto &metaSignal : std::as_const(signalsFound)) { + PySideSignal *self = PyObject_New(PySideSignal, PySideSignal_TypeF()); self->data = new PySideSignalData; - self->data->signalName = it.key(); + self->data->signalName = metaSignal.methodName; self->homonymousMethod = nullptr; // Empty signatures comes first! So they will be the default signal signature - std::stable_sort(it.value().begin(), it.value().end(), &compareSignals); - const auto endJ = it.value().cend(); - for (auto j = it.value().cbegin(); j != endJ; ++j) { - const SignalSignature &sig = *j; - appendSignature(self, sig); - } + self->data->signatures = metaSignal.signatures; + std::stable_sort(self->data->signatures.begin(), + self->data->signatures.end(), &compareSignals); - _addSignalToWrapper(pyObj, it.key(), self); + _addSignalToWrapper(pyObj, metaSignal.methodName, self); Py_DECREF(reinterpret_cast<PyObject *>(self)); } } @@ -1012,53 +1191,84 @@ const char *getSignature(PySideSignalInstance *signal) return signal->d->signature; } -QStringList getArgsFromSignature(const char *signature, bool *isShortCircuit) +EmitterData getEmitterData(PySideSignalInstance *signal) +{ + EmitterData result; + result.emitter = PySide::convertToQObject(getObject(signal), false); + if (result.emitter != nullptr) { + auto *mo = result.emitter->metaObject(); + result.methodIndex = mo->indexOfMethod(getSignature(signal)); + } + return result; +} + +QByteArrayList getArgsFromSignature(const char *signature, bool *isShortCircuit) { - QString qsignature = QString::fromLatin1(signature).trimmed(); - QStringList result; + QByteArray qsignature = QByteArray(signature).trimmed(); + QByteArrayList result; if (isShortCircuit) - *isShortCircuit = !qsignature.contains(QLatin1Char('(')); - if (qsignature.contains(QLatin1String("()")) || qsignature.contains(QLatin1String("(void)"))) + *isShortCircuit = !qsignature.contains(u'('); + if (qsignature.contains("()") || qsignature.contains("(void)")) return result; - if (qsignature.endsWith(QLatin1Char(')'))) { - const int paren = qsignature.indexOf(QLatin1Char('(')); + if (qsignature.endsWith(')')) { + const auto paren = qsignature.indexOf('('); if (paren >= 0) { qsignature.chop(1); qsignature.remove(0, paren + 1); - result = qsignature.split(QLatin1Char(',')); - for (QString &type : result) + result = qsignature.split(u','); + for (auto &type : result) type = type.trimmed(); } } return result; } -QString getCallbackSignature(const char *signal, QObject *receiver, PyObject *callback, bool encodeName) +QByteArray getCallbackSignature(const char *signal, QObject *receiver, + PyObject *callback, bool encodeName) { QByteArray functionName; - int numArgs = -1; + qsizetype numArgs = -1; - PyObject *function = nullptr; - PepCodeObject *objCode = nullptr; - bool useSelf = false; + const auto slotArgs = extractFunctionArgumentsFromSlot(callback); + qsizetype useSelf = slotArgs.isMethod ? 1 : 0; - extractFunctionArgumentsFromSlot(callback, function, objCode, useSelf, &functionName); + if (slotArgs.function != nullptr) { + numArgs = argCount(slotArgs).max; +#ifdef PYPY_VERSION + } else if (Py_TYPE(callback) == PepBuiltinMethod_TypePtr) { + // PYSIDE-535: PyPy has a special builtin method that acts almost like PyCFunction. + Shiboken::AutoDecRef temp(PyObject_GetAttr(callback, Shiboken::PyMagicName::name())); + functionName = Shiboken::String::toCString(temp); + useSelf = true; - if (function != nullptr) { - numArgs = PepCode_GET_FLAGS(objCode) & CO_VARARGS ? -1 : PepCode_GET_ARGCOUNT(objCode); + if (receiver) { + // Search for signature on metaobject + const QMetaObject *mo = receiver->metaObject(); + QByteArray prefix(functionName); + prefix += '('; + for (int i = 0; i < mo->methodCount(); i++) { + QMetaMethod me = mo->method(i); + if ((strncmp(me.methodSignature(), prefix, prefix.size()) == 0) && + QMetaObject::checkConnectArgs(signal, me.methodSignature())) { + numArgs = me.parameterTypes().size() + useSelf; + break; + } + } + } +#endif } else if (PyCFunction_Check(callback)) { const PyCFunctionObject *funcObj = reinterpret_cast<const PyCFunctionObject *>(callback); functionName = PepCFunction_GET_NAMESTR(funcObj); - useSelf = PyCFunction_GET_SELF(funcObj); + useSelf = PyCFunction_GET_SELF(funcObj) != nullptr ? 1 : 0; const int flags = PyCFunction_GET_FLAGS(funcObj); if (receiver) { - //Search for signature on metaobject + // Search for signature on metaobject const QMetaObject *mo = receiver->metaObject(); QByteArray prefix(functionName); prefix += '('; - for (int i = 0; i < mo->methodCount(); i++) { + for (int i = 0, count = mo->methodCount(); i < count; ++i) { QMetaMethod me = mo->method(i); if ((strncmp(me.methodSignature(), prefix, prefix.size()) == 0) && QMetaObject::checkConnectArgs(signal, me.methodSignature())) { @@ -1075,26 +1285,30 @@ QString getCallbackSignature(const char *signal, QObject *receiver, PyObject *ca numArgs = 0; } } else if (PyCallable_Check(callback)) { - functionName = "__callback" + QByteArray::number((qlonglong)callback); + functionName = "__callback" + QByteArray::number(quintptr(callback)); } + if (functionName.isEmpty() && slotArgs.functionName != nullptr) + functionName = Shiboken::String::toCString(slotArgs.functionName); Q_ASSERT(!functionName.isEmpty()); bool isShortCircuit = false; - const QString functionNameS = QLatin1String(functionName); - QString signature = encodeName ? codeCallbackName(callback, functionNameS) : functionNameS; - QStringList args = getArgsFromSignature(signal, &isShortCircuit); + if (functionName.startsWith('<') && functionName.endsWith('>')) { // fix "<lambda>" + functionName[0] = '_'; + functionName[functionName.size() - 1] = '_'; + } + QByteArray signature = encodeName ? codeCallbackName(callback, functionName) : functionName; + QByteArrayList args = getArgsFromSignature(signal, &isShortCircuit); if (!isShortCircuit) { - signature.append(QLatin1Char('(')); + signature.append(u'('); if (numArgs == -1) - numArgs = std::numeric_limits<int>::max(); - while (args.count() && (args.count() > (numArgs - useSelf))) { + numArgs = std::numeric_limits<qsizetype>::max(); + while (!args.isEmpty() && (args.size() > (numArgs - useSelf))) args.removeLast(); - } - signature.append(args.join(QLatin1Char(','))); - signature.append(QLatin1Char(')')); + signature.append(args.join(',')); + signature.append(')'); } return signature; } @@ -1113,22 +1327,21 @@ bool checkQtSignal(const char *signal) return true; } -QString codeCallbackName(PyObject *callback, const QString &funcName) +QByteArray codeCallbackName(PyObject *callback, const QByteArray &funcName) { if (PyMethod_Check(callback)) { PyObject *self = PyMethod_GET_SELF(callback); PyObject *func = PyMethod_GET_FUNCTION(callback); - return funcName + QString::number(quint64(self), 16) + QString::number(quint64(func), 16); + return funcName + QByteArray::number(quint64(self), 16) + QByteArray::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())) { + if (PySide::isCompiledMethod(callback)) { // 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); + Shiboken::AutoDecRef self(PyObject_GetAttr(callback, PySide::PySideName::im_self())); + Shiboken::AutoDecRef func(PyObject_GetAttr(callback, PySide::PySideName::im_func())); + return funcName + QByteArray::number(quint64(self), 16) + QByteArray::number(quint64(func), 16); } - return funcName + QString::number(quint64(callback), 16); + return funcName + QByteArray::number(quint64(callback), 16); } QByteArray voidType() @@ -1136,6 +1349,4 @@ QByteArray voidType() return QByteArrayLiteral("void"); } -} //namespace Signal -} //namespace PySide - +} //namespace PySide::Signal |