diff options
Diffstat (limited to 'sources/pyside6/libpyside/globalreceiverv2.cpp')
-rw-r--r-- | sources/pyside6/libpyside/globalreceiverv2.cpp | 267 |
1 files changed, 122 insertions, 145 deletions
diff --git a/sources/pyside6/libpyside/globalreceiverv2.cpp b/sources/pyside6/libpyside/globalreceiverv2.cpp index 8ff5d896f..51070b4ad 100644 --- a/sources/pyside6/libpyside/globalreceiverv2.cpp +++ b/sources/pyside6/libpyside/globalreceiverv2.cpp @@ -1,76 +1,31 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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) 2016 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 "globalreceiverv2.h" -#include "dynamicqmetaobject_p.h" #include "pysideweakref.h" +#include "pysidestaticstrings.h" +#include "pysideutils.h" #include "signalmanager.h" #include <autodecref.h> #include <gilstate.h> +#include <pep384ext.h> -#include <QtCore/qhashfunctions.h> #include <QtCore/QMetaMethod> #include <QtCore/QSet> +#include <QtCore/QDebug> + +#include <cstring> #define RECEIVER_DESTROYED_SLOT_NAME "__receiverDestroyed__(QObject*)" -namespace -{ - static int DESTROY_SIGNAL_ID = 0; - static int DESTROY_SLOT_ID = 0; -} namespace PySide { -size_t qHash(const GlobalReceiverKey &k, size_t seed) -{ - QtPrivate::QHashCombine hash; - seed = hash(seed, k.object); - seed = hash(seed, k.method); - return seed; -} - class DynamicSlotDataV2 { - Q_DISABLE_COPY(DynamicSlotDataV2) + Q_DISABLE_COPY_MOVE(DynamicSlotDataV2) public: DynamicSlotDataV2(PyObject *callback, GlobalReceiverV2 *parent); ~DynamicSlotDataV2(); @@ -84,6 +39,8 @@ class DynamicSlotDataV2 static void onCallbackDestroyed(void *data); static GlobalReceiverKey key(PyObject *callback); + void formatDebug(QDebug &debug) const; + private: bool m_isMethod; PyObject *m_callback; @@ -94,26 +51,65 @@ class DynamicSlotDataV2 GlobalReceiverV2 *m_parent; }; +void DynamicSlotDataV2::formatDebug(QDebug &debug) const +{ + debug << "method=" << m_isMethod << ", m_callback=" << m_callback; + if (m_callback != nullptr) + debug << '/' << Py_TYPE(m_callback)->tp_name; + debug << ", self=" << m_pythonSelf; + if (m_pythonSelf != nullptr) + debug << '/' << Py_TYPE(m_pythonSelf)->tp_name; + debug << ", m_pyClass=" << m_pyClass; + if (m_pyClass != nullptr) + debug << '/' << Py_TYPE(m_pyClass)->tp_name; + debug << ", signatures=" << m_signatures.keys(); +} + +QDebug operator<<(QDebug debug, const DynamicSlotDataV2 *d) +{ + QDebugStateSaver saver(debug); + debug.noquote(); + debug.nospace(); + debug << "DynamicSlotDataV2("; + if (d) + d->formatDebug(debug); + else + debug << '0'; + debug << ')'; + return debug; } 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 (PySide::isCompiledMethod(callback)) { + // PYSIDE-1523: PyMethod_Check is not accepting compiled form, we just go by attributes. + m_isMethod = true; + m_callback = PyObject_GetAttr(callback, PySide::PySideName::im_func()); + Py_DECREF(m_callback); + + m_pythonSelf = PyObject_GetAttr(callback, PySide::PySideName::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); } @@ -125,6 +121,11 @@ GlobalReceiverKey DynamicSlotDataV2::key(PyObject *callback) if (PyMethod_Check(callback)) { // PYSIDE-1422: Avoid hash on self which might be unhashable. return {PyMethod_GET_SELF(callback), PyMethod_GET_FUNCTION(callback)}; + } else if (PySide::isCompiledMethod(callback)) { + // PYSIDE-1589: Fix for slots in compiled functions + Shiboken::AutoDecRef self(PyObject_GetAttr(callback, PySide::PySideName::im_self())); + Shiboken::AutoDecRef func(PyObject_GetAttr(callback, PySide::PySideName::im_func())); + return {self, func}; } return {nullptr, callback}; } @@ -135,7 +136,7 @@ PyObject *DynamicSlotDataV2::callback() //create a callback based on method data if (m_isMethod) - callback = PyMethod_New(m_callback, m_pythonSelf); + callback = PepExt_Type_CallDescrGet(m_callback, m_pythonSelf, nullptr); else Py_INCREF(callback); @@ -159,9 +160,9 @@ int DynamicSlotDataV2::addSlot(const char *signature) void DynamicSlotDataV2::onCallbackDestroyed(void *data) { auto self = reinterpret_cast<DynamicSlotDataV2 *>(data); - self->m_weakRef = 0; + self->m_weakRef = nullptr; Py_BEGIN_ALLOW_THREADS - delete self->m_parent; + SignalManager::instance().deleteGlobalReceiver(self->m_parent); Py_END_ALLOW_THREADS } @@ -170,37 +171,25 @@ DynamicSlotDataV2::~DynamicSlotDataV2() Shiboken::GilState gil; Py_XDECREF(m_weakRef); - m_weakRef = 0; + m_weakRef = nullptr; - if (!m_isMethod) - Py_DECREF(m_callback); + Py_DECREF(m_callback); } -GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, GlobalReceiverV2MapPtr map) : +const char *GlobalReceiverV2::senderDynamicProperty = "_q_pyside_sender"; + +GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, QObject *receiver) : QObject(nullptr), m_metaObject("__GlobalReceiver__", &QObject::staticMetaObject), - m_sharedMap(std::move(map)) + m_receiver(receiver) { m_data = new DynamicSlotDataV2(callback, this); - m_metaObject.addSlot(RECEIVER_DESTROYED_SLOT_NAME); - m_metaObject.update(); - m_refs.append(NULL); - - - if (DESTROY_SIGNAL_ID == 0) - DESTROY_SIGNAL_ID = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)"); - - if (DESTROY_SLOT_ID == 0) - DESTROY_SLOT_ID = m_metaObject.indexOfMethod(QMetaMethod::Slot, RECEIVER_DESTROYED_SLOT_NAME); - - } GlobalReceiverV2::~GlobalReceiverV2() { m_refs.clear(); // Remove itself from map. - m_sharedMap->remove(m_data->key()); // Suppress handling of destroyed() for objects whose last reference is contained inside // the callback object that will now be deleted. The reference could be a default argument, // a callback local variable, etc. @@ -209,7 +198,7 @@ GlobalReceiverV2::~GlobalReceiverV2() // leading to the object being deleted, which emits destroyed(), which would try to invoke // the already deleted callback, and also try to delete the object again. DynamicSlotDataV2 *data = m_data; - m_data = Q_NULLPTR; + m_data = nullptr; delete data; } @@ -220,67 +209,34 @@ int GlobalReceiverV2::addSlot(const char *signature) void GlobalReceiverV2::incRef(const QObject *link) { - if (link) { - if (!m_refs.contains(link)) { - bool connected; - Py_BEGIN_ALLOW_THREADS - connected = QMetaObject::connect(link, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID); - Py_END_ALLOW_THREADS - if (connected) - m_refs.append(link); - else - Q_ASSERT(false); - } else { - m_refs.append(link); - } - } else { - m_refs.append(NULL); - } + Q_ASSERT(link); + m_refs.append(link); } void GlobalReceiverV2::decRef(const QObject *link) { - if (m_refs.empty()) - return; - - + Q_ASSERT(link); m_refs.removeOne(link); - if (link) { - if (!m_refs.contains(link)) { - bool result; - Py_BEGIN_ALLOW_THREADS - result = QMetaObject::disconnect(link, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID); - Py_END_ALLOW_THREADS - Q_ASSERT(result); - if (!result) - return; - } - } - - if (m_refs.empty()) - Py_BEGIN_ALLOW_THREADS - delete this; - Py_END_ALLOW_THREADS +} +void GlobalReceiverV2::notify() +{ + purgeDeletedSenders(); } -int GlobalReceiverV2::refCount(const QObject *link) const +static bool isNull(const QPointer<const QObject> &p) { - if (link) - return m_refs.count(link); + return p.isNull(); +} - return m_refs.size(); +void GlobalReceiverV2::purgeDeletedSenders() +{ + m_refs.erase(std::remove_if(m_refs.begin(), m_refs.end(), isNull), m_refs.end()); } -void GlobalReceiverV2::notify() +bool GlobalReceiverV2::isEmpty() const { - const QSet<const QObject *> objSet(m_refs.cbegin(), m_refs.cend()); - Py_BEGIN_ALLOW_THREADS - for (const QObject *o : objSet) { - QMetaObject::disconnect(o, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID); - QMetaObject::connect(o, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID); - } - Py_END_ALLOW_THREADS + return std::all_of(m_refs.cbegin(), m_refs.cend(), isNull); } GlobalReceiverKey GlobalReceiverV2::key() const @@ -308,26 +264,22 @@ int GlobalReceiverV2::qt_metacall(QMetaObject::Call call, int id, void **args) Q_ASSERT(slot.methodType() == QMetaMethod::Slot); if (!m_data) { - if (id != DESTROY_SLOT_ID) { - const QByteArray message = "PySide6 Warning: Skipping callback call " - + slot.methodSignature() + " because the callback object is being destructed."; - PyErr_WarnEx(PyExc_RuntimeWarning, message.constData(), 0); - } + const QByteArray message = "PySide6 Warning: Skipping callback call " + + slot.methodSignature() + " because the callback object is being destructed."; + PyErr_WarnEx(PyExc_RuntimeWarning, message.constData(), 0); return -1; } - if (id == DESTROY_SLOT_ID) { - if (m_refs.empty()) - return -1; - auto obj = *reinterpret_cast<QObject **>(args[1]); - incRef(); //keep the object live (safe ref) - m_refs.removeAll(obj); // remove all refs to this object - decRef(); //remove the safe ref - } else { - bool isShortCuit = (strstr(slot.methodSignature(), "(") == 0); - Shiboken::AutoDecRef callback(m_data->callback()); - SignalManager::callPythonMetaMethod(slot, args, callback, isShortCuit); - } + const bool setSenderDynamicProperty = !m_receiver.isNull(); + if (setSenderDynamicProperty) + m_receiver->setProperty(senderDynamicProperty, QVariant::fromValue(sender())); + + const bool isShortCuit = std::strchr(slot.methodSignature(), '(') == nullptr; + Shiboken::AutoDecRef callback(m_data->callback()); + SignalManager::callPythonMetaMethod(slot, args, callback, isShortCuit); + + if (setSenderDynamicProperty) + m_receiver->setProperty(senderDynamicProperty, QVariant{}); // SignalManager::callPythonMetaMethod might have failed, in that case we have to print the // error so it considered "handled". @@ -344,3 +296,28 @@ int GlobalReceiverV2::qt_metacall(QMetaObject::Call call, int id, void **args) return -1; } + +void GlobalReceiverV2::formatDebug(QDebug &debug) const +{ + debug << "receiver=" << m_receiver << ", slot=" << m_data; + if (isEmpty()) + debug << ", empty"; + else + debug << ", refs=" << m_refs; +}; + +QDebug operator<<(QDebug debug, const GlobalReceiverV2 *g) +{ + QDebugStateSaver saver(debug); + debug.noquote(); + debug.nospace(); + debug << "GlobalReceiverV2("; + if (g) + g->formatDebug(debug); + else + debug << '0'; + debug << ')'; + return debug; +} + +} // namespace PySide |