Skip to content

Commit 8034e74

Browse files
committed
Sync Dispatcher
1 parent 92a3c31 commit 8034e74

File tree

2 files changed

+47
-18
lines changed

2 files changed

+47
-18
lines changed

src/DispatchPtr.cxx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111
PyObject* CPyCppyy::DispatchPtr::Get() const
1212
{
1313
if (fPyHardRef) return fPyHardRef;
14-
if (fPyWeakRef) return PyWeakref_GetObject(fPyWeakRef);
14+
if (fPyWeakRef) {
15+
PyObject* disp = PyWeakref_GetObject(fPyWeakRef);
16+
if (disp != Py_None) // dispatcher object disappeared?
17+
return disp;
18+
}
1519
return nullptr;
1620
}
1721

@@ -45,7 +49,7 @@ CPyCppyy::DispatchPtr::~DispatchPtr() {
4549
// continued access
4650
if (fPyWeakRef) {
4751
PyObject* pyobj = PyWeakref_GetObject(fPyWeakRef);
48-
if (pyobj && ((CPPScope*)Py_TYPE(pyobj))->fFlags & CPPScope::kIsPython)
52+
if (pyobj && pyobj != Py_None && ((CPPScope*)Py_TYPE(pyobj))->fFlags & CPPScope::kIsPython)
4953
((CPPInstance*)pyobj)->GetObjectRaw() = nullptr;
5054
Py_DECREF(fPyWeakRef);
5155
} else if (fPyHardRef) {
@@ -83,6 +87,7 @@ void CPyCppyy::DispatchPtr::CppOwns()
8387
// C++ maintains the hardref, keeping the PyObject alive w/o outstanding ref
8488
if (fPyWeakRef) {
8589
fPyHardRef = PyWeakref_GetObject(fPyWeakRef);
90+
if (fPyHardRef == Py_None) fPyHardRef = nullptr;
8691
Py_XINCREF(fPyHardRef);
8792
Py_DECREF(fPyWeakRef); fPyWeakRef = nullptr;
8893
}

src/Dispatcher.cxx

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,26 @@ static inline void InjectMethod(Cppyy::TCppMethod_t method, const std::string& m
3434
if (Cppyy::IsConstMethod(method)) code << "const ";
3535
code << "{\n";
3636

37-
// start function body
37+
// on destruction, the Python object may go first, in which case provide a diagnostic
38+
// warning (raising a PyException may not be possible as this could happen during
39+
// program shutdown); note that this means that the actual result will be the default
40+
// and the caller may need to act on that, but that's still an improvement over a
41+
// possible crash
42+
code << " PyObject* iself = (PyObject*)_internal_self;\n"
43+
" if (!iself || iself == Py_None) {\n"
44+
" PyErr_Warn(PyExc_RuntimeWarning, (char*)\"Call attempted on deleted python-side proxy\");\n"
45+
" return";
46+
if (retType != "void") {
47+
if (retType.back() != '*')
48+
code << " " << CPyCppyy::TypeManip::remove_const(retType) << "{}";
49+
else
50+
code << " nullptr";
51+
}
52+
code << ";\n"
53+
" }\n"
54+
" Py_INCREF(iself);\n";
55+
56+
// start actual function body
3857
Utility::ConstructCallbackPreamble(retType, argtypes, code);
3958

4059
// perform actual method call
@@ -43,13 +62,13 @@ static inline void InjectMethod(Cppyy::TCppMethod_t method, const std::string& m
4362
#else
4463
code << " PyObject* mtPyName = PyUnicode_FromString(\"" << mtCppName << "\");\n"
4564
#endif
46-
" PyObject* pyresult = PyObject_CallMethodObjArgs((PyObject*)_internal_self, mtPyName";
65+
" PyObject* pyresult = PyObject_CallMethodObjArgs(iself, mtPyName";
4766
for (Cppyy::TCppIndex_t i = 0; i < nArgs; ++i)
4867
code << ", pyargs[" << i << "]";
49-
code << ", NULL);\n Py_DECREF(mtPyName);\n";
68+
code << ", NULL);\n Py_DECREF(mtPyName);\n Py_DECREF(iself);\n";
5069

5170
// close
52-
Utility::ConstructCallbackReturn(retType, nArgs, code);
71+
Utility::ConstructCallbackReturn(retType, (int)nArgs, code);
5372
}
5473

5574
//----------------------------------------------------------------------------
@@ -127,11 +146,11 @@ namespace {
127146
using namespace Cppyy;
128147

129148
static inline
130-
std::vector<TCppMethod_t> FindBaseMethod(TCppScope_t tbase, const std::string mtCppName)
149+
std::vector<TCppIndex_t> FindBaseMethod(TCppScope_t tbase, const std::string mtCppName)
131150
{
132151
// Recursively walk the inheritance tree to find the overloads of the named method
133-
std::vector<TCppMethod_t> result;
134-
result = GetMethodsFromName(tbase, mtCppName);
152+
std::vector<TCppIndex_t> result;
153+
result = GetMethodIndicesFromName(tbase, mtCppName);
135154
if (result.empty()) {
136155
for (TCppIndex_t ibase = 0; ibase < GetNumBases(tbase); ++ibase) {
137156
TCppScope_t b = GetScope(GetBaseName(tbase, ibase));
@@ -215,17 +234,22 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct,
215234

216235
// add a virtual destructor for good measure, which is allowed to be "overridden" by
217236
// the conventional __destruct__ method (note that __del__ is always called, too, if
218-
// provided, but only when the Python object goes away)
237+
// provided, but only when the Python object goes away; furthermore, if the Python
238+
// object goes before the C++ one, only __del__ is called)
219239
if (PyMapping_HasKeyString(dct, (char*)"__destruct__")) {
220240
code << " virtual ~" << derivedName << "() {\n"
241+
" PyObject* iself = (PyObject*)_internal_self;\n"
242+
" if (!iself || iself == Py_None)\n"
243+
" return;\n" // safe, as destructor always returns void
244+
" Py_INCREF(iself);\n"
221245
" PyObject* mtPyName = PyUnicode_FromString(\"__destruct__\");\n"
222-
" PyObject* pyresult = PyObject_CallMethodObjArgs((PyObject*)_internal_self, mtPyName, NULL);\n"
223-
" Py_DECREF(mtPyName);\n";
246+
" PyObject* pyresult = PyObject_CallMethodObjArgs(iself, mtPyName, NULL);\n"
247+
" Py_DECREF(mtPyName);\n Py_DECREF(iself);\n";
224248

225249
// this being a destructor, print on exception rather than propagate using the
226250
// magic C++ exception ...
227-
code << " if (!pyresult) PyErr_Print();\n"
228-
" else { Py_DECREF(pyresult); }\n"
251+
code << " if (!pyresult) PyErr_Print();\n"
252+
" else { Py_DECREF(pyresult); }\n"
229253
" }\n";
230254
} else
231255
code << " virtual ~" << derivedName << "() {}\n";
@@ -342,10 +366,10 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct,
342366
// TODO: should probably invert this looping; but that makes handling overloads clunky
343367
PyObject* key = PyList_GET_ITEM(keys, i);
344368
std::string mtCppName = CPyCppyy_PyText_AsString(key);
345-
const auto& methods = FindBaseMethod(tbase, mtCppName);
346-
for (auto method : methods)
347-
InjectMethod(method, mtCppName, code);
348-
if (!methods.empty()) {
369+
const auto& v = FindBaseMethod(tbase, mtCppName);
370+
for (auto idx : v)
371+
InjectMethod(Cppyy::GetMethod(tbase, idx), mtCppName, code);
372+
if (!v.empty()) {
349373
if (PyDict_DelItem(clbs, key) != 0) PyErr_Clear();
350374
}
351375
}

0 commit comments

Comments
 (0)