diff options
Diffstat (limited to 'sources/shiboken6/libshiboken/basewrapper.cpp')
-rw-r--r-- | sources/shiboken6/libshiboken/basewrapper.cpp | 1780 |
1 files changed, 1780 insertions, 0 deletions
diff --git a/sources/shiboken6/libshiboken/basewrapper.cpp b/sources/shiboken6/libshiboken/basewrapper.cpp new file mode 100644 index 000000000..2f19e5bd2 --- /dev/null +++ b/sources/shiboken6/libshiboken/basewrapper.cpp @@ -0,0 +1,1780 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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$ +** +****************************************************************************/ + +#include "basewrapper.h" +#include "basewrapper_p.h" +#include "bindingmanager.h" +#include "helper.h" +#include "sbkconverter.h" +#include "sbkenum.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 "qapp_macro.h" +#include "voidptr.h" + +#include <iostream> + +#if defined(__APPLE__) +#include <dlfcn.h> +#endif + +namespace { + void _destroyParentInfo(SbkObject *obj, bool keepReference); +} + +static void callDestructor(const Shiboken::DtorAccumulatorVisitor::DestructorEntries &dts) +{ + for (const auto &e : dts) { + Shiboken::ThreadStateSaver threadSaver; + threadSaver.save(); + e.destructor(e.cppInstance); + } +} + +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)); + } + Py_TYPE(self)->tp_free(self); +} + +static void SbkObjectTypeDealloc(PyObject *pyObj); +static PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *kwds); + +static SelectableFeatureHook SelectFeatureSet = nullptr; + +static PyObject *Sbk_TypeGet___dict__(PyTypeObject *type, void *context); // forward + +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); + return PyDict_SetItem(type->tp_dict, Shiboken::PyMagicName::doc(), value); +} + +// PYSIDE-908: The function PyType_Modified does not work in PySide, so we need to +// explicitly pass __doc__. For __signature__ it _did_ actually work, because +// it was not existing before. We add them both for clarity. +static PyGetSetDef SbkObjectType_Type_getsetlist[] = { + {const_cast<char *>("__signature__"), (getter)Sbk_TypeGet___signature__}, + {const_cast<char *>("__doc__"), (getter)Sbk_TypeGet___doc__, (setter)type_set_doc}, + {const_cast<char *>("__dict__"), (getter)Sbk_TypeGet___dict__}, + {nullptr} // Sentinel +}; + +static PyObject *(*type_getattro)(PyObject *type, PyObject *name); // forward +static PyObject *mangled_type_getattro(PyTypeObject *type, PyObject *name); // forward + +static PyType_Slot SbkObjectType_Type_slots[] = { + {Py_tp_dealloc, reinterpret_cast<void *>(SbkObjectTypeDealloc)}, + {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 *>(SbkObjectTypeTpNew)}, + {Py_tp_free, reinterpret_cast<void *>(PyObject_GC_Del)}, + {Py_tp_getset, reinterpret_cast<void *>(SbkObjectType_Type_getsetlist)}, + {0, nullptr} +}; +static PyType_Spec SbkObjectType_Type_spec = { + "1:Shiboken.ObjectType", + 0, // basicsize (inserted later) + sizeof(PyMemberDef), + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + SbkObjectType_Type_slots, +}; + +PyTypeObject *SbkObjectType_TypeF(void) +{ + static PyTypeObject *type = nullptr; + if (!type) { + // PYSIDE-1019: Insert the default tp_getattro explicitly here + // so we can overwrite it a bit. + type_getattro = PyType_Type.tp_getattro; + SbkObjectType_Type_spec.basicsize = + PepHeapType_SIZE + sizeof(SbkObjectTypePrivate); + type = reinterpret_cast<PyTypeObject *>(SbkType_FromSpec(&SbkObjectType_Type_spec)); + } + return type; +} + +static PyObject *SbkObjectGetDict(PyObject *pObj, void *) +{ + auto *obj = reinterpret_cast<SbkObject *>(pObj); + if (!obj->ob_dict) + obj->ob_dict = PyDict_New(); + if (!obj->ob_dict) + return nullptr; + Py_INCREF(obj->ob_dict); + return obj->ob_dict; +} + +static PyGetSetDef SbkObjectGetSetList[] = { + {const_cast<char *>("__dict__"), SbkObjectGetDict, nullptr, nullptr, nullptr}, + {nullptr, nullptr, nullptr, nullptr, nullptr} // Sentinel +}; + +static int SbkObject_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); + return 0; +} + +static int SbkObject_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 PyObject *SbkObject_GenericGetAttr(PyObject *obj, PyObject *name); +static int SbkObject_GenericSetAttr(PyObject *obj, PyObject *name, PyObject *value); + +static 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_traverse)}, + {Py_tp_clear, reinterpret_cast<void *>(SbkObject_clear)}, + // unsupported: {Py_tp_weaklistoffset, (void *)offsetof(SbkObject, weakreflist)}, + {Py_tp_getset, reinterpret_cast<void *>(SbkObjectGetSetList)}, + // unsupported: {Py_tp_dictoffset, (void *)offsetof(SbkObject, ob_dict)}, + {0, nullptr} +}; +static PyType_Spec SbkObject_Type_spec = { + "1:Shiboken.Object", + sizeof(SbkObject), + 0, + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, + SbkObject_Type_slots, +}; + + +SbkObjectType *SbkObject_TypeF(void) +{ + static PyTypeObject *type = nullptr; + if (!type) { + type = reinterpret_cast<PyTypeObject *>(SbkType_FromSpec(&SbkObject_Type_spec)); + Py_TYPE(type) = SbkObjectType_TypeF(); + Py_INCREF(Py_TYPE(type)); + type->tp_weaklistoffset = offsetof(SbkObject, weakreflist); + type->tp_dictoffset = offsetof(SbkObject, ob_dict); + } + return reinterpret_cast<SbkObjectType *>(type); +} + +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) + bool needTypeDecref = (false + || PyType_GetSlot(pyType, Py_tp_dealloc) == SbkDeallocWrapper + || PyType_GetSlot(pyType, Py_tp_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. + // PYSIDE-571: Some objects do not use GC, so check this! + if (PyObject_IS_GC(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. + if (sbkObj->weakreflist && Py_IsInitialized()) + PyObject_ClearWeakRefs(pyObj); + + // If I have ownership and is valid delete C++ pointer + SbkObjectTypePrivate *sotp{nullptr}; + canDelete &= sbkObj->d->hasOwnership && sbkObj->d->validCppObject; + if (canDelete) { + sotp = PepType_SOTP(pyType); + if (sotp->delete_in_main_thread && Shiboken::currentThreadId() != Shiboken::mainThreadId()) { + auto &bindingManager = Shiboken::BindingManager::instance(); + if (sotp->is_multicpp) { + Shiboken::DtorAccumulatorVisitor visitor(sbkObj); + Shiboken::walkThroughClassHierarchy(Py_TYPE(pyObj), &visitor); + for (const auto &e : visitor.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) { + Shiboken::DtorAccumulatorVisitor visitor(sbkObj); + Shiboken::walkThroughClassHierarchy(Py_TYPE(pyObj), &visitor); + Shiboken::Object::deallocData(sbkObj, true); + callDestructor(visitor.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); + } +} + +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 SbkObjectTypeDealloc(PyObject *pyObj) +{ + SbkObjectTypePrivate *sotp = PepType_SOTP(pyObj); + auto type = reinterpret_cast<PyTypeObject *>(pyObj); + + PyObject_GC_UnTrack(pyObj); +#ifndef Py_LIMITED_API + Py_TRASHCAN_SAFE_BEGIN(pyObj); +#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(type)) + Shiboken::Conversions::deleteConverter(sotp->converter); + delete sotp; + sotp = nullptr; + } +#ifndef Py_LIMITED_API + Py_TRASHCAN_SAFE_END(pyObj); +#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)); + } +} + +////////////////////////////////////////////////////////////////////////////// +// +// PYSIDE-1019: Support switchable extensions +// +// We simply exchange the complete class dicts. +// +// This is done in which replaces +// --------------- -------------- +// mangled_type_getattro type_getattro +// Sbk_TypeGet___dict__ type_dict +// SbkObject_GenericGetAttr PyObject_GenericGetAttr +// SbkObject_GenericSetAttr PyObject_GenericSetAttr +// + +void initSelectableFeature(SelectableFeatureHook func) +{ + SelectFeatureSet = func; +} + +static PyObject *mangled_type_getattro(PyTypeObject *type, PyObject *name) +{ + /* + * Note: This `type_getattro` version is only the default that comes + * from `PyType_Type.tp_getattro`. This does *not* interfere in any way + * with the complex `tp_getattro` of `QObject` and other instances. + * What we change here is the meta class of `QObject`. + */ + if (SelectFeatureSet != nullptr) + type->tp_dict = SelectFeatureSet(type); + return type_getattro(reinterpret_cast<PyObject *>(type), name); +} + +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) + Py_RETURN_NONE; + if (SelectFeatureSet != nullptr) + dict = SelectFeatureSet(type); + return PyDictProxy_New(dict); +} + +// These functions replace the standard PyObject_Generic(Get|Set)Attr functions. +// They provide the default that "object" inherits. +// Everything else is directly handled by cppgenerator that calls `Feature::Select`. +static PyObject *SbkObject_GenericGetAttr(PyObject *obj, PyObject *name) +{ + auto type = Py_TYPE(obj); + if (SelectFeatureSet != nullptr) + type->tp_dict = SelectFeatureSet(type); + return PyObject_GenericGetAttr(obj, name); +} + +static int SbkObject_GenericSetAttr(PyObject *obj, PyObject *name, PyObject *value) +{ + auto type = Py_TYPE(obj); + if (SelectFeatureSet != nullptr) + type->tp_dict = SelectFeatureSet(type); + return PyObject_GenericSetAttr(obj, name, value); +} + +// Caching the select Id. +int SbkObjectType_GetReserved(PyTypeObject *type) +{ + auto ptr = PepType_SOTP(reinterpret_cast<SbkObjectType *>(type)); + // PYSIDE-1019: During import PepType_SOTP is still zero. + if (ptr == nullptr) + return -1; + return ptr->pyside_reserved_bits; +} + +void SbkObjectType_SetReserved(PyTypeObject *type, int value) +{ + PepType_SOTP(reinterpret_cast<SbkObjectType *>(type))->pyside_reserved_bits = value; +} + +const char **SbkObjectType_GetPropertyStrings(PyTypeObject *type) +{ + return PepType_SOTP(type)->propertyStrings; +} + +void SbkObjectType_SetPropertyStrings(PyTypeObject *type, const char **strings) +{ + PepType_SOTP(reinterpret_cast<SbkObjectType *>(type))->propertyStrings = strings; +} + +// +////////////////////////////////////////////////////////////////////////////// + +static PyObject *SbkObjectTypeTpNew(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, "sO!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 (reinterpret_cast<PyTypeObject *>(baseType)->tp_new == SbkDummyNew) { + // PYSIDE-595: A base class does not allow inheritance. + return SbkDummyNew(metatype, args, kwds); + } + } + + // The meta type creates a new type when the Python programmer extends a wrapped C++ class. + auto type_new = reinterpret_cast<newfunc>(PyType_Type.tp_new); + + // PYSIDE-939: This is a temporary patch that circumvents the problem + // with Py_TPFLAGS_METHOD_DESCRIPTOR until this is finally solved. + // PyType_Ready uses mro(). We need to temporarily remove the flag from it's type. + // We cannot use PyMethodDescr_Type since it is not exported by Python 2.7 . + static PyTypeObject *PyMethodDescr_TypePtr = Py_TYPE( + PyObject_GetAttr(reinterpret_cast<PyObject *>(&PyType_Type), Shiboken::PyName::mro())); + auto hold = PyMethodDescr_TypePtr->tp_flags; + PyMethodDescr_TypePtr->tp_flags &= ~Py_TPFLAGS_METHOD_DESCRIPTOR; + auto *newType = reinterpret_cast<SbkObjectType *>(type_new(metatype, args, kwds)); + PyMethodDescr_TypePtr->tp_flags = hold; + + if (!newType) + return nullptr; + + Shiboken::ObjectType::initPrivateData(newType); + SbkObjectTypePrivate *sotp = PepType_SOTP(newType); + + const auto bases = Shiboken::getCppBaseClasses(reinterpret_cast<PyTypeObject *>(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) + sotp->original_name = strdup(PepType_SOTP(bases.front())->original_name); + else + sotp->original_name = strdup("object"); + sotp->user_data = nullptr; + sotp->d_func = nullptr; + sotp->is_user_type = 1; + + for (SbkObjectType *base : bases) { + if (PepType_SOTP(base)->subtype_init) + PepType_SOTP(base)->subtype_init(newType, args, kwds); + } + return reinterpret_cast<PyObject *>(newType); +} + +static PyObject *_setupNew(SbkObject *self, PyTypeObject *subtype) +{ + Py_INCREF(reinterpret_cast<PyObject *>(subtype)); + auto d = new SbkObjectPrivate; + + SbkObjectTypePrivate *sotp = PepType_SOTP(subtype); + 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; + self->ob_dict = nullptr; + self->weakreflist = nullptr; + self->d = d; + 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; +} + +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 + auto self = reinterpret_cast<SbkObject *>(MakeQAppWrapper(subtype)); + return self == nullptr ? nullptr : _setupNew(self, subtype); +} + +PyObject *SbkDummyNew(PyTypeObject *type, PyObject *, PyObject *) +{ + // PYSIDE-595: Give the same error as type_call does when tp_new is NULL. + PyErr_Format(PyExc_TypeError, + "cannot create '%.100s' instances ¯\\_(ツ)_/¯", + type->tp_name); + return nullptr; +} + +PyObject *SbkType_FromSpec(PyType_Spec *spec) +{ + return SbkType_FromSpecWithBases(spec, nullptr); +} + +PyObject *SbkType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) +{ + // PYSIDE-1286: Generate correct __module__ and __qualname__ + // The name field can now be extended by an "n:" prefix which is + // the number of modules in the name. The default is 1. + // + // Example: + // "2:mainmod.submod.mainclass.subclass" + // results in + // __module__ : "mainmod.submod" + // __qualname__ : "mainclass.subclass" + // __name__ : "subclass" + + PyType_Spec new_spec = *spec; + const char *colon = strchr(spec->name, ':'); + assert(colon); + int package_level = atoi(spec->name); + const char *mod = new_spec.name = colon + 1; + + PyObject *type = PyType_FromSpecWithBases(&new_spec, bases); + if (type == nullptr) + return nullptr; + + const char *qual = mod; + for (int idx = package_level; idx > 0; --idx) { + const char *dot = strchr(qual, '.'); + if (!dot) + break; + qual = dot + 1; + } + int mlen = qual - mod - 1; + Shiboken::AutoDecRef module(Shiboken::String::fromCString(mod, mlen)); + Shiboken::AutoDecRef qualname(Shiboken::String::fromCString(qual)); + if (PyObject_SetAttr(type, Shiboken::PyMagicName::module(), module) < 0) + return nullptr; + if (PyObject_SetAttr(type, Shiboken::PyMagicName::qualname(), qualname) < 0) + return nullptr; + return type; +} + +// 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 NULL; + } + Py_INCREF(res); + return res; +} + +} //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 +{ +bool walkThroughClassHierarchy(PyTypeObject *currentType, HierarchyVisitor *visitor) +{ + PyObject *bases = currentType->tp_bases; + Py_ssize_t numBases = PyTuple_GET_SIZE(bases); + bool result = false; + for (int i = 0; !result && i < numBases; ++i) { + auto type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(bases, i)); + if (PyType_IsSubtype(type, reinterpret_cast<PyTypeObject *>(SbkObject_TypeF()))) { + auto sbkType = reinterpret_cast<SbkObjectType *>(type); + result = PepType_SOTP(sbkType)->is_user_type + ? walkThroughClassHierarchy(type, visitor) : visitor->visit(sbkType); + } + } + return result; +} + +// Wrapper metatype and base type ---------------------------------------------------------- + +HierarchyVisitor::HierarchyVisitor() = default; +HierarchyVisitor::~HierarchyVisitor() = default; + +bool BaseCountVisitor::visit(SbkObjectType *) +{ + m_count++; + return false; +} + +bool BaseAccumulatorVisitor::visit(SbkObjectType *node) +{ + m_bases.push_back(node); + return false; +} + +bool GetIndexVisitor::visit(SbkObjectType *node) +{ + m_index++; + return PyType_IsSubtype(reinterpret_cast<PyTypeObject *>(node), m_desiredType); +} + +bool DtorAccumulatorVisitor::visit(SbkObjectType *node) +{ + m_entries.push_back(DestructorEntry{PepType_SOTP(node)->cpp_dtor, + m_pyObject->d->cptr[m_entries.size()]}); + return false; +} + +void _initMainThreadId(); // helper.cpp + +namespace Conversions { void init(); } + +void init() +{ + static bool shibokenAlreadInitialised = false; + if (shibokenAlreadInitialised) + return; + + _initMainThreadId(); + + Conversions::init(); + + //Init private data + Pep384_Init(); + + Shiboken::ObjectType::initPrivateData(SbkObject_TypeF()); + + if (PyType_Ready(SbkEnumType_TypeF()) < 0) + Py_FatalError("[libshiboken] Failed to initialize Shiboken.SbkEnumType metatype."); + + if (PyType_Ready(SbkObjectType_TypeF()) < 0) + Py_FatalError("[libshiboken] Failed to initialize Shiboken.BaseWrapperType metatype."); + + if (PyType_Ready(reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())) < 0) + Py_FatalError("[libshiboken] Failed to initialize Shiboken.BaseWrapper type."); + + VoidPtr::init(); + + shibokenAlreadInitialised = true; +} + +// setErrorAboutWrongArguments now gets overload info from the signature module. +void setErrorAboutWrongArguments(PyObject *args, const char *funcName) +{ + SetError_Argument(args, funcName); +} + +class FindBaseTypeVisitor : public HierarchyVisitor +{ +public: + explicit FindBaseTypeVisitor(PyTypeObject *typeToFind) : m_typeToFind(typeToFind) {} + + bool visit(SbkObjectType *node) override + { + return reinterpret_cast<PyTypeObject *>(node) == m_typeToFind; + } + +private: + PyTypeObject *m_typeToFind; +}; + +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, reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())) != 0; +} + +bool isUserType(PyTypeObject *type) +{ + return checkType(type) && PepType_SOTP(type)->is_user_type; +} + +bool canCallConstructor(PyTypeObject *myType, PyTypeObject *ctorType) +{ + FindBaseTypeVisitor visitor(ctorType); + if (!walkThroughClassHierarchy(myType, &visitor)) { + 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(SbkObjectType *type) +{ + return PepType_SOTP(type)->mi_specialcast != nullptr; +} + +void *cast(SbkObjectType *sourceType, SbkObject *obj, PyTypeObject *targetType) +{ + return PepType_SOTP(sourceType)->mi_specialcast(Object::cppPointer(obj, targetType), + reinterpret_cast<SbkObjectType *>(targetType)); +} + +void setCastFunction(SbkObjectType *type, SpecialCastFunction func) +{ + PepType_SOTP(type)->mi_specialcast = func; +} + +void setOriginalName(SbkObjectType *type, const char *name) +{ + SbkObjectTypePrivate *sotp = PepType_SOTP(type); + if (sotp->original_name) + free(sotp->original_name); + sotp->original_name = strdup(name); +} + +const char *getOriginalName(SbkObjectType *type) +{ + return PepType_SOTP(type)->original_name; +} + +void setTypeDiscoveryFunctionV2(SbkObjectType *type, TypeDiscoveryFuncV2 func) +{ + PepType_SOTP(type)->type_discovery = func; +} + +void copyMultipleInheritance(SbkObjectType *type, SbkObjectType *other) +{ + PepType_SOTP(type)->mi_init = PepType_SOTP(other)->mi_init; + PepType_SOTP(type)->mi_offsets = PepType_SOTP(other)->mi_offsets; + PepType_SOTP(type)->mi_specialcast = PepType_SOTP(other)->mi_specialcast; +} + +void setMultipleInheritanceFunction(SbkObjectType *type, MultipleInheritanceInitFunction function) +{ + PepType_SOTP(type)->mi_init = function; +} + +MultipleInheritanceInitFunction getMultipleInheritanceFunction(SbkObjectType *type) +{ + return PepType_SOTP(type)->mi_init; +} + +void setDestructorFunction(SbkObjectType *type, ObjectDestructor func) +{ + PepType_SOTP(type)->cpp_dtor = func; +} + +void initPrivateData(SbkObjectType *type) +{ + PepType_SOTP(type) = new SbkObjectTypePrivate; + memset(PepType_SOTP(type), 0, sizeof(SbkObjectTypePrivate)); +} + +SbkObjectType * +introduceWrapperType(PyObject *enclosingObject, + const char *typeName, + const char *originalName, + PyType_Spec *typeSpec, + ObjectDestructor cppObjDtor, + SbkObjectType *baseType, + PyObject *baseTypes, + unsigned wrapperFlags) +{ + typeSpec->slots[0].pfunc = reinterpret_cast<void *>(baseType ? baseType : SbkObject_TypeF()); + + PyObject *heaptype = SbkType_FromSpecWithBases(typeSpec, baseTypes); + Py_TYPE(heaptype) = SbkObjectType_TypeF(); + Py_INCREF(Py_TYPE(heaptype)); + auto *type = reinterpret_cast<SbkObjectType *>(heaptype); + if (baseType) { + if (baseTypes) { + for (int i = 0; i < PySequence_Fast_GET_SIZE(baseTypes); ++i) + BindingManager::instance().addClassInheritance(reinterpret_cast<SbkObjectType *>(PySequence_Fast_GET_ITEM(baseTypes, i)), type); + } else { + BindingManager::instance().addClassInheritance(baseType, type); + } + } + if (PyType_Ready(reinterpret_cast<PyTypeObject *>(type)) < 0) + return nullptr; + + initPrivateData(type); + auto sotp = PepType_SOTP(type); + if (wrapperFlags & DeleteInMainThread) + sotp->delete_in_main_thread = 1; + + setOriginalName(type, originalName); + setDestructorFunction(type, cppObjDtor); + auto *ob_type = reinterpret_cast<PyObject *>(type); + + if (wrapperFlags & InnerClass) + 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(SbkObjectType *type, SubTypeInitHook func) +{ + PepType_SOTP(type)->subtype_init = func; +} + +void *getTypeUserData(SbkObjectType *type) +{ + return PepType_SOTP(type)->user_data; +} + +void setTypeUserData(SbkObjectType *type, void *userData, DeleteUserDataFunc d_func) +{ + SbkObjectTypePrivate *sotp = PepType_SOTP(type); + sotp->user_data = userData; + sotp->d_func = d_func; +} + +// Try to find the exact type of cptr. +SbkObjectType *typeForTypeName(const char *typeName) +{ + SbkObjectType *result{}; + if (typeName) { + if (PyTypeObject *pyType = Shiboken::Conversions::getPythonTypeObject(typeName)) + result = reinterpret_cast<SbkObjectType *>(pyType); + } + return result; +} + +bool hasSpecialCastFunction(SbkObjectType *sbkType) +{ + const SbkObjectTypePrivate *d = PepType_SOTP(sbkType); + return d != nullptr && d->mi_specialcast != nullptr; +} + +} // 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) +{ + PyTypeObject *type = Py_TYPE(pyObj); + SbkObjectTypePrivate *sotp = PepType_SOTP(type); + if (sotp->is_multicpp) { + Shiboken::DtorAccumulatorVisitor visitor(pyObj); + Shiboken::walkThroughClassHierarchy(type, &visitor); + callDestructor(visitor.entries()); + } else { + Shiboken::ThreadStateSaver threadSaver; + threadSaver.save(); + sotp->cpp_dtor(pyObj->d->cptr[0]); + } + + /* 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; +} + +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 = reinterpret_cast<SbkObjectType *>(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) { + RefCountMap &refCountMap = *(self->d->referredObjects); + RefCountMap::iterator iter; + for (auto it = refCountMap.begin(), end = refCountMap.end(); it != end; ++it) { + if (Shiboken::Object::checkType(it->second)) + makeValid(reinterpret_cast<SbkObject *>(it->second)); + } + } +} + +void *cppPointer(SbkObject *pyObj, PyTypeObject *desiredType) +{ + PyTypeObject *type = Py_TYPE(pyObj); + int idx = 0; + if (PepType_SOTP(reinterpret_cast<SbkObjectType *>(type))->is_multicpp) + idx = getTypeIndexOnHierarchy(type, 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) +{ + int idx = 0; + PyTypeObject *type = Py_TYPE(sbkObj); + if (PepType_SOTP(type)->is_multicpp) + idx = getTypeIndexOnHierarchy(type, desiredType); + + const bool alreadyInitialized = sbkObj->d->cptr[idx] != nullptr; + if (alreadyInitialized) + PyErr_SetString(PyExc_RuntimeError, "You can't initialize an object twice!"); + else + sbkObj->d->cptr[idx] = cptr; + + sbkObj->d->cppObjectCreated = true; + return !alreadyInitialized; +} + +bool isValid(PyObject *pyObj) +{ + if (!pyObj || pyObj == Py_None + || 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), reinterpret_cast<PyTypeObject *>(SbkObject_TypeF()))) { + return true; + } + return isValid(reinterpret_cast<SbkObject *>(pyObj), throwPyError); +} + +SbkObject *findColocatedChild(SbkObject *wrapper, + const SbkObjectType *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; +} + +PyObject *newObject(SbkObjectType *instanceType, + void *cptr, + bool hasOwnership, + bool isExactType, + const char *typeName) +{ + // Try to find the exact type of cptr. + if (!isExactType) { + if (SbkObjectType *exactType = ObjectType::typeForTypeName(typeName)) + instanceType = exactType; + else + instanceType = BindingManager::instance().resolveType(&cptr, instanceType); + } + + 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 *>(SbkObjectTpNew(reinterpret_cast<PyTypeObject *>(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); + + // PYSIDE-571: qApp is no longer allocated. + if (PyObject_IS_GC(reinterpret_cast<PyObject *>(self))) + Py_TYPE(self)->tp_free(self); +} + +void setTypeUserData(SbkObject *wrapper, void *userData, DeleteUserDataFunc d_func) +{ + SbkObjectTypePrivate *sotp = PepType_SOTP(Py_TYPE(wrapper)); + if (sotp->user_data) + sotp->d_func(sotp->user_data); + + sotp->d_func = d_func; + sotp->user_data = userData; +} + +void *getTypeUserData(SbkObject *wrapper) +{ + return PepType_SOTP(Py_TYPE(wrapper))->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(); +} + +std::string info(SbkObject *self) +{ + std::ostringstream s; + + if (self->d && self->d->cptr) { + std::vector<SbkObjectType *> bases; + if (ObjectType::isUserType(Py_TYPE(self))) + bases = getCppBaseClasses(Py_TYPE(self)); + else + bases.push_back(reinterpret_cast<SbkObjectType *>(Py_TYPE(self))); + + s << "C++ address....... "; + for (size_t i = 0, size = bases.size(); i < size; ++i) { + auto base = reinterpret_cast<PyTypeObject *>(bases[i]); + s << base->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"; + + + 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()) { + Shiboken::RefCountMap &map = *self->d->referredObjects; + s << "referred objects.. "; + std::string lastKey; + for (auto it = map.begin(), end = map.end(); it != end; ++it) { + if (it->first != lastKey) { + if (!lastKey.empty()) + s << " "; + s << '"' << it->first << "\" => "; + lastKey = it->first; + } + Shiboken::AutoDecRef obj(PyObject_Str(it->second)); + s << String::toCString(obj) << ' '; + } + s << '\n'; + } + return s.str(); +} + +} // namespace Object + +} // namespace Shiboken |