diff options
Diffstat (limited to 'sources/shiboken2/libshiboken/basewrapper.cpp')
-rw-r--r-- | sources/shiboken2/libshiboken/basewrapper.cpp | 116 |
1 files changed, 78 insertions, 38 deletions
diff --git a/sources/shiboken2/libshiboken/basewrapper.cpp b/sources/shiboken2/libshiboken/basewrapper.cpp index ac086b354..dca5631bc 100644 --- a/sources/shiboken2/libshiboken/basewrapper.cpp +++ b/sources/shiboken2/libshiboken/basewrapper.cpp @@ -56,7 +56,6 @@ #include <algorithm> #include "threadstatesaver.h" #include "signature.h" -#include "qapp_macro.h" #include "voidptr.h" #include <iostream> @@ -96,6 +95,13 @@ static void SbkObjectTypeDealloc(PyObject *pyObj); static PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *kwds); static SelectableFeatureHook SelectFeatureSet = nullptr; +static DestroyQAppHook DestroyQApplication = nullptr; + +// PYSIDE-1470: Provide a hook to kill an Application from Shiboken. +void setDestroyQApplication(DestroyQAppHook func) +{ + DestroyQApplication = func; +} static PyObject *Sbk_TypeGet___dict__(PyTypeObject *type, void *context); // forward @@ -427,9 +433,7 @@ static void SbkDeallocWrapperCommon(PyObject *pyObj, bool canDelete) // be invoked and it trying to delete this object while it is still in // progress from the first time around, resulting in a double delete and a // crash. - // PYSIDE-571: Some objects do not use GC, so check this! - if (PyObject_IS_GC(pyObj)) - PyObject_GC_UnTrack(pyObj); + PyObject_GC_UnTrack(pyObj); // Check that Python is still initialized as sometimes this is called by a static destructor // after Python interpeter is shutdown. @@ -541,6 +545,47 @@ void SbkObjectTypeDealloc(PyObject *pyObj) } } +//////////////////////////////////////////////////////////////////////////// +// +// Support for the qApp macro. +// +// qApp is a macro in Qt5. In Python, we simulate that a little by a +// variable that monitors Q*Application.instance(). +// This variable is also able to destroy the app by qApp.shutdown(). +// + +PyObject *MakeQAppWrapper(PyTypeObject *type) +{ + static PyObject *qApp_last = nullptr; + + // protecting from multiple application instances + if (!(type == nullptr || qApp_last == Py_None)) { + const char *res_name = PepType_GetNameStr(Py_TYPE(qApp_last)); + const char *type_name = PepType_GetNameStr(type); + PyErr_Format(PyExc_RuntimeError, "Please destroy the %s singleton before" + " creating a new %s instance.", res_name, type_name); + return nullptr; + } + + // monitoring the last application state + PyObject *qApp_curr = type != nullptr ? PyObject_GC_New(PyObject, type) : Py_None; + static PyObject *builtins = PyEval_GetBuiltins(); + if (PyDict_SetItem(builtins, Shiboken::PyName::qApp(), qApp_curr) < 0) + return nullptr; + qApp_last = qApp_curr; + // Note: This Py_INCREF would normally be wrong because the qApp + // object already has a reference from PyObject_GC_New. But this is + // exactly the needed reference that keeps qApp alive from alone! + Py_INCREF(qApp_curr); + // PYSIDE-1470: As a side effect, the interactive "_" variable tends to + // create reference cycles. It was found when using gc.collect(). But using + // PyGC_collect() inside the C code had no effect in the interactive shell. + // The cycle exists only in the eval loop of the interpreter! + if (PyDict_GetItem(builtins, Shiboken::PyName::underscore())) + PyDict_SetItem(builtins, Shiboken::PyName::underscore(), Py_None); + return qApp_curr; +} + ////////////////////////////////////////////////////////////////////////////// // // PYSIDE-1019: Support switchable extensions @@ -555,9 +600,11 @@ void SbkObjectTypeDealloc(PyObject *pyObj) // SbkObject_GenericSetAttr PyObject_GenericSetAttr // -void initSelectableFeature(SelectableFeatureHook func) +SelectableFeatureHook initSelectableFeature(SelectableFeatureHook func) { + auto ret = SelectFeatureSet; SelectFeatureSet = func; + return ret; } static PyObject *mangled_type_getattro(PyTypeObject *type, PyObject *name) @@ -579,7 +626,7 @@ static PyObject *Sbk_TypeGet___dict__(PyTypeObject *type, void *context) * This is the override for getting a dict. */ auto dict = type->tp_dict; - if (dict == NULL) + if (dict == nullptr) Py_RETURN_NONE; if (SelectFeatureSet != nullptr) dict = SelectFeatureSet(type); @@ -723,10 +770,13 @@ static PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyOb sotp->d_func = nullptr; sotp->is_user_type = 1; + // PYSIDE-1463: Prevent feature switching while in the creation process + auto saveFeature = initSelectableFeature(nullptr); for (SbkObjectType *base : bases) { if (PepType_SOTP(base)->subtype_init) PepType_SOTP(base)->subtype_init(newType, args, kwds); } + initSelectableFeature(saveFeature); return reinterpret_cast<PyObject *>(newType); } @@ -749,42 +799,25 @@ static PyObject *_setupNew(SbkObject *self, PyTypeObject *subtype) self->ob_dict = nullptr; self->weakreflist = nullptr; self->d = d; + PyObject_GC_Track(reinterpret_cast<PyObject *>(self)); return reinterpret_cast<PyObject *>(self); } PyObject *SbkObjectTpNew(PyTypeObject *subtype, PyObject *, PyObject *) { SbkObject *self = PyObject_GC_New(SbkObject, subtype); - PyObject *res = _setupNew(self, subtype); - PyObject_GC_Track(reinterpret_cast<PyObject *>(self)); - return res; + return _setupNew(self, subtype); } PyObject *SbkQAppTpNew(PyTypeObject *subtype, PyObject *, PyObject *) { - // PYSIDE-571: - // For qApp, we need to create a singleton Python object. - // We cannot track this with the GC, because it is a static variable! - - // Python 2 has a weird handling of flags in derived classes that Python 3 - // does not have. Observed with bug_307.py. - // But it could theoretically also happen with Python3. - // Therefore we enforce that there is no GC flag, ever! - - // PYSIDE-560: - // We avoid to use this in Python 3, because we have a hard time to get - // write access to these flags - - // PYSIDE-1447: - // Since Python 3.8, we have the same weird flags handling in Python 3.8 - // as well. The singleton Python is no longer needed and we could remove - // the whole special handling, maybe in another checkin. - if (PyType_HasFeature(subtype, Py_TPFLAGS_HAVE_GC)) { - subtype->tp_flags &= ~Py_TPFLAGS_HAVE_GC; - subtype->tp_free = PyObject_Del; - } auto self = reinterpret_cast<SbkObject *>(MakeQAppWrapper(subtype)); - return self == nullptr ? nullptr : _setupNew(self, subtype); + if (self == nullptr) + return nullptr; + auto ret = _setupNew(self, subtype); + auto priv = self->d; + priv->isQAppSingleton = 1; + return ret; } PyObject *SbkDummyNew(PyTypeObject *type, PyObject *, PyObject *) @@ -862,7 +895,7 @@ PyObject *FallbackRichCompare(PyObject *self, PyObject *other, int op) opstrings[op], self->ob_type->tp_name, other->ob_type->tp_name); - return NULL; + return nullptr; } Py_INCREF(res); return res; @@ -1274,6 +1307,12 @@ bool wasCreatedByPython(SbkObject *pyObj) void callCppDestructors(SbkObject *pyObj) { + auto priv = pyObj->d; + if (priv->isQAppSingleton && DestroyQApplication) { + // PYSIDE-1470: Allow to destroy the application from Shiboken. + DestroyQApplication(); + return; + } PyTypeObject *type = Py_TYPE(pyObj); SbkObjectTypePrivate *sotp = PepType_SOTP(type); if (sotp->is_multicpp) { @@ -1286,18 +1325,19 @@ void callCppDestructors(SbkObject *pyObj) sotp->cpp_dtor(pyObj->d->cptr[0]); } + if (priv->validCppObject && priv->containsCppWrapper) { + BindingManager::instance().releaseWrapper(pyObj); + } + /* invalidate needs to be called before deleting pointer array because it needs to delete entries for them from the BindingManager hash table; also release wrapper explicitly if object contains C++ wrapper because invalidate doesn't */ invalidate(pyObj); - if (pyObj->d->validCppObject && pyObj->d->containsCppWrapper) { - BindingManager::instance().releaseWrapper(pyObj); - } - delete[] pyObj->d->cptr; - pyObj->d->cptr = nullptr; - pyObj->d->validCppObject = false; + delete[] priv->cptr; + priv->cptr = nullptr; + priv->validCppObject = false; } bool hasOwnership(SbkObject *pyObj) |