1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
// 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 "dynamicslot_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/QMetaMethod>
#include <QtCore/QSet>
#include <QtCore/QDebug>
#include <cstring>
#include <utility>
namespace PySide
{
GlobalReceiverKey GlobalReceiverV2::key(PyObject *callback)
{
Shiboken::GilState gil;
switch (DynamicSlot::slotType(callback)) {
case DynamicSlot::SlotType::Method:
// PYSIDE-1422: Avoid hash on self which might be unhashable.
return {PyMethod_GET_SELF(callback), PyMethod_GET_FUNCTION(callback)};
case DynamicSlot::SlotType::CompiledMethod: {
// 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};
}
case DynamicSlot::SlotType::Callable:
break;
}
return {nullptr, callback};
}
const char *GlobalReceiverV2::senderDynamicProperty = "_q_pyside_sender";
GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, QObject *receiver) :
QObject(nullptr),
m_metaObject("__GlobalReceiver__", &QObject::staticMetaObject),
m_data(DynamicSlot::create(callback, this)),
m_receiver(receiver)
{
}
GlobalReceiverV2::~GlobalReceiverV2()
{
m_refs.clear();
// Remove itself from map.
// 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.
// The signal has to be suppressed because it would lead to the following situation:
// Callback is deleted, hence the last reference is decremented,
// 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.
delete std::exchange(m_data, nullptr);
}
int GlobalReceiverV2::addSlot(const QByteArray &signature)
{
auto it = m_signatures.find(signature);
if (it == m_signatures.end()) {
const int index = metaObjectBuilder().addSlot(signature);
it = m_signatures.insert(signature, index);
}
return it.value();
}
void GlobalReceiverV2::incRef(const QObject *link)
{
Q_ASSERT(link);
m_refs.append(link);
}
void GlobalReceiverV2::decRef(const QObject *link)
{
Q_ASSERT(link);
m_refs.removeOne(link);
}
void GlobalReceiverV2::notify()
{
purgeDeletedSenders();
}
static bool isNull(const QPointer<const QObject> &p)
{
return p.isNull();
}
void GlobalReceiverV2::purgeDeletedSenders()
{
m_refs.erase(std::remove_if(m_refs.begin(), m_refs.end(), isNull), m_refs.end());
}
bool GlobalReceiverV2::isEmpty() const
{
return std::all_of(m_refs.cbegin(), m_refs.cend(), isNull);
}
const QMetaObject *GlobalReceiverV2::metaObject() const
{
return const_cast<GlobalReceiverV2 *>(this)->m_metaObject.update();
}
int GlobalReceiverV2::qt_metacall(QMetaObject::Call call, int id, void **args)
{
Shiboken::GilState gil;
Q_ASSERT(call == QMetaObject::InvokeMetaMethod);
Q_ASSERT(id >= QObject::staticMetaObject.methodCount());
QMetaMethod slot = metaObject()->method(id);
Q_ASSERT(slot.methodType() == QMetaMethod::Slot);
if (!m_data) {
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;
}
const bool setSenderDynamicProperty = !m_receiver.isNull();
if (setSenderDynamicProperty)
m_receiver->setProperty(senderDynamicProperty, QVariant::fromValue(sender()));
m_data->call(slot.parameterTypes(), slot.typeName(), args);
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".
if (PyErr_Occurred()) {
int reclimit = Py_GetRecursionLimit();
// Inspired by Python's errors.c: PyErr_GivenExceptionMatches() function.
// Temporarily bump the recursion limit, so that PyErr_Print will not raise a recursion
// error again. Don't do it when the limit is already insanely high, to avoid overflow.
if (reclimit < (1 << 30))
Py_SetRecursionLimit(reclimit + 5);
PyErr_Print();
Py_SetRecursionLimit(reclimit);
}
return -1;
}
void GlobalReceiverV2::formatDebug(QDebug &debug) const
{
debug << "receiver=" << m_receiver
<< ", signatures=" << m_signatures.keys() << ", 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
|