aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/libshiboken/basewrapper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/libshiboken/basewrapper.cpp')
-rw-r--r--sources/shiboken6/libshiboken/basewrapper.cpp1925
1 files changed, 1925 insertions, 0 deletions
diff --git a/sources/shiboken6/libshiboken/basewrapper.cpp b/sources/shiboken6/libshiboken/basewrapper.cpp
new file mode 100644
index 000000000..a79a4cb69
--- /dev/null
+++ b/sources/shiboken6/libshiboken/basewrapper.cpp
@@ -0,0 +1,1925 @@
+// Copyright (C) 2019 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 "basewrapper.h"
+#include "basewrapper_p.h"
+#include "bindingmanager.h"
+#include "helper.h"
+#include "pep384ext.h"
+#include "sbkconverter.h"
+#include "sbkenum.h"
+#include "sbkerrors.h"
+#include "sbkfeature_base.h"
+#include "sbkmodule.h"
+#include "sbkstring.h"
+#include "sbkstaticstrings.h"
+#include "sbkstaticstrings_p.h"
+#include "autodecref.h"
+#include "gilstate.h"
+#include <string>
+#include <cstring>
+#include <cstddef>
+#include <set>
+#include <sstream>
+#include <algorithm>
+#include "threadstatesaver.h"
+#include "signature.h"
+#include "signature_p.h"
+#include "voidptr.h"
+
+#include <string>
+#include <iostream>
+#include <sstream>
+
+#if defined(__APPLE__)
+#include <dlfcn.h>
+#endif
+
+namespace {
+ void _destroyParentInfo(SbkObject *obj, bool keepReference);
+}
+
+namespace Shiboken
+{
+// Walk through the first level of non-user-type Sbk base classes relevant for
+// C++ object allocation. Return true from the predicate to terminate.
+template <class Predicate>
+bool walkThroughBases(PyTypeObject *currentType, Predicate predicate)
+{
+ PyObject *bases = currentType->tp_bases;
+ const Py_ssize_t numBases = PyTuple_Size(bases);
+ bool result = false;
+ for (Py_ssize_t i = 0; !result && i < numBases; ++i) {
+ auto type = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(bases, i));
+ if (PyType_IsSubtype(type, SbkObject_TypeF()) != 0) {
+ result = PepType_SOTP(type)->is_user_type
+ ? walkThroughBases(type, predicate) : predicate(type);
+ }
+ }
+ return result;
+}
+
+int getTypeIndexOnHierarchy(PyTypeObject *baseType, PyTypeObject *desiredType)
+{
+ int index = -1;
+ walkThroughBases(baseType, [&index, desiredType](PyTypeObject *node) {
+ ++index;
+ return PyType_IsSubtype(node, desiredType) != 0;
+ });
+ return index;
+}
+
+int getNumberOfCppBaseClasses(PyTypeObject *baseType)
+{
+ int count = 0;
+ walkThroughBases(baseType, [&count](PyTypeObject *) {
+ ++count;
+ return false;
+ });
+ return count;
+}
+
+std::vector<PyTypeObject *> getCppBaseClasses(PyTypeObject *baseType)
+{
+ std::vector<PyTypeObject *> cppBaseClasses;
+ walkThroughBases(baseType, [&cppBaseClasses](PyTypeObject *node) {
+ cppBaseClasses.push_back(node);
+ return false;
+ });
+ return cppBaseClasses;
+}
+
+using DestructorEntries = std::vector<DestructorEntry>;
+
+DestructorEntries getDestructorEntries(SbkObject *o)
+{
+ DestructorEntries result;
+ void **cptrs = o->d->cptr;
+ walkThroughBases(Py_TYPE(o), [&result, cptrs](PyTypeObject *node) {
+ auto *sotp = PepType_SOTP(node);
+ auto index = result.size();
+ result.push_back(DestructorEntry{sotp->cpp_dtor,
+ cptrs[index]});
+ return false;
+ });
+ return result;
+}
+
+static void callDestructor(const DestructorEntries &dts)
+{
+ for (const auto &e : dts) {
+ Shiboken::ThreadStateSaver threadSaver;
+ threadSaver.save();
+ e.destructor(e.cppInstance);
+ }
+}
+
+} // namespace Shiboken
+
+extern "C"
+{
+
+// PYSIDE-939: A general replacement for object_dealloc.
+void Sbk_object_dealloc(PyObject *self)
+{
+ if (PepRuntime_38_flag) {
+ // PYSIDE-939: Handling references correctly.
+ // This was not needed before Python 3.8 (Python issue 35810)
+ Py_DECREF(Py_TYPE(self));
+ }
+ PepExt_TypeCallFree(self);
+}
+
+static void SbkObjectType_tp_dealloc(PyTypeObject *pyType);
+static PyTypeObject *SbkObjectType_tp_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds);
+
+static DestroyQAppHook DestroyQApplication = nullptr;
+
+// PYSIDE-1470: Provide a hook to kill an Application from Shiboken.
+void setDestroyQApplication(DestroyQAppHook func)
+{
+ DestroyQApplication = func;
+}
+
+// PYSIDE-535: Use the C API in PyPy instead of `op->ob_dict`, directly
+LIBSHIBOKEN_API PyObject *SbkObject_GetDict_NoRef(PyObject *op)
+{
+ assert(Shiboken::Object::checkType(op));
+#ifdef PYPY_VERSION
+ Shiboken::GilState state;
+ auto *ret = PyObject_GenericGetDict(op, nullptr);
+ Py_DECREF(ret);
+ return ret;
+#else
+ auto *sbkObj = reinterpret_cast<SbkObject *>(op);
+ if (!sbkObj->ob_dict) {
+ Shiboken::GilState state;
+ sbkObj->ob_dict = PyDict_New();
+ }
+ return sbkObj->ob_dict;
+#endif
+}
+
+static int
+check_set_special_type_attr(PyTypeObject *type, PyObject *value, const char *name)
+{
+ if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
+ PyErr_Format(PyExc_TypeError,
+ "can't set %s.%s", type->tp_name, name);
+ return 0;
+ }
+ if (!value) {
+ PyErr_Format(PyExc_TypeError,
+ "can't delete %s.%s", type->tp_name, name);
+ return 0;
+ }
+ return 1;
+}
+
+// PYSIDE-1177: Add a setter to allow setting type doc.
+static int
+type_set_doc(PyTypeObject *type, PyObject *value, void * /* context */)
+{
+ if (!check_set_special_type_attr(type, value, "__doc__"))
+ return -1;
+ PyType_Modified(type);
+ Shiboken::AutoDecRef tpDict(PepType_GetDict(type));
+ return PyDict_SetItem(tpDict.object(), Shiboken::PyMagicName::doc(), value);
+}
+
+// PYSIDE-908: The function PyType_Modified does not work in PySide, so we need to
+// explicitly pass __doc__.
+static PyGetSetDef SbkObjectType_tp_getset[] = {
+ {const_cast<char *>("__doc__"), reinterpret_cast<getter>(Sbk_TypeGet___doc__),
+ reinterpret_cast<setter>(type_set_doc), nullptr, nullptr},
+ {const_cast<char *>("__dict__"), reinterpret_cast<getter>(Sbk_TypeGet___dict__),
+ nullptr, nullptr, nullptr},
+ {nullptr, nullptr, nullptr, nullptr, nullptr} // Sentinel
+};
+
+static PyTypeObject *createObjectTypeType()
+{
+ PyType_Slot SbkObjectType_Type_slots[] = {
+ {Py_tp_dealloc, reinterpret_cast<void *>(SbkObjectType_tp_dealloc)},
+ {Py_tp_getattro, reinterpret_cast<void *>(mangled_type_getattro)},
+ {Py_tp_base, static_cast<void *>(&PyType_Type)},
+ {Py_tp_alloc, reinterpret_cast<void *>(PyType_GenericAlloc)},
+ {Py_tp_new, reinterpret_cast<void *>(SbkObjectType_tp_new)},
+ {Py_tp_free, reinterpret_cast<void *>(PyObject_GC_Del)},
+ {Py_tp_getset, reinterpret_cast<void *>(SbkObjectType_tp_getset)},
+ {0, nullptr}
+ };
+
+ // PYSIDE-535: The tp_itemsize field is inherited and does not need to be set.
+ // In PyPy, it _must_ not be set, because it would have the meanin
+ // that a `__len__` field must be defined. Not doing so creates
+ // a hard-to-find crash.
+ //
+ // PYSIDE-2230: In Python < 3.12, the decision which base class should create
+ // the instance is arbitrarily drawn by the size of the type.
+ // Ignoring this creates a bug in the new version of bug_825 that
+ // selects the wrong metatype.
+ //
+ PyType_Spec SbkObjectType_Type_spec = {
+ "1:Shiboken.ObjectType",
+ static_cast<int>(PyType_Type.tp_basicsize) + 1, // see above
+ 0, // sizeof(PyMemberDef), not for PyPy without a __len__ defined
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_TYPE_SUBCLASS,
+ SbkObjectType_Type_slots,
+ };
+
+ PyType_Spec SbkObjectType_Type_spec_312 = {
+ "1:Shiboken.ObjectType",
+ -long(sizeof(SbkObjectTypePrivate)),
+ 0, // sizeof(PyMemberDef), not for PyPy without a __len__ defined
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_TYPE_SUBCLASS,
+ SbkObjectType_Type_slots,
+ };
+
+ return SbkType_FromSpec(_PepRuntimeVersion() >= 0x030C00 ?
+ &SbkObjectType_Type_spec_312 :
+ &SbkObjectType_Type_spec);
+}
+
+PyTypeObject *SbkObjectType_TypeF(void)
+{
+ static auto *type = createObjectTypeType();
+ return type;
+}
+
+static PyObject *SbkObjectGetDict(PyObject *pObj, void *)
+{
+ auto ret = SbkObject_GetDict_NoRef(pObj);
+ Py_XINCREF(ret);
+ return ret;
+}
+
+static PyGetSetDef SbkObject_tp_getset[] = {
+ {const_cast<char *>("__dict__"), SbkObjectGetDict, nullptr, nullptr, nullptr},
+ {nullptr, nullptr, nullptr, nullptr, nullptr} // Sentinel
+};
+
+static int SbkObject_tp_traverse(PyObject *self, visitproc visit, void *arg)
+{
+ auto *sbkSelf = reinterpret_cast<SbkObject *>(self);
+
+ //Visit children
+ Shiboken::ParentInfo *pInfo = sbkSelf->d->parentInfo;
+ if (pInfo) {
+ for (SbkObject *c : pInfo->children)
+ Py_VISIT(c);
+ }
+
+ //Visit refs
+ Shiboken::RefCountMap *rInfo = sbkSelf->d->referredObjects;
+ if (rInfo) {
+ for (auto it = rInfo->begin(), end = rInfo->end(); it != end; ++it)
+ Py_VISIT(it->second);
+ }
+
+ if (sbkSelf->ob_dict)
+ Py_VISIT(sbkSelf->ob_dict);
+
+ // This was not needed before Python 3.9 (Python issue 35810 and 40217)
+ Py_VISIT(Py_TYPE(self));
+ return 0;
+}
+
+static int SbkObject_tp_clear(PyObject *self)
+{
+ auto *sbkSelf = reinterpret_cast<SbkObject *>(self);
+
+ Shiboken::Object::removeParent(sbkSelf);
+
+ if (sbkSelf->d->parentInfo)
+ _destroyParentInfo(sbkSelf, true);
+
+ Shiboken::Object::clearReferences(sbkSelf);
+
+ if (sbkSelf->ob_dict)
+ Py_CLEAR(sbkSelf->ob_dict);
+ return 0;
+}
+
+static PyTypeObject *createObjectType()
+{
+ PyType_Slot SbkObject_Type_slots[] = {
+ {Py_tp_getattro, reinterpret_cast<void *>(SbkObject_GenericGetAttr)},
+ {Py_tp_setattro, reinterpret_cast<void *>(SbkObject_GenericSetAttr)},
+ {Py_tp_dealloc, reinterpret_cast<void *>(SbkDeallocWrapperWithPrivateDtor)},
+ {Py_tp_traverse, reinterpret_cast<void *>(SbkObject_tp_traverse)},
+ {Py_tp_clear, reinterpret_cast<void *>(SbkObject_tp_clear)},
+ // unsupported: {Py_tp_weaklistoffset, (void *)offsetof(SbkObject, weakreflist)},
+ {Py_tp_getset, reinterpret_cast<void *>(SbkObject_tp_getset)},
+ // unsupported: {Py_tp_dictoffset, (void *)offsetof(SbkObject, ob_dict)},
+ {0, nullptr}
+ };
+
+ PyType_Spec SbkObject_Type_spec = {
+ "1:Shiboken.Object",
+ sizeof(SbkObject),
+ 0,
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC,
+ SbkObject_Type_slots,
+ };
+
+ // PYSIDE-2230: When creating this type, we cannot easily handle the metaclass.
+ // In versions < Python 3.12, the metaclass can only be set
+ // indirectly by a base which has that metaclass.
+ // But before 3.12 is the minimum version, we cannot use the new
+ // function, although we would need this for 3.12 :-D
+ // We do a special patching here that is triggered through Py_None.
+ auto *type = SbkType_FromSpec_BMDWB(&SbkObject_Type_spec,
+ Py_None, // bases, spectial flag!
+ SbkObjectType_TypeF(),
+ offsetof(SbkObject, ob_dict),
+ offsetof(SbkObject, weakreflist),
+ nullptr); // bufferprocs
+ return type;
+}
+
+PyTypeObject *SbkObject_TypeF(void)
+{
+ static auto *type = createObjectType(); // bufferprocs
+ return type;
+}
+
+static const char *SbkObject_SignatureStrings[] = {
+ "Shiboken.Object(self)",
+ nullptr}; // Sentinel
+
+static int mainThreadDeletionHandler(void *)
+{
+ if (Py_IsInitialized())
+ Shiboken::BindingManager::instance().runDeletionInMainThread();
+ return 0;
+}
+
+static void SbkDeallocWrapperCommon(PyObject *pyObj, bool canDelete)
+{
+ auto *sbkObj = reinterpret_cast<SbkObject *>(pyObj);
+ PyTypeObject *pyType = Py_TYPE(pyObj);
+
+ // Need to decref the type if this is the dealloc func; if type
+ // is subclassed, that dealloc func will decref (see subtype_dealloc
+ // in typeobject.c in the python sources)
+ auto dealloc = PyType_GetSlot(pyType, Py_tp_dealloc);
+ bool needTypeDecref = dealloc == SbkDeallocWrapper
+ || dealloc == SbkDeallocWrapperWithPrivateDtor;
+ if (PepRuntime_38_flag) {
+ // PYSIDE-939: Additional rule: Also when a subtype is heap allocated,
+ // then the subtype_dealloc deref will be suppressed, and we need again
+ // to supply a decref.
+ needTypeDecref |= (pyType->tp_base->tp_flags & Py_TPFLAGS_HEAPTYPE) != 0;
+ }
+
+#if defined(__APPLE__)
+ // Just checking once that our assumptions are right.
+ if (false) {
+ void *p = PyType_GetSlot(pyType, Py_tp_dealloc);
+ Dl_info dl_info;
+ dladdr(p, &dl_info);
+ fprintf(stderr, "tp_dealloc is %s\n", dl_info.dli_sname);
+ }
+ // Gives one of our functions
+ // "Sbk_object_dealloc"
+ // "SbkDeallocWrapperWithPrivateDtor"
+ // "SbkDeallocQAppWrapper"
+ // "SbkDeallocWrapper"
+ // but for typedealloc_test.py we get
+ // "subtype_dealloc"
+#endif
+
+ // Ensure that the GC is no longer tracking this object to avoid a
+ // possible reentrancy problem. Since there are multiple steps involved
+ // in deallocating a SbkObject it is possible for the garbage collector to
+ // 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.
+ PyObject_GC_UnTrack(pyObj);
+
+ // Check that Python is still initialized as sometimes this is called by a static destructor
+ // after Python interpeter is shutdown.
+ if (sbkObj->weakreflist && Py_IsInitialized())
+ PyObject_ClearWeakRefs(pyObj);
+
+ // If I have ownership and is valid delete C++ pointer
+ auto *sotp = PepType_SOTP(pyType);
+ canDelete &= sbkObj->d->hasOwnership && sbkObj->d->validCppObject;
+ if (canDelete) {
+ if (sotp->delete_in_main_thread && Shiboken::currentThreadId() != Shiboken::mainThreadId()) {
+ auto &bindingManager = Shiboken::BindingManager::instance();
+ if (sotp->is_multicpp) {
+ const auto entries = Shiboken::getDestructorEntries(sbkObj);
+ for (const auto &e : entries)
+ bindingManager.addToDeletionInMainThread(e);
+ } else {
+ Shiboken::DestructorEntry e{sotp->cpp_dtor, sbkObj->d->cptr[0]};
+ bindingManager.addToDeletionInMainThread(e);
+ }
+ Py_AddPendingCall(mainThreadDeletionHandler, nullptr);
+ canDelete = false;
+ }
+ }
+
+ PyObject *error_type, *error_value, *error_traceback;
+
+ /* Save the current exception, if any. */
+ PyErr_Fetch(&error_type, &error_value, &error_traceback);
+
+ if (canDelete) {
+ if (sotp->is_multicpp) {
+ const auto entries = Shiboken::getDestructorEntries(sbkObj);
+ Shiboken::Object::deallocData(sbkObj, true);
+ callDestructor(entries);
+ } else {
+ void *cptr = sbkObj->d->cptr[0];
+ Shiboken::Object::deallocData(sbkObj, true);
+
+ Shiboken::ThreadStateSaver threadSaver;
+ if (Py_IsInitialized())
+ threadSaver.save();
+ sotp->cpp_dtor(cptr);
+ }
+ } else {
+ Shiboken::Object::deallocData(sbkObj, true);
+ }
+
+ /* Restore the saved exception. */
+ PyErr_Restore(error_type, error_value, error_traceback);
+
+ if (needTypeDecref)
+ Py_DECREF(pyType);
+ if (PepRuntime_38_flag) {
+ // PYSIDE-939: Handling references correctly.
+ // This was not needed before Python 3.8 (Python issue 35810)
+ Py_DECREF(pyType);
+ }
+}
+
+static inline PyObject *_Sbk_NewVarObject(PyTypeObject *type)
+{
+ // PYSIDE-1970: Support __slots__, implemented by PyVarObject
+ auto const baseSize = sizeof(SbkObject);
+ auto varCount = Py_SIZE(type);
+ auto *self = PyObject_GC_NewVar(PyObject, type, varCount);
+ if (varCount)
+ std::memset(reinterpret_cast<char *>(self) + baseSize, 0, varCount * sizeof(void *));
+ return self;
+}
+
+void SbkDeallocWrapper(PyObject *pyObj)
+{
+ SbkDeallocWrapperCommon(pyObj, true);
+}
+
+void SbkDeallocQAppWrapper(PyObject *pyObj)
+{
+ SbkDeallocWrapper(pyObj);
+ // PYSIDE-571: make sure to create a singleton deleted qApp.
+ Py_DECREF(MakeQAppWrapper(nullptr));
+}
+
+void SbkDeallocWrapperWithPrivateDtor(PyObject *self)
+{
+ SbkDeallocWrapperCommon(self, false);
+}
+
+void SbkObjectType_tp_dealloc(PyTypeObject *sbkType)
+{
+ SbkObjectTypePrivate *sotp = PepType_SOTP(sbkType);
+ auto pyObj = reinterpret_cast<PyObject *>(sbkType);
+
+ PyObject_GC_UnTrack(pyObj);
+#if !defined(Py_LIMITED_API) && !defined(PYPY_VERSION)
+# if PY_VERSION_HEX >= 0x030A0000
+ Py_TRASHCAN_BEGIN(pyObj, 1);
+# else
+ Py_TRASHCAN_SAFE_BEGIN(pyObj);
+# endif
+#endif
+ if (sotp) {
+ if (sotp->user_data && sotp->d_func) {
+ sotp->d_func(sotp->user_data);
+ sotp->user_data = nullptr;
+ }
+ free(sotp->original_name);
+ sotp->original_name = nullptr;
+ if (!Shiboken::ObjectType::isUserType(sbkType))
+ Shiboken::Conversions::deleteConverter(sotp->converter);
+ PepType_SOTP_delete(sbkType);
+ }
+#if !defined(Py_LIMITED_API) && !defined(PYPY_VERSION)
+# if PY_VERSION_HEX >= 0x030A0000
+ Py_TRASHCAN_END;
+# else
+ Py_TRASHCAN_SAFE_END(pyObj);
+# endif
+#endif
+ if (PepRuntime_38_flag) {
+ // PYSIDE-939: Handling references correctly.
+ // This was not needed before Python 3.8 (Python issue 35810)
+ Py_DECREF(Py_TYPE(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 = qApp_last != nullptr
+ ? PepType_GetNameStr(Py_TYPE(qApp_last)) : "<Unknown>";
+ 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 ? _Sbk_NewVarObject(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. This is disturbing when trying
+ // to remove qApp with del.
+ // PYSIDE-1758: Since we moved to an explicit qApp.shutdown() call, we
+ // no longer initialize "_" with Py_None.
+ return qApp_curr;
+}
+
+static PyTypeObject *SbkObjectType_tp_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
+{
+ // Check if all bases are new style before calling type.tp_new
+ // Was causing gc assert errors in test_bug704.py when
+ // this check happened after creating the type object.
+ // Argument parsing take from type.tp_new code.
+
+ // PYSIDE-595: Also check if all bases allow inheritance.
+ // Before we changed to heap types, it was sufficient to remove the
+ // Py_TPFLAGS_BASETYPE flag. That does not work, because PySide does
+ // not respect this flag itself!
+ PyObject *name;
+ PyObject *pyBases;
+ PyObject *dict;
+ static const char *kwlist[] = { "name", "bases", "dict", nullptr};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO!O!:sbktype", const_cast<char **>(kwlist),
+ &name,
+ &PyTuple_Type, &pyBases,
+ &PyDict_Type, &dict))
+ return nullptr;
+
+ for (int i=0, i_max=PyTuple_GET_SIZE(pyBases); i < i_max; i++) {
+ PyObject *baseType = PyTuple_GET_ITEM(pyBases, i);
+ if (PepExt_Type_GetNewSlot(reinterpret_cast<PyTypeObject *>(baseType)) == SbkDummyNew) {
+ // PYSIDE-595: A base class does not allow inheritance.
+ return reinterpret_cast<PyTypeObject *>(SbkDummyNew(metatype, args, kwds));
+ }
+ }
+
+ // PYSIDE-939: This is still a temporary patch that circumvents the problem
+ // with Py_TPFLAGS_METHOD_DESCRIPTOR. The problem exists in Python 3.8
+ // until 3.9.12, only. We check the runtime and hope for this version valishing.
+ // https://github.com/python/cpython/issues/92112 will not be fixed for 3.8 :/
+ PyTypeObject *newType{};
+ static auto triplet = _PepRuntimeVersion();
+ if (triplet >= (3 << 16 | 8 << 8 | 0) && triplet < (3 << 16 | 9 << 8 | 13)) {
+ auto hold = PyMethodDescr_Type.tp_flags;
+ PyMethodDescr_Type.tp_flags &= ~Py_TPFLAGS_METHOD_DESCRIPTOR;
+ newType = PepType_Type_tp_new(metatype, args, kwds);
+ PyMethodDescr_Type.tp_flags = hold;
+ } else {
+ newType = PepType_Type_tp_new(metatype, args, kwds);
+ }
+
+ if (!newType)
+ return nullptr;
+
+ SbkObjectTypePrivate *sotp = PepType_SOTP(newType);
+
+ const auto bases = Shiboken::getCppBaseClasses(newType);
+ if (bases.size() == 1) {
+ SbkObjectTypePrivate *parentType = PepType_SOTP(bases.front());
+ sotp->mi_offsets = parentType->mi_offsets;
+ sotp->mi_init = parentType->mi_init;
+ sotp->mi_specialcast = parentType->mi_specialcast;
+ sotp->type_discovery = parentType->type_discovery;
+ sotp->cpp_dtor = parentType->cpp_dtor;
+ sotp->is_multicpp = 0;
+ sotp->converter = parentType->converter;
+ } else {
+ sotp->mi_offsets = nullptr;
+ sotp->mi_init = nullptr;
+ sotp->mi_specialcast = nullptr;
+ sotp->type_discovery = nullptr;
+ sotp->cpp_dtor = nullptr;
+ sotp->is_multicpp = 1;
+ sotp->converter = nullptr;
+ }
+ if (bases.size() == 1) {
+ const char *original_name = PepType_SOTP(bases.front())->original_name;
+ if (original_name == nullptr)
+ original_name = "object";
+ sotp->original_name = strdup(original_name);
+ }
+ else
+ sotp->original_name = strdup("object");
+ sotp->user_data = nullptr;
+ sotp->d_func = nullptr;
+ sotp->is_user_type = 1;
+
+ // PYSIDE-1463: Prevent feature switching while in the creation process
+ auto saveFeature = initSelectableFeature(nullptr);
+ for (PyTypeObject *base : bases) {
+ sotp = PepType_SOTP(base);
+ if (sotp->subtype_init)
+ sotp->subtype_init(newType, args, kwds);
+ }
+ initSelectableFeature(saveFeature);
+ return newType;
+}
+
+static PyObject *_setupNew(PyObject *obSelf, PyTypeObject *subtype)
+{
+ auto *obSubtype = reinterpret_cast<PyObject *>(subtype);
+ auto *sbkSubtype = subtype;
+ auto *self = reinterpret_cast<SbkObject *>(obSelf);
+
+ Py_INCREF(obSubtype);
+ auto d = new SbkObjectPrivate;
+
+ auto *sotp = PepType_SOTP(sbkSubtype);
+ int numBases = ((sotp && sotp->is_multicpp) ?
+ Shiboken::getNumberOfCppBaseClasses(subtype) : 1);
+ d->cptr = new void *[numBases];
+ std::memset(d->cptr, 0, sizeof(void *) *size_t(numBases));
+ d->hasOwnership = 1;
+ d->containsCppWrapper = 0;
+ d->validCppObject = 0;
+ d->parentInfo = nullptr;
+ d->referredObjects = nullptr;
+ d->cppObjectCreated = 0;
+ d->isQAppSingleton = 0;
+ self->ob_dict = nullptr;
+ self->weakreflist = nullptr;
+ self->d = d;
+ PyObject_GC_Track(obSelf);
+ return obSelf;
+}
+
+PyObject *SbkObject_tp_new(PyTypeObject *subtype, PyObject * /* args */, PyObject * /* kwds */)
+{
+ PyObject *self = _Sbk_NewVarObject(subtype);
+ return _setupNew(self, subtype);
+}
+
+PyObject *SbkQApp_tp_new(PyTypeObject *subtype, PyObject *, PyObject *)
+{
+ auto *obSelf = MakeQAppWrapper(subtype);
+ auto *self = reinterpret_cast<SbkObject *>(obSelf);
+ if (self == nullptr)
+ return nullptr;
+ auto ret = _setupNew(obSelf, subtype);
+ auto priv = self->d;
+ priv->isQAppSingleton = 1;
+ return ret;
+}
+
+PyObject *SbkDummyNew(PyTypeObject *type, PyObject *, PyObject *)
+{
+ // PYSIDE-595: Give the same error as type_call does when tp_new is NULL.
+ const char regret[] = "¯\\_(ツ)_/¯";
+ PyErr_Format(PyExc_TypeError,
+ "cannot create '%.100s' instances %s", type->tp_name, regret);
+ return nullptr;
+}
+
+// PYSIDE-74: Fallback used in all types now.
+PyObject *FallbackRichCompare(PyObject *self, PyObject *other, int op)
+{
+ // This is a very simple implementation that supplies a simple identity.
+ static const char * const opstrings[] = {"<", "<=", "==", "!=", ">", ">="};
+ PyObject *res;
+
+ switch (op) {
+
+ case Py_EQ:
+ res = (self == other) ? Py_True : Py_False;
+ break;
+ case Py_NE:
+ res = (self != other) ? Py_True : Py_False;
+ break;
+ default:
+ PyErr_Format(PyExc_TypeError,
+ "'%s' not supported between instances of '%.100s' and '%.100s'",
+ opstrings[op],
+ self->ob_type->tp_name,
+ other->ob_type->tp_name);
+ return nullptr;
+ }
+ Py_INCREF(res);
+ return res;
+}
+
+bool SbkObjectType_Check(PyTypeObject *type)
+{
+ static auto *meta = SbkObjectType_TypeF();
+ return Py_TYPE(type) == meta || PyType_IsSubtype(Py_TYPE(type), meta);
+}
+
+} //extern "C"
+
+
+namespace
+{
+
+void _destroyParentInfo(SbkObject *obj, bool keepReference)
+{
+ Shiboken::ParentInfo *pInfo = obj->d->parentInfo;
+ if (pInfo) {
+ while(!pInfo->children.empty()) {
+ SbkObject *first = *pInfo->children.begin();
+ // Mark child as invalid
+ Shiboken::Object::invalidate(first);
+ Shiboken::Object::removeParent(first, false, keepReference);
+ }
+ Shiboken::Object::removeParent(obj, false);
+ }
+}
+
+}
+
+namespace Shiboken
+{
+
+// Wrapper metatype and base type ----------------------------------------------------------
+
+void _initMainThreadId(); // helper.cpp
+
+static std::string msgFailedToInitializeType(const char *description)
+{
+ std::ostringstream stream;
+ stream << "[libshiboken] Failed to initialize " << description;
+ if (auto *error = PepErr_GetRaisedException()) {
+ if (auto *str = PyObject_Str(error))
+ stream << ": " << Shiboken::String::toCString(str);
+ Py_DECREF(error);
+ }
+ stream << '.';
+ return stream.str();
+}
+
+namespace Conversions { void init(); }
+
+void init()
+{
+ static bool shibokenAlreadInitialised = false;
+ if (shibokenAlreadInitialised)
+ return;
+
+ _initMainThreadId();
+
+ Conversions::init();
+
+ //Init private data
+ Pep384_Init();
+
+ auto *type = SbkObjectType_TypeF();
+ if (type == nullptr || PyType_Ready(type) < 0)
+ Py_FatalError(msgFailedToInitializeType("Shiboken.BaseWrapperType metatype").c_str());
+
+ type = SbkObject_TypeF();
+ if (type == nullptr || PyType_Ready(type) < 0)
+ Py_FatalError(msgFailedToInitializeType("Shiboken.BaseWrapper type").c_str());
+
+ VoidPtr::init();
+
+ shibokenAlreadInitialised = true;
+}
+
+// PYSIDE-1415: Publish Shiboken objects.
+// PYSIDE-1735: Initialize the whole Shiboken startup.
+void initShibokenSupport(PyObject *module)
+{
+ Py_INCREF(SbkObject_TypeF());
+ PyModule_AddObject(module, "Object", reinterpret_cast<PyObject *>(SbkObject_TypeF()));
+
+ // PYSIDE-1735: When the initialization was moved into Shiboken import, this
+ // Py_INCREF became necessary. No idea why.
+ Py_INCREF(module);
+ init_shibokensupport_module();
+
+ auto *type = SbkObject_TypeF();
+ if (InitSignatureStrings(type, SbkObject_SignatureStrings) < 0)
+ Py_FatalError("Error in initShibokenSupport");
+}
+
+// setErrorAboutWrongArguments now gets overload info from the signature module.
+// Info can be nullptr and contains extra info.
+void setErrorAboutWrongArguments(PyObject *args, const char *funcName, PyObject *info)
+{
+ SetError_Argument(args, funcName, info);
+}
+
+PyObject *returnWrongArguments(PyObject *args, const char *funcName, PyObject *info)
+{
+ setErrorAboutWrongArguments(args, funcName, info);
+ return {};
+}
+
+int returnWrongArguments_Zero(PyObject *args, const char *funcName, PyObject *info)
+{
+ setErrorAboutWrongArguments(args, funcName, info);
+ return 0;
+}
+
+int returnWrongArguments_MinusOne(PyObject *args, const char *funcName, PyObject *info)
+{
+ setErrorAboutWrongArguments(args, funcName, info);
+ return -1;
+}
+
+PyObject *returnFromRichCompare(PyObject *result)
+{
+ if (result && !PyErr_Occurred())
+ return result;
+ Shiboken::Errors::setOperatorNotImplemented();
+ return {};
+}
+
+PyObject *checkInvalidArgumentCount(Py_ssize_t numArgs, Py_ssize_t minArgs, Py_ssize_t maxArgs)
+{
+ PyObject *result = nullptr;
+ // for seterror_argument(), signature/errorhandler.py
+ if (numArgs > maxArgs) {
+ static PyObject *const tooMany = Shiboken::String::createStaticString(">");
+ result = tooMany;
+ Py_INCREF(result);
+ } else if (numArgs < minArgs) {
+ static PyObject *const tooFew = Shiboken::String::createStaticString("<");
+ static PyObject *const noArgs = Shiboken::String::createStaticString("0");
+ result = numArgs > 0 ? tooFew : noArgs;
+ Py_INCREF(result);
+ }
+ return result;
+}
+
+std::vector<SbkObject *> splitPyObject(PyObject *pyObj)
+{
+ std::vector<SbkObject *> result;
+ if (PySequence_Check(pyObj)) {
+ AutoDecRef lst(PySequence_Fast(pyObj, "Invalid keep reference object."));
+ if (!lst.isNull()) {
+ for (Py_ssize_t i = 0, i_max = PySequence_Fast_GET_SIZE(lst.object()); i < i_max; ++i) {
+ PyObject *item = PySequence_Fast_GET_ITEM(lst.object(), i);
+ if (Object::checkType(item))
+ result.push_back(reinterpret_cast<SbkObject *>(item));
+ }
+ }
+ } else {
+ result.push_back(reinterpret_cast<SbkObject *>(pyObj));
+ }
+ return result;
+}
+
+template <class Iterator>
+inline void decRefPyObjectList(Iterator i1, Iterator i2)
+{
+ for (; i1 != i2; ++i1)
+ Py_DECREF(i1->second);
+}
+
+namespace ObjectType
+{
+
+bool checkType(PyTypeObject *type)
+{
+ return PyType_IsSubtype(type, SbkObject_TypeF()) != 0;
+}
+
+bool isUserType(PyTypeObject *type)
+{
+ return checkType(type) && PepType_SOTP(type)->is_user_type;
+}
+
+bool canCallConstructor(PyTypeObject *myType, PyTypeObject *ctorType)
+{
+ auto findBasePred = [ctorType](PyTypeObject *type) { return type == ctorType; };
+ if (!walkThroughBases(myType, findBasePred)) {
+ PyErr_Format(PyExc_TypeError, "%s isn't a direct base class of %s", ctorType->tp_name, myType->tp_name);
+ return false;
+ }
+ return true;
+}
+
+bool hasCast(PyTypeObject *type)
+{
+ return PepType_SOTP(type)->mi_specialcast != nullptr;
+}
+
+void *cast(PyTypeObject *sourceType, SbkObject *obj, PyTypeObject *pyTargetType)
+{
+ auto *sotp = PepType_SOTP(sourceType);
+ return sotp->mi_specialcast(Object::cppPointer(obj, pyTargetType), pyTargetType);
+}
+
+void setCastFunction(PyTypeObject *type, SpecialCastFunction func)
+{
+ auto *sotp = PepType_SOTP(type);
+ sotp->mi_specialcast = func;
+}
+
+void setOriginalName(PyTypeObject *type, const char *name)
+{
+ auto *sotp = PepType_SOTP(type);
+ if (sotp->original_name)
+ free(sotp->original_name);
+ sotp->original_name = strdup(name);
+}
+
+const char *getOriginalName(PyTypeObject *type)
+{
+ return PepType_SOTP(type)->original_name;
+}
+
+void setTypeDiscoveryFunctionV2(PyTypeObject *type, TypeDiscoveryFuncV2 func)
+{
+ PepType_SOTP(type)->type_discovery = func;
+}
+
+void copyMultipleInheritance(PyTypeObject *type, PyTypeObject *other)
+{
+ auto *sotp_type = PepType_SOTP(type);
+ auto *sotp_other = PepType_SOTP(other);
+ sotp_type->mi_init = sotp_other->mi_init;
+ sotp_type->mi_offsets = sotp_other->mi_offsets;
+ sotp_type->mi_specialcast = sotp_other->mi_specialcast;
+}
+
+void setMultipleInheritanceFunction(PyTypeObject *type, MultipleInheritanceInitFunction function)
+{
+ PepType_SOTP(type)->mi_init = function;
+}
+
+MultipleInheritanceInitFunction getMultipleInheritanceFunction(PyTypeObject *type)
+{
+ return PepType_SOTP(type)->mi_init;
+}
+
+void setDestructorFunction(PyTypeObject *type, ObjectDestructor func)
+{
+ PepType_SOTP(type)->cpp_dtor = func;
+}
+
+PyTypeObject *
+introduceWrapperType(PyObject *enclosingObject,
+ const char *typeName,
+ const char *originalName,
+ PyType_Spec *typeSpec,
+ ObjectDestructor cppObjDtor,
+ PyObject *bases,
+ unsigned wrapperFlags)
+{
+ const auto basesSize = PySequence_Fast_GET_SIZE(bases);
+ assert(basesSize > 0);
+ typeSpec->slots[0].pfunc = PySequence_Fast_GET_ITEM(bases, 0);
+
+ auto *type = SbkType_FromSpecBasesMeta(typeSpec, bases, SbkObjectType_TypeF());
+
+ auto sotp = PepType_SOTP(type);
+ if (wrapperFlags & DeleteInMainThread)
+ sotp->delete_in_main_thread = 1;
+ sotp->type_behaviour = (wrapperFlags & Value) != 0
+ ? BEHAVIOUR_VALUETYPE : BEHAVIOUR_OBJECTTYPE;
+
+ setOriginalName(type, originalName);
+ setDestructorFunction(type, cppObjDtor);
+ auto *ob_type = reinterpret_cast<PyObject *>(type);
+
+ if (wrapperFlags & InnerClass) {
+ // PYSIDE-2230: Instead of tp_dict, use the enclosing type.
+ // This stays interface compatible.
+ if (PyType_Check(enclosingObject)) {
+ AutoDecRef tpDict(PepType_GetDict(reinterpret_cast<PyTypeObject *>(enclosingObject)));
+ return PyDict_SetItemString(tpDict, typeName, ob_type) == 0 ? type : nullptr;
+ }
+ assert(PyDict_Check(enclosingObject));
+ return PyDict_SetItemString(enclosingObject, typeName, ob_type) == 0 ? type : nullptr;
+ }
+
+ // PyModule_AddObject steals type's reference.
+ Py_INCREF(ob_type);
+ if (PyModule_AddObject(enclosingObject, typeName, ob_type) != 0) {
+ std::cerr << "Warning: " << __FUNCTION__ << " returns nullptr for "
+ << typeName << '/' << originalName << " due to PyModule_AddObject(enclosingObject="
+ << enclosingObject << ", ob_type=" << ob_type << ") failing\n";
+ return nullptr;
+ }
+ return type;
+}
+
+void setSubTypeInitHook(PyTypeObject *type, SubTypeInitHook func)
+{
+ assert(SbkObjectType_Check(type));
+ PepType_SOTP(type)->subtype_init = func;
+}
+
+void *getTypeUserData(PyTypeObject *type)
+{
+ assert(SbkObjectType_Check(type));
+ return PepType_SOTP(type)->user_data;
+}
+
+void setTypeUserData(PyTypeObject *type, void *userData, DeleteUserDataFunc d_func)
+{
+ assert(SbkObjectType_Check(type));
+ auto *sotp = PepType_SOTP(type);
+ sotp->user_data = userData;
+ sotp->d_func = d_func;
+}
+
+// Try to find the exact type of cptr.
+PyTypeObject *typeForTypeName(const char *typeName)
+{
+ PyTypeObject *result{};
+ if (typeName) {
+ if (PyTypeObject *pyType = Shiboken::Conversions::getPythonTypeObject(typeName))
+ result = pyType;
+ }
+ return result;
+}
+
+bool hasSpecialCastFunction(PyTypeObject *sbkType)
+{
+ const auto *d = PepType_SOTP(sbkType);
+ return d != nullptr && d->mi_specialcast != nullptr;
+}
+
+// Find whether base is a direct single line base class of type
+// (no multiple inheritance), that is, a C++ pointer cast can safely be done.
+static bool isDirectAncestor(PyTypeObject *type, PyTypeObject *base)
+{
+ if (type == base)
+ return true;
+ if (PyTuple_Size(type->tp_bases) == 0)
+ return false;
+ auto *sbkObjectType = SbkObject_TypeF();
+ auto *firstBase = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(type->tp_bases, 0));
+ return firstBase != sbkObjectType
+ && PyType_IsSubtype(type, sbkObjectType) != 0
+ && isDirectAncestor(firstBase, base);
+}
+
+bool canDowncastTo(PyTypeObject *baseType, PyTypeObject *targetType)
+{
+ return isDirectAncestor(targetType, baseType);
+}
+
+} // namespace ObjectType
+
+
+namespace Object
+{
+
+static void recursive_invalidate(SbkObject *self, std::set<SbkObject *>& seen);
+
+bool checkType(PyObject *pyObj)
+{
+ return ObjectType::checkType(Py_TYPE(pyObj));
+}
+
+bool isUserType(PyObject *pyObj)
+{
+ return ObjectType::isUserType(Py_TYPE(pyObj));
+}
+
+Py_hash_t hash(PyObject *pyObj)
+{
+ assert(Shiboken::Object::checkType(pyObj));
+ return reinterpret_cast<Py_hash_t>(pyObj);
+}
+
+static void setSequenceOwnership(PyObject *pyObj, bool owner)
+{
+
+ bool has_length = true;
+
+ if (!pyObj)
+ return;
+
+ if (PySequence_Size(pyObj) < 0) {
+ PyErr_Clear();
+ has_length = false;
+ }
+
+ if (PySequence_Check(pyObj) && has_length) {
+ Py_ssize_t size = PySequence_Size(pyObj);
+ if (size > 0) {
+ const auto objs = splitPyObject(pyObj);
+ if (owner) {
+ for (SbkObject *o : objs)
+ getOwnership(o);
+ } else {
+ for (SbkObject *o : objs)
+ releaseOwnership(o);
+ }
+ }
+ } else if (Object::checkType(pyObj)) {
+ if (owner)
+ getOwnership(reinterpret_cast<SbkObject *>(pyObj));
+ else
+ releaseOwnership(reinterpret_cast<SbkObject *>(pyObj));
+ }
+}
+
+void setValidCpp(SbkObject *pyObj, bool value)
+{
+ pyObj->d->validCppObject = value;
+}
+
+void setHasCppWrapper(SbkObject *pyObj, bool value)
+{
+ pyObj->d->containsCppWrapper = value;
+}
+
+bool hasCppWrapper(SbkObject *pyObj)
+{
+ return pyObj->d->containsCppWrapper;
+}
+
+bool wasCreatedByPython(SbkObject *pyObj)
+{
+ return pyObj->d->cppObjectCreated;
+}
+
+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);
+ auto *sotp = PepType_SOTP(type);
+ if (sotp->is_multicpp) {
+ callDestructor(getDestructorEntries(pyObj));
+ } else {
+ Shiboken::ThreadStateSaver threadSaver;
+ threadSaver.save();
+ 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);
+
+ delete[] priv->cptr;
+ priv->cptr = nullptr;
+ priv->validCppObject = false;
+}
+
+bool hasOwnership(SbkObject *pyObj)
+{
+ return pyObj->d->hasOwnership;
+}
+
+void getOwnership(SbkObject *self)
+{
+ // skip if already have the ownership
+ if (self->d->hasOwnership)
+ return;
+
+ // skip if this object has parent
+ if (self->d->parentInfo && self->d->parentInfo->parent)
+ return;
+
+ // Get back the ownership
+ self->d->hasOwnership = true;
+
+ if (self->d->containsCppWrapper)
+ Py_DECREF(reinterpret_cast<PyObject *>(self)); // Remove extra ref
+ else
+ makeValid(self); // Make the object valid again
+}
+
+void getOwnership(PyObject *pyObj)
+{
+ if (pyObj)
+ setSequenceOwnership(pyObj, true);
+}
+
+void releaseOwnership(SbkObject *self)
+{
+ // skip if the ownership have already moved to c++
+ auto *selfType = Py_TYPE(self);
+ if (!self->d->hasOwnership || Shiboken::Conversions::pythonTypeIsValueType(PepType_SOTP(selfType)->converter))
+ return;
+
+ // remove object ownership
+ self->d->hasOwnership = false;
+
+ // If We have control over object life
+ if (self->d->containsCppWrapper)
+ Py_INCREF(reinterpret_cast<PyObject *>(self)); // keep the python object alive until the wrapper destructor call
+ else
+ invalidate(self); // If I do not know when this object will die We need to invalidate this to avoid use after
+}
+
+void releaseOwnership(PyObject *self)
+{
+ setSequenceOwnership(self, false);
+}
+
+/* Needed forward declarations */
+static void recursive_invalidate(PyObject *pyobj, std::set<SbkObject *>& seen);
+static void recursive_invalidate(SbkObject *self, std::set<SbkObject *> &seen);
+
+void invalidate(PyObject *pyobj)
+{
+ std::set<SbkObject *> seen;
+ recursive_invalidate(pyobj, seen);
+}
+
+void invalidate(SbkObject *self)
+{
+ std::set<SbkObject *> seen;
+ recursive_invalidate(self, seen);
+}
+
+static void recursive_invalidate(PyObject *pyobj, std::set<SbkObject *> &seen)
+{
+ const auto objs = splitPyObject(pyobj);
+ for (SbkObject *o : objs)
+ recursive_invalidate(o, seen);
+}
+
+static void recursive_invalidate(SbkObject *self, std::set<SbkObject *> &seen)
+{
+ // Skip if this object not is a valid object or if it's already been seen
+ if (!self || reinterpret_cast<PyObject *>(self) == Py_None || seen.find(self) != seen.end())
+ return;
+ seen.insert(self);
+
+ if (!self->d->containsCppWrapper) {
+ self->d->validCppObject = false; // Mark object as invalid only if this is not a wrapper class
+ BindingManager::instance().releaseWrapper(self);
+ }
+
+ // If it is a parent invalidate all children.
+ if (self->d->parentInfo) {
+ // Create a copy because this list can be changed during the process
+ ChildrenList copy = self->d->parentInfo->children;
+
+ for (SbkObject *child : copy) {
+ // invalidate the child
+ recursive_invalidate(child, seen);
+
+ // if the parent not is a wrapper class, then remove children from him, because We do not know when this object will be destroyed
+ if (!self->d->validCppObject)
+ removeParent(child, true, true);
+ }
+ }
+
+ // If has ref to other objects invalidate all
+ if (self->d->referredObjects) {
+ RefCountMap &refCountMap = *(self->d->referredObjects);
+ for (auto it = refCountMap.begin(), end = refCountMap.end(); it != end; ++it)
+ recursive_invalidate(it->second, seen);
+ }
+}
+
+void makeValid(SbkObject *self)
+{
+ // Skip if this object not is a valid object
+ if (!self || reinterpret_cast<PyObject *>(self) == Py_None || self->d->validCppObject)
+ return;
+
+ // Mark object as invalid only if this is not a wrapper class
+ self->d->validCppObject = true;
+
+ // If it is a parent make all children valid
+ if (self->d->parentInfo) {
+ for (SbkObject *child : self->d->parentInfo->children)
+ makeValid(child);
+ }
+
+ // If has ref to other objects make all valid again
+ if (self->d->referredObjects) {
+ const RefCountMap &refCountMap = *(self->d->referredObjects);
+ for (const auto &p : refCountMap) {
+ if (Shiboken::Object::checkType(p.second))
+ makeValid(reinterpret_cast<SbkObject *>(p.second));
+ }
+ }
+}
+
+void *cppPointer(SbkObject *pyObj, PyTypeObject *desiredType)
+{
+ PyTypeObject *pyType = Py_TYPE(pyObj);
+ auto *sotp = PepType_SOTP(pyType);
+ int idx = 0;
+ if (sotp->is_multicpp)
+ idx = getTypeIndexOnHierarchy(pyType, desiredType);
+ if (pyObj->d->cptr)
+ return pyObj->d->cptr[idx];
+ return nullptr;
+}
+
+std::vector<void *> cppPointers(SbkObject *pyObj)
+{
+ int n = getNumberOfCppBaseClasses(Py_TYPE(pyObj));
+ std::vector<void *> ptrs(n);
+ for (int i = 0; i < n; ++i)
+ ptrs[i] = pyObj->d->cptr[i];
+ return ptrs;
+}
+
+
+bool setCppPointer(SbkObject *sbkObj, PyTypeObject *desiredType, void *cptr)
+{
+ PyTypeObject *type = Py_TYPE(sbkObj);
+ int idx = 0;
+ if (PepType_SOTP(type)->is_multicpp)
+ idx = getTypeIndexOnHierarchy(type, desiredType);
+
+ const bool alreadyInitialized = sbkObj->d->cptr[idx] != nullptr;
+ if (alreadyInitialized)
+ PyErr_Format(PyExc_RuntimeError, "You can't initialize an %s object in class %s twice!",
+ desiredType->tp_name, type->tp_name);
+ else
+ sbkObj->d->cptr[idx] = cptr;
+
+ sbkObj->d->cppObjectCreated = true;
+ return !alreadyInitialized;
+}
+
+bool isValid(PyObject *pyObj)
+{
+ if (!pyObj || pyObj == Py_None
+ || PyType_Check(pyObj) != 0
+ || Py_TYPE(Py_TYPE(pyObj)) != SbkObjectType_TypeF()) {
+ return true;
+ }
+
+ auto priv = reinterpret_cast<SbkObject *>(pyObj)->d;
+
+ if (!priv->cppObjectCreated && isUserType(pyObj)) {
+ PyErr_Format(PyExc_RuntimeError, "'__init__' method of object's base class (%s) not called.",
+ Py_TYPE(pyObj)->tp_name);
+ return false;
+ }
+
+ if (!priv->validCppObject) {
+ PyErr_Format(PyExc_RuntimeError, "Internal C++ object (%s) already deleted.",
+ Py_TYPE(pyObj)->tp_name);
+ return false;
+ }
+
+ return true;
+}
+
+bool isValid(SbkObject *pyObj, bool throwPyError)
+{
+ if (!pyObj)
+ return false;
+
+ SbkObjectPrivate *priv = pyObj->d;
+ if (!priv->cppObjectCreated && isUserType(reinterpret_cast<PyObject *>(pyObj))) {
+ if (throwPyError)
+ PyErr_Format(PyExc_RuntimeError, "Base constructor of the object (%s) not called.",
+ Py_TYPE(pyObj)->tp_name);
+ return false;
+ }
+
+ if (!priv->validCppObject) {
+ if (throwPyError)
+ PyErr_Format(PyExc_RuntimeError, "Internal C++ object (%s) already deleted.",
+ (Py_TYPE(pyObj))->tp_name);
+ return false;
+ }
+
+ return true;
+}
+
+bool isValid(PyObject *pyObj, bool throwPyError)
+{
+ if (!pyObj || pyObj == Py_None ||
+ !PyType_IsSubtype(Py_TYPE(pyObj), SbkObject_TypeF())) {
+ return true;
+ }
+ return isValid(reinterpret_cast<SbkObject *>(pyObj), throwPyError);
+}
+
+SbkObject *findColocatedChild(SbkObject *wrapper,
+ const PyTypeObject *instanceType)
+{
+ // Degenerate case, wrapper is the correct wrapper.
+ if (reinterpret_cast<const void *>(Py_TYPE(wrapper)) == reinterpret_cast<const void *>(instanceType))
+ return wrapper;
+
+ if (!(wrapper->d && wrapper->d->cptr))
+ return nullptr;
+
+ ParentInfo *pInfo = wrapper->d->parentInfo;
+ if (!pInfo)
+ return nullptr;
+
+ ChildrenList &children = pInfo->children;
+
+ for (SbkObject *child : children) {
+ if (!(child->d && child->d->cptr))
+ continue;
+ if (child->d->cptr[0] == wrapper->d->cptr[0]) {
+ return reinterpret_cast<const void *>(Py_TYPE(child)) == reinterpret_cast<const void *>(instanceType)
+ ? child : findColocatedChild(child, instanceType);
+ }
+ }
+ return nullptr;
+}
+
+// Legacy, for compatibility only.
+PyObject *newObject(PyTypeObject *instanceType,
+ void *cptr,
+ bool hasOwnership,
+ bool isExactType,
+ const char *typeName)
+{
+ return isExactType
+ ? newObjectForType(instanceType, cptr, hasOwnership)
+ : newObjectWithHeuristics(instanceType, cptr, hasOwnership, typeName);
+}
+
+static PyObject *newObjectWithHeuristicsHelper(PyTypeObject *instanceType,
+ PyTypeObject *exactType,
+ void *cptr,
+ bool hasOwnership)
+{
+ // Try to find the exact type of cptr. For hierarchies with
+ // non-virtual destructors, typeid() will return the base name.
+ // Try type discovery in these cases.
+ if (exactType == nullptr || exactType == instanceType) {
+ auto resolved = BindingManager::instance().findDerivedType(cptr, instanceType);
+ if (resolved.first != nullptr
+ && Shiboken::ObjectType::canDowncastTo(instanceType, resolved.first)) {
+ exactType = resolved.first;
+ cptr = resolved.second;
+ }
+ }
+
+ return newObjectForType(exactType != nullptr ? exactType : instanceType,
+ cptr, hasOwnership);
+}
+
+PyObject *newObjectForPointer(PyTypeObject *instanceType,
+ void *cptr,
+ bool hasOwnership,
+ const char *typeName)
+{
+ // Try to find the exact type of cptr.
+ PyTypeObject *exactType = ObjectType::typeForTypeName(typeName);
+ // PYSIDE-868: In case of multiple inheritance, (for example,
+ // a function returning a QPaintDevice * from a QWidget *),
+ // use instance type to avoid pointer offset errors.
+ return exactType != nullptr && !Shiboken::ObjectType::canDowncastTo(instanceType, exactType)
+ ? newObjectForType(instanceType, cptr, hasOwnership)
+ : newObjectWithHeuristicsHelper(instanceType, exactType, cptr, hasOwnership);
+}
+
+
+PyObject *newObjectWithHeuristics(PyTypeObject *instanceType,
+ void *cptr,
+ bool hasOwnership,
+ const char *typeName)
+{
+ return newObjectWithHeuristicsHelper(instanceType,
+ ObjectType::typeForTypeName(typeName),
+ cptr, hasOwnership);
+}
+
+PyObject *newObjectForType(PyTypeObject *instanceType, void *cptr, bool hasOwnership)
+{
+ bool shouldCreate = true;
+ bool shouldRegister = true;
+ SbkObject *self = nullptr;
+
+ // Some logic to ensure that colocated child field does not overwrite the parent
+ if (BindingManager::instance().hasWrapper(cptr)) {
+ SbkObject *existingWrapper = BindingManager::instance().retrieveWrapper(cptr);
+
+ self = findColocatedChild(existingWrapper, instanceType);
+ if (self) {
+ // Wrapper already registered for cptr.
+ // This should not ideally happen, binding code should know when a wrapper
+ // already exists and retrieve it instead.
+ shouldRegister = shouldCreate = false;
+ } else if (hasOwnership &&
+ (!(Shiboken::Object::hasCppWrapper(existingWrapper) ||
+ Shiboken::Object::hasOwnership(existingWrapper)))) {
+ // Old wrapper is likely junk, since we have ownership and it doesn't.
+ BindingManager::instance().releaseWrapper(existingWrapper);
+ } else {
+ // Old wrapper may be junk caused by some bug in identifying object deletion
+ // but it may not be junk when a colocated field is accessed for an
+ // object which was not created by python (returned from c++ factory function).
+ // Hence we cannot release the wrapper confidently so we do not register.
+ shouldRegister = false;
+ }
+ }
+
+ if (shouldCreate) {
+ self = reinterpret_cast<SbkObject *>(SbkObject_tp_new(instanceType, nullptr, nullptr));
+ self->d->cptr[0] = cptr;
+ self->d->hasOwnership = hasOwnership;
+ self->d->validCppObject = 1;
+ if (shouldRegister) {
+ BindingManager::instance().registerWrapper(self, cptr);
+ }
+ } else {
+ Py_IncRef(reinterpret_cast<PyObject *>(self));
+ }
+ return reinterpret_cast<PyObject *>(self);
+}
+
+void destroy(SbkObject *self, void *cppData)
+{
+ // Skip if this is called with NULL pointer this can happen in derived classes
+ if (!self)
+ return;
+
+ // This can be called in c++ side
+ Shiboken::GilState gil;
+
+ // Remove all references attached to this object
+ clearReferences(self);
+
+ // Remove the object from parent control
+
+ // Verify if this object has parent
+ bool hasParent = (self->d->parentInfo && self->d->parentInfo->parent);
+
+ if (self->d->parentInfo) {
+ // Check for children information and make all invalid if they exists
+ _destroyParentInfo(self, true);
+ // If this object has parent then the pyobject can be invalid now, because we remove the last ref after remove from parent
+ }
+
+ //if !hasParent this object could still alive
+ if (!hasParent && self->d->containsCppWrapper && !self->d->hasOwnership) {
+ // Remove extra ref used by c++ object this will case the pyobject destruction
+ // This can cause the object death
+ Py_DECREF(reinterpret_cast<PyObject *>(self));
+ }
+
+ //Python Object is not destroyed yet
+ if (cppData && Shiboken::BindingManager::instance().hasWrapper(cppData)) {
+ // Remove from BindingManager
+ Shiboken::BindingManager::instance().releaseWrapper(self);
+ self->d->hasOwnership = false;
+
+ // the cpp object instance was deleted
+ delete[] self->d->cptr;
+ self->d->cptr = nullptr;
+ }
+
+ // After this point the object can be death do not use the self pointer bellow
+}
+
+void removeParent(SbkObject *child, bool giveOwnershipBack, bool keepReference)
+{
+ ParentInfo *pInfo = child->d->parentInfo;
+ if (!pInfo || !pInfo->parent) {
+ if (pInfo && pInfo->hasWrapperRef) {
+ pInfo->hasWrapperRef = false;
+ }
+ return;
+ }
+
+ ChildrenList &oldBrothers = pInfo->parent->d->parentInfo->children;
+ // Verify if this child is part of parent list
+ auto iChild = oldBrothers.find(child);
+ if (iChild == oldBrothers.end())
+ return;
+
+ oldBrothers.erase(iChild);
+
+ pInfo->parent = nullptr;
+
+ // This will keep the wrapper reference, will wait for wrapper destruction to remove that
+ if (keepReference &&
+ child->d->containsCppWrapper) {
+ //If have already a extra ref remove this one
+ if (pInfo->hasWrapperRef)
+ Py_DECREF(child);
+ else
+ pInfo->hasWrapperRef = true;
+ return;
+ }
+
+ // Transfer ownership back to Python
+ child->d->hasOwnership = giveOwnershipBack;
+
+ // Remove parent ref
+ Py_DECREF(child);
+}
+
+void setParent(PyObject *parent, PyObject *child)
+{
+ if (!child || child == Py_None || child == parent)
+ return;
+
+ /*
+ * setParent is recursive when the child is a native Python sequence, i.e. objects not binded by Shiboken
+ * like tuple and list.
+ *
+ * This "limitation" exists to fix the following problem: A class multiple inherits QObject and QString,
+ * so if you pass this class to someone that takes the ownership, we CAN'T enter in this if, but hey! QString
+ * follows the sequence protocol.
+ */
+ if (PySequence_Check(child) && !Object::checkType(child)) {
+ Shiboken::AutoDecRef seq(PySequence_Fast(child, nullptr));
+ for (Py_ssize_t i = 0, max = PySequence_Size(seq); i < max; ++i)
+ setParent(parent, PySequence_Fast_GET_ITEM(seq.object(), i));
+ return;
+ }
+
+ bool parentIsNull = !parent || parent == Py_None;
+ auto parent_ = reinterpret_cast<SbkObject *>(parent);
+ auto child_ = reinterpret_cast<SbkObject *>(child);
+
+ if (!parentIsNull) {
+ if (!parent_->d->parentInfo)
+ parent_->d->parentInfo = new ParentInfo;
+
+ // do not re-add a child
+ if (child_->d->parentInfo && (child_->d->parentInfo->parent == parent_))
+ return;
+ }
+
+ ParentInfo *pInfo = child_->d->parentInfo;
+ bool hasAnotherParent = pInfo && pInfo->parent && pInfo->parent != parent_;
+
+ //Avoid destroy child during reparent operation
+ Py_INCREF(child);
+
+ // check if we need to remove this child from the old parent
+ if (parentIsNull || hasAnotherParent)
+ removeParent(child_);
+
+ // Add the child to the new parent
+ pInfo = child_->d->parentInfo;
+ if (!parentIsNull) {
+ if (!pInfo)
+ pInfo = child_->d->parentInfo = new ParentInfo;
+
+ pInfo->parent = parent_;
+ parent_->d->parentInfo->children.insert(child_);
+
+ // Add Parent ref
+ Py_INCREF(child_);
+
+ // Remove ownership
+ child_->d->hasOwnership = false;
+ }
+
+ // Remove previous safe ref
+ Py_DECREF(child);
+}
+
+void deallocData(SbkObject *self, bool cleanup)
+{
+ // Make cleanup if this is not a wrapper otherwise this will be done on wrapper destructor
+ if(cleanup) {
+ removeParent(self);
+
+ if (self->d->parentInfo)
+ _destroyParentInfo(self, true);
+
+ clearReferences(self);
+ }
+
+ if (self->d->cptr) {
+ // Remove from BindingManager
+ Shiboken::BindingManager::instance().releaseWrapper(self);
+ delete[] self->d->cptr;
+ self->d->cptr = nullptr;
+ // delete self->d; PYSIDE-205: wrong!
+ }
+ delete self->d; // PYSIDE-205: always delete d.
+ Py_XDECREF(self->ob_dict);
+ PepExt_TypeCallFree(reinterpret_cast<PyObject *>(self));
+}
+
+void setTypeUserData(SbkObject *wrapper, void *userData, DeleteUserDataFunc d_func)
+{
+ auto *type = Py_TYPE(wrapper);
+ auto *sotp = PepType_SOTP(type);
+ if (sotp->user_data)
+ sotp->d_func(sotp->user_data);
+
+ sotp->d_func = d_func;
+ sotp->user_data = userData;
+}
+
+void *getTypeUserData(SbkObject *wrapper)
+{
+ auto *type = Py_TYPE(wrapper);
+ return PepType_SOTP(type)->user_data;
+}
+
+static inline bool isNone(const PyObject *o)
+{
+ return o == nullptr || o == Py_None;
+}
+
+static void removeRefCountKey(SbkObject *self, const char *key)
+{
+ if (self->d->referredObjects) {
+ const auto iterPair = self->d->referredObjects->equal_range(key);
+ if (iterPair.first != iterPair.second) {
+ decRefPyObjectList(iterPair.first, iterPair.second);
+ self->d->referredObjects->erase(iterPair.first, iterPair.second);
+ }
+ }
+}
+
+void keepReference(SbkObject *self, const char *key, PyObject *referredObject, bool append)
+{
+ if (isNone(referredObject)) {
+ removeRefCountKey(self, key);
+ return;
+ }
+
+ if (!self->d->referredObjects) {
+ self->d->referredObjects =
+ new Shiboken::RefCountMap{RefCountMap::value_type{key, referredObject}};
+ Py_INCREF(referredObject);
+ return;
+ }
+
+ RefCountMap &refCountMap = *(self->d->referredObjects);
+ const auto iterPair = refCountMap.equal_range(key);
+ if (std::any_of(iterPair.first, iterPair.second,
+ [referredObject](const RefCountMap::value_type &v) { return v.second == referredObject; })) {
+ return;
+ }
+
+ if (!append && iterPair.first != iterPair.second) {
+ decRefPyObjectList(iterPair.first, iterPair.second);
+ refCountMap.erase(iterPair.first, iterPair.second);
+ }
+
+ refCountMap.insert(RefCountMap::value_type{key, referredObject});
+ Py_INCREF(referredObject);
+}
+
+void removeReference(SbkObject *self, const char *key, PyObject *referredObject)
+{
+ if (!isNone(referredObject))
+ removeRefCountKey(self, key);
+}
+
+void clearReferences(SbkObject *self)
+{
+ if (!self->d->referredObjects)
+ return;
+
+ RefCountMap &refCountMap = *(self->d->referredObjects);
+ for (auto it = refCountMap.begin(), end = refCountMap.end(); it != end; ++it)
+ Py_DECREF(it->second);
+ self->d->referredObjects->clear();
+}
+
+// Helpers for debug / info formatting
+
+static std::vector<PyTypeObject *> getBases(SbkObject *self)
+{
+ return ObjectType::isUserType(Py_TYPE(self))
+ ? getCppBaseClasses(Py_TYPE(self))
+ : std::vector<PyTypeObject *>(1, Py_TYPE(self));
+}
+
+static bool isValueType(SbkObject *self)
+{
+ return PepType_SOTP(Py_TYPE(self))->type_behaviour == BEHAVIOUR_VALUETYPE;
+}
+
+void _debugFormat(std::ostream &s, SbkObject *self)
+{
+ assert(self);
+ auto *d = self->d;
+ if (!d) {
+ s << "[Invalid]";
+ return;
+ }
+ if (d->cptr) {
+ const std::vector<PyTypeObject *> bases = getBases(self);
+ for (size_t i = 0, size = bases.size(); i < size; ++i)
+ s << ", C++: " << bases[i]->tp_name << '/' << self->d->cptr[i];
+ } else {
+ s << " [Deleted]";
+ }
+ if (d->hasOwnership)
+ s << " [hasOwnership]";
+ if (d->containsCppWrapper)
+ s << " [containsCppWrapper]";
+ if (d->validCppObject)
+ s << " [validCppObject]";
+ if (d->cppObjectCreated)
+ s << " [wasCreatedByPython]";
+ s << (isValueType(self) ? " [value]" : " [object]");
+
+ if (d->parentInfo) {
+ if (auto *parent = d->parentInfo->parent)
+ s << ", parent=" << reinterpret_cast<PyObject *>(parent)->ob_type->tp_name
+ << '/' << parent;
+ if (!d->parentInfo->children.empty())
+ s << ", " << d->parentInfo->children.size() << " child(ren)";
+ }
+ if (d->referredObjects && !d->referredObjects->empty())
+ s << ", " << d->referredObjects->size() << " referred object(s)";
+}
+
+std::string info(SbkObject *self)
+{
+ std::ostringstream s;
+
+ if (self->d && self->d->cptr) {
+ const std::vector<PyTypeObject *> bases = getBases(self);
+
+ s << "C++ address....... ";
+ for (size_t i = 0, size = bases.size(); i < size; ++i)
+ s << bases[i]->tp_name << '/' << self->d->cptr[i] << ' ';
+ s << "\n";
+ }
+ else {
+ s << "C++ address....... <<Deleted>>\n";
+ }
+
+ s << "hasOwnership...... " << bool(self->d->hasOwnership) << "\n"
+ "containsCppWrapper " << self->d->containsCppWrapper << "\n"
+ "validCppObject.... " << self->d->validCppObject << "\n"
+ "wasCreatedByPython " << self->d->cppObjectCreated << "\n"
+ "value...... " << isValueType(self) << "\n"
+ "reference count... " << reinterpret_cast<PyObject *>(self)->ob_refcnt << '\n';
+
+ if (self->d->parentInfo && self->d->parentInfo->parent) {
+ s << "parent............ ";
+ Shiboken::AutoDecRef parent(PyObject_Str(reinterpret_cast<PyObject *>(self->d->parentInfo->parent)));
+ s << String::toCString(parent) << "\n";
+ }
+
+ if (self->d->parentInfo && !self->d->parentInfo->children.empty()) {
+ s << "children.......... ";
+ for (SbkObject *sbkChild : self->d->parentInfo->children) {
+ Shiboken::AutoDecRef child(PyObject_Str(reinterpret_cast<PyObject *>(sbkChild)));
+ s << String::toCString(child) << ' ';
+ }
+ s << '\n';
+ }
+
+ if (self->d->referredObjects && !self->d->referredObjects->empty()) {
+ const Shiboken::RefCountMap &map = *self->d->referredObjects;
+ s << "referred objects.. ";
+ std::string lastKey;
+ for (const auto &p : map) {
+ if (p.first != lastKey) {
+ if (!lastKey.empty())
+ s << " ";
+ s << '"' << p.first << "\" => ";
+ lastKey = p.first;
+ }
+ Shiboken::AutoDecRef obj(PyObject_Str(p.second));
+ s << String::toCString(obj) << ' ';
+ }
+ s << '\n';
+ }
+ return s.str();
+}
+
+} // namespace Object
+
+} // namespace Shiboken