diff options
Diffstat (limited to 'sources/shiboken6/libshiboken/basewrapper.cpp')
-rw-r--r-- | sources/shiboken6/libshiboken/basewrapper.cpp | 1074 |
1 files changed, 582 insertions, 492 deletions
diff --git a/sources/shiboken6/libshiboken/basewrapper.cpp b/sources/shiboken6/libshiboken/basewrapper.cpp index 7f927523a..a79a4cb69 100644 --- a/sources/shiboken6/libshiboken/basewrapper.cpp +++ b/sources/shiboken6/libshiboken/basewrapper.cpp @@ -1,48 +1,16 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// 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" @@ -56,9 +24,12 @@ #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> @@ -68,7 +39,73 @@ namespace { void _destroyParentInfo(SbkObject *obj, bool keepReference); } -static void callDestructor(const Shiboken::DtorAccumulatorVisitor::DestructorEntries &dts) +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; @@ -77,6 +114,8 @@ static void callDestructor(const Shiboken::DtorAccumulatorVisitor::DestructorEnt } } +} // namespace Shiboken + extern "C" { @@ -88,13 +127,12 @@ void Sbk_object_dealloc(PyObject *self) // This was not needed before Python 3.8 (Python issue 35810) Py_DECREF(Py_TYPE(self)); } - Py_TYPE(self)->tp_free(self); + PepExt_TypeCallFree(self); } -static void SbkObjectTypeDealloc(PyObject *pyObj); -static PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *kwds); +static void SbkObjectType_tp_dealloc(PyTypeObject *pyType); +static PyTypeObject *SbkObjectType_tp_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds); -static SelectableFeatureHook SelectFeatureSet = nullptr; static DestroyQAppHook DestroyQApplication = nullptr; // PYSIDE-1470: Provide a hook to kill an Application from Shiboken. @@ -103,7 +141,24 @@ void setDestroyQApplication(DestroyQAppHook func) DestroyQApplication = func; } -static PyObject *Sbk_TypeGet___dict__(PyTypeObject *type, void *context); // forward +// 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) @@ -123,76 +178,88 @@ check_set_special_type_attr(PyTypeObject *type, PyObject *value, const char *nam // PYSIDE-1177: Add a setter to allow setting type doc. static int -type_set_doc(PyTypeObject *type, PyObject *value, void *context) +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); + 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__. 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 +// 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 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, -}; +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 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)); - } + static auto *type = createObjectTypeType(); 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; + auto ret = SbkObject_GetDict_NoRef(pObj); + Py_XINCREF(ret); + return ret; } -static PyGetSetDef SbkObjectGetSetList[] = { +static PyGetSetDef SbkObject_tp_getset[] = { {const_cast<char *>("__dict__"), SbkObjectGetDict, nullptr, nullptr, nullptr}, {nullptr, nullptr, nullptr, nullptr, nullptr} // Sentinel }; -static int SbkObject_traverse(PyObject *self, visitproc visit, void *arg) +static int SbkObject_tp_traverse(PyObject *self, visitproc visit, void *arg) { auto *sbkSelf = reinterpret_cast<SbkObject *>(self); @@ -213,14 +280,12 @@ static int SbkObject_traverse(PyObject *self, visitproc visit, void *arg) if (sbkSelf->ob_dict) Py_VISIT(sbkSelf->ob_dict); -#if PY_VERSION_HEX >= 0x03090000 // This was not needed before Python 3.9 (Python issue 35810 and 40217) Py_VISIT(Py_TYPE(self)); -#endif return 0; } -static int SbkObject_clear(PyObject *self) +static int SbkObject_tp_clear(PyObject *self) { auto *sbkSelf = reinterpret_cast<SbkObject *>(self); @@ -236,42 +301,53 @@ static int SbkObject_clear(PyObject *self) 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, -}; - +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; +} -SbkObjectType *SbkObject_TypeF(void) +PyTypeObject *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 auto *type = createObjectType(); // bufferprocs + return type; } +static const char *SbkObject_SignatureStrings[] = { + "Shiboken.Object(self)", + nullptr}; // Sentinel + static int mainThreadDeletionHandler(void *) { if (Py_IsInitialized()) @@ -287,9 +363,9 @@ static void SbkDeallocWrapperCommon(PyObject *pyObj, bool canDelete) // 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); + 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 @@ -328,16 +404,14 @@ static void SbkDeallocWrapperCommon(PyObject *pyObj, bool canDelete) PyObject_ClearWeakRefs(pyObj); // If I have ownership and is valid delete C++ pointer - SbkObjectTypePrivate *sotp{nullptr}; + auto *sotp = PepType_SOTP(pyType); 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()) + 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]}; @@ -355,10 +429,9 @@ static void SbkDeallocWrapperCommon(PyObject *pyObj, bool canDelete) if (canDelete) { if (sotp->is_multicpp) { - Shiboken::DtorAccumulatorVisitor visitor(sbkObj); - Shiboken::walkThroughClassHierarchy(Py_TYPE(pyObj), &visitor); + const auto entries = Shiboken::getDestructorEntries(sbkObj); Shiboken::Object::deallocData(sbkObj, true); - callDestructor(visitor.entries()); + callDestructor(entries); } else { void *cptr = sbkObj->d->cptr[0]; Shiboken::Object::deallocData(sbkObj, true); @@ -384,6 +457,17 @@ static void SbkDeallocWrapperCommon(PyObject *pyObj, bool canDelete) } } +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); @@ -401,14 +485,18 @@ void SbkDeallocWrapperWithPrivateDtor(PyObject *self) SbkDeallocWrapperCommon(self, false); } -void SbkObjectTypeDealloc(PyObject *pyObj) +void SbkObjectType_tp_dealloc(PyTypeObject *sbkType) { - SbkObjectTypePrivate *sotp = PepType_SOTP(pyObj); - auto type = reinterpret_cast<PyTypeObject *>(pyObj); + SbkObjectTypePrivate *sotp = PepType_SOTP(sbkType); + auto pyObj = reinterpret_cast<PyObject *>(sbkType); PyObject_GC_UnTrack(pyObj); -#ifndef Py_LIMITED_API +#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) { @@ -417,13 +505,16 @@ void SbkObjectTypeDealloc(PyObject *pyObj) } free(sotp->original_name); sotp->original_name = nullptr; - if (!Shiboken::ObjectType::isUserType(type)) + if (!Shiboken::ObjectType::isUserType(sbkType)) Shiboken::Conversions::deleteConverter(sotp->converter); - delete sotp; - sotp = nullptr; + PepType_SOTP_delete(sbkType); } -#ifndef Py_LIMITED_API +#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. @@ -447,7 +538,8 @@ PyObject *MakeQAppWrapper(PyTypeObject *type) // protecting from multiple application instances if (!(type == nullptr || qApp_last == Py_None)) { - const char *res_name = PepType_GetNameStr(Py_TYPE(qApp_last)); + 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); @@ -455,7 +547,7 @@ PyObject *MakeQAppWrapper(PyTypeObject *type) } // monitoring the last application state - PyObject *qApp_curr = type != nullptr ? PyObject_GC_New(PyObject, type) : Py_None; + 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; @@ -465,109 +557,14 @@ PyObject *MakeQAppWrapper(PyTypeObject *type) // exactly the needed reference that keeps qApp alive from alone! Py_INCREF(qApp_curr); // PYSIDE-1470: As a side effect, the interactive "_" variable tends to - // create reference cycles. It was found when using gc.collect(). But using - // PyGC_collect() inside the C code had no effect in the interactive shell. - // The cycle exists only in the eval loop of the interpreter! - if (PyDict_GetItem(builtins, Shiboken::PyName::underscore())) - PyDict_SetItem(builtins, Shiboken::PyName::underscore(), Py_None); + // 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; } -////////////////////////////////////////////////////////////////////////////// -// -// 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 -// - -SelectableFeatureHook initSelectableFeature(SelectableFeatureHook func) -{ - auto ret = SelectFeatureSet; - SelectFeatureSet = func; - return ret; -} - -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 == nullptr) - 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) +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 @@ -583,7 +580,7 @@ static PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyOb PyObject *dict; static const char *kwlist[] = { "name", "bases", "dict", nullptr}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO!O!:sbktype", const_cast<char **>(kwlist), + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO!O!:sbktype", const_cast<char **>(kwlist), &name, &PyTuple_Type, &pyBases, &PyDict_Type, &dict)) @@ -591,33 +588,33 @@ static PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyOb 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) { + if (PepExt_Type_GetNewSlot(reinterpret_cast<PyTypeObject *>(baseType)) == SbkDummyNew) { // PYSIDE-595: A base class does not allow inheritance. - return SbkDummyNew(metatype, args, kwds); + return reinterpret_cast<PyTypeObject *>(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; + // 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; - Shiboken::ObjectType::initPrivateData(newType); SbkObjectTypePrivate *sotp = PepType_SOTP(newType); - const auto bases = Shiboken::getCppBaseClasses(reinterpret_cast<PyTypeObject *>(newType)); + const auto bases = Shiboken::getCppBaseClasses(newType); if (bases.size() == 1) { SbkObjectTypePrivate *parentType = PepType_SOTP(bases.front()); sotp->mi_offsets = parentType->mi_offsets; @@ -636,8 +633,12 @@ static PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyOb sotp->is_multicpp = 1; sotp->converter = nullptr; } - if (bases.size() == 1) - sotp->original_name = strdup(PepType_SOTP(bases.front())->original_name); + 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; @@ -646,20 +647,25 @@ static PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyOb // PYSIDE-1463: Prevent feature switching while in the creation process auto saveFeature = initSelectableFeature(nullptr); - for (SbkObjectType *base : bases) { - if (PepType_SOTP(base)->subtype_init) - PepType_SOTP(base)->subtype_init(newType, args, kwds); + for (PyTypeObject *base : bases) { + sotp = PepType_SOTP(base); + if (sotp->subtype_init) + sotp->subtype_init(newType, args, kwds); } initSelectableFeature(saveFeature); - return reinterpret_cast<PyObject *>(newType); + return newType; } -static PyObject *_setupNew(SbkObject *self, PyTypeObject *subtype) +static PyObject *_setupNew(PyObject *obSelf, PyTypeObject *subtype) { - Py_INCREF(reinterpret_cast<PyObject *>(subtype)); + auto *obSubtype = reinterpret_cast<PyObject *>(subtype); + auto *sbkSubtype = subtype; + auto *self = reinterpret_cast<SbkObject *>(obSelf); + + Py_INCREF(obSubtype); auto d = new SbkObjectPrivate; - SbkObjectTypePrivate *sotp = PepType_SOTP(subtype); + auto *sotp = PepType_SOTP(sbkSubtype); int numBases = ((sotp && sotp->is_multicpp) ? Shiboken::getNumberOfCppBaseClasses(subtype) : 1); d->cptr = new void *[numBases]; @@ -670,25 +676,27 @@ static PyObject *_setupNew(SbkObject *self, PyTypeObject *subtype) 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(reinterpret_cast<PyObject *>(self)); - return reinterpret_cast<PyObject *>(self); + PyObject_GC_Track(obSelf); + return obSelf; } -PyObject *SbkObjectTpNew(PyTypeObject *subtype, PyObject *, PyObject *) +PyObject *SbkObject_tp_new(PyTypeObject *subtype, PyObject * /* args */, PyObject * /* kwds */) { - SbkObject *self = PyObject_GC_New(SbkObject, subtype); + PyObject *self = _Sbk_NewVarObject(subtype); return _setupNew(self, subtype); } -PyObject *SbkQAppTpNew(PyTypeObject *subtype, PyObject *, PyObject *) +PyObject *SbkQApp_tp_new(PyTypeObject *subtype, PyObject *, PyObject *) { - auto self = reinterpret_cast<SbkObject *>(MakeQAppWrapper(subtype)); + auto *obSelf = MakeQAppWrapper(subtype); + auto *self = reinterpret_cast<SbkObject *>(obSelf); if (self == nullptr) return nullptr; - auto ret = _setupNew(self, subtype); + auto ret = _setupNew(obSelf, subtype); auto priv = self->d; priv->isQAppSingleton = 1; return ret; @@ -697,57 +705,12 @@ PyObject *SbkQAppTpNew(PyTypeObject *subtype, PyObject *, PyObject *) 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 ¯\\_(ツ)_/¯", - type->tp_name); + "cannot create '%.100s' instances %s", type->tp_name, regret); 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) { @@ -775,6 +738,12 @@ PyObject *FallbackRichCompare(PyObject *self, PyObject *other, int op) 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" @@ -799,54 +768,24 @@ void _destroyParentInfo(SbkObject *obj, bool keepReference) 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); -} +void _initMainThreadId(); // helper.cpp -bool DtorAccumulatorVisitor::visit(SbkObjectType *node) +static std::string msgFailedToInitializeType(const char *description) { - m_entries.push_back(DestructorEntry{PepType_SOTP(node)->cpp_dtor, - m_pyObject->d->cptr[m_entries.size()]}); - return false; + 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(); } -void _initMainThreadId(); // helper.cpp - namespace Conversions { void init(); } void init() @@ -862,22 +801,36 @@ void 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."); + auto *type = SbkObjectType_TypeF(); + if (type == nullptr || PyType_Ready(type) < 0) + Py_FatalError(msgFailedToInitializeType("Shiboken.BaseWrapperType metatype").c_str()); - 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."); + 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) @@ -885,19 +838,48 @@ void setErrorAboutWrongArguments(PyObject *args, const char *funcName, PyObject SetError_Argument(args, funcName, info); } -class FindBaseTypeVisitor : public HierarchyVisitor +PyObject *returnWrongArguments(PyObject *args, const char *funcName, PyObject *info) { -public: - explicit FindBaseTypeVisitor(PyTypeObject *typeToFind) : m_typeToFind(typeToFind) {} + setErrorAboutWrongArguments(args, funcName, info); + return {}; +} - bool visit(SbkObjectType *node) override - { - return reinterpret_cast<PyTypeObject *>(node) == m_typeToFind; - } +int returnWrongArguments_Zero(PyObject *args, const char *funcName, PyObject *info) +{ + setErrorAboutWrongArguments(args, funcName, info); + return 0; +} -private: - PyTypeObject *m_typeToFind; -}; +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) { @@ -929,7 +911,7 @@ namespace ObjectType bool checkType(PyTypeObject *type) { - return PyType_IsSubtype(type, reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())) != 0; + return PyType_IsSubtype(type, SbkObject_TypeF()) != 0; } bool isUserType(PyTypeObject *type) @@ -939,160 +921,177 @@ bool isUserType(PyTypeObject *type) bool canCallConstructor(PyTypeObject *myType, PyTypeObject *ctorType) { - FindBaseTypeVisitor visitor(ctorType); - if (!walkThroughClassHierarchy(myType, &visitor)) { + 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(SbkObjectType *type) +bool hasCast(PyTypeObject *type) { return PepType_SOTP(type)->mi_specialcast != nullptr; } -void *cast(SbkObjectType *sourceType, SbkObject *obj, PyTypeObject *targetType) +void *cast(PyTypeObject *sourceType, SbkObject *obj, PyTypeObject *pyTargetType) { - return PepType_SOTP(sourceType)->mi_specialcast(Object::cppPointer(obj, targetType), - reinterpret_cast<SbkObjectType *>(targetType)); + auto *sotp = PepType_SOTP(sourceType); + return sotp->mi_specialcast(Object::cppPointer(obj, pyTargetType), pyTargetType); } -void setCastFunction(SbkObjectType *type, SpecialCastFunction func) +void setCastFunction(PyTypeObject *type, SpecialCastFunction func) { - PepType_SOTP(type)->mi_specialcast = func; + auto *sotp = PepType_SOTP(type); + sotp->mi_specialcast = func; } -void setOriginalName(SbkObjectType *type, const char *name) +void setOriginalName(PyTypeObject *type, const char *name) { - SbkObjectTypePrivate *sotp = PepType_SOTP(type); + auto *sotp = PepType_SOTP(type); if (sotp->original_name) free(sotp->original_name); sotp->original_name = strdup(name); } -const char *getOriginalName(SbkObjectType *type) +const char *getOriginalName(PyTypeObject *type) { return PepType_SOTP(type)->original_name; } -void setTypeDiscoveryFunctionV2(SbkObjectType *type, TypeDiscoveryFuncV2 func) +void setTypeDiscoveryFunctionV2(PyTypeObject *type, TypeDiscoveryFuncV2 func) { PepType_SOTP(type)->type_discovery = func; } -void copyMultipleInheritance(SbkObjectType *type, SbkObjectType *other) +void copyMultipleInheritance(PyTypeObject *type, PyTypeObject *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; + 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(SbkObjectType *type, MultipleInheritanceInitFunction function) +void setMultipleInheritanceFunction(PyTypeObject *type, MultipleInheritanceInitFunction function) { PepType_SOTP(type)->mi_init = function; } -MultipleInheritanceInitFunction getMultipleInheritanceFunction(SbkObjectType *type) +MultipleInheritanceInitFunction getMultipleInheritanceFunction(PyTypeObject *type) { return PepType_SOTP(type)->mi_init; } -void setDestructorFunction(SbkObjectType *type, ObjectDestructor func) +void setDestructorFunction(PyTypeObject *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 * +PyTypeObject * introduceWrapperType(PyObject *enclosingObject, const char *typeName, const char *originalName, PyType_Spec *typeSpec, ObjectDestructor cppObjDtor, - SbkObjectType *baseType, - PyObject *baseTypes, + PyObject *bases, unsigned wrapperFlags) { - typeSpec->slots[0].pfunc = reinterpret_cast<void *>(baseType ? baseType : SbkObject_TypeF()); + const auto basesSize = PySequence_Fast_GET_SIZE(bases); + assert(basesSize > 0); + typeSpec->slots[0].pfunc = PySequence_Fast_GET_ITEM(bases, 0); - 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; + auto *type = SbkType_FromSpecBasesMeta(typeSpec, bases, SbkObjectType_TypeF()); - initPrivateData(type); 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) + 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"; + << enclosingObject << ", ob_type=" << ob_type << ") failing\n"; return nullptr; } return type; } -void setSubTypeInitHook(SbkObjectType *type, SubTypeInitHook func) +void setSubTypeInitHook(PyTypeObject *type, SubTypeInitHook func) { + assert(SbkObjectType_Check(type)); PepType_SOTP(type)->subtype_init = func; } -void *getTypeUserData(SbkObjectType *type) +void *getTypeUserData(PyTypeObject *type) { + assert(SbkObjectType_Check(type)); return PepType_SOTP(type)->user_data; } -void setTypeUserData(SbkObjectType *type, void *userData, DeleteUserDataFunc d_func) +void setTypeUserData(PyTypeObject *type, void *userData, DeleteUserDataFunc d_func) { - SbkObjectTypePrivate *sotp = PepType_SOTP(type); + 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. -SbkObjectType *typeForTypeName(const char *typeName) +PyTypeObject *typeForTypeName(const char *typeName) { - SbkObjectType *result{}; + PyTypeObject *result{}; if (typeName) { if (PyTypeObject *pyType = Shiboken::Conversions::getPythonTypeObject(typeName)) - result = reinterpret_cast<SbkObjectType *>(pyType); + result = pyType; } return result; } -bool hasSpecialCastFunction(SbkObjectType *sbkType) +bool hasSpecialCastFunction(PyTypeObject *sbkType) { - const SbkObjectTypePrivate *d = PepType_SOTP(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 @@ -1179,11 +1178,9 @@ void callCppDestructors(SbkObject *pyObj) return; } PyTypeObject *type = Py_TYPE(pyObj); - SbkObjectTypePrivate *sotp = PepType_SOTP(type); + auto *sotp = PepType_SOTP(type); if (sotp->is_multicpp) { - Shiboken::DtorAccumulatorVisitor visitor(pyObj); - Shiboken::walkThroughClassHierarchy(type, &visitor); - callDestructor(visitor.entries()); + callDestructor(getDestructorEntries(pyObj)); } else { Shiboken::ThreadStateSaver threadSaver; threadSaver.save(); @@ -1238,7 +1235,7 @@ void getOwnership(PyObject *pyObj) void releaseOwnership(SbkObject *self) { // skip if the ownership have already moved to c++ - auto *selfType = reinterpret_cast<SbkObjectType *>(Py_TYPE(self)); + auto *selfType = Py_TYPE(self); if (!self->d->hasOwnership || Shiboken::Conversions::pythonTypeIsValueType(PepType_SOTP(selfType)->converter)) return; @@ -1332,21 +1329,21 @@ void makeValid(SbkObject *self) // 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)); + 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 *type = Py_TYPE(pyObj); + PyTypeObject *pyType = Py_TYPE(pyObj); + auto *sotp = PepType_SOTP(pyType); int idx = 0; - if (PepType_SOTP(reinterpret_cast<SbkObjectType *>(type))->is_multicpp) - idx = getTypeIndexOnHierarchy(type, desiredType); + if (sotp->is_multicpp) + idx = getTypeIndexOnHierarchy(pyType, desiredType); if (pyObj->d->cptr) return pyObj->d->cptr[idx]; return nullptr; @@ -1364,14 +1361,15 @@ std::vector<void *> cppPointers(SbkObject *pyObj) bool setCppPointer(SbkObject *sbkObj, PyTypeObject *desiredType, void *cptr) { - int idx = 0; 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_SetString(PyExc_RuntimeError, "You can't initialize an object twice!"); + 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; @@ -1382,6 +1380,7 @@ bool setCppPointer(SbkObject *sbkObj, PyTypeObject *desiredType, void *cptr) bool isValid(PyObject *pyObj) { if (!pyObj || pyObj == Py_None + || PyType_Check(pyObj) != 0 || Py_TYPE(Py_TYPE(pyObj)) != SbkObjectType_TypeF()) { return true; } @@ -1429,14 +1428,14 @@ bool isValid(SbkObject *pyObj, bool throwPyError) bool isValid(PyObject *pyObj, bool throwPyError) { if (!pyObj || pyObj == Py_None || - !PyType_IsSubtype(Py_TYPE(pyObj), reinterpret_cast<PyTypeObject *>(SbkObject_TypeF()))) { + !PyType_IsSubtype(Py_TYPE(pyObj), SbkObject_TypeF())) { return true; } return isValid(reinterpret_cast<SbkObject *>(pyObj), throwPyError); } SbkObject *findColocatedChild(SbkObject *wrapper, - const SbkObjectType *instanceType) + const PyTypeObject *instanceType) { // Degenerate case, wrapper is the correct wrapper. if (reinterpret_cast<const void *>(Py_TYPE(wrapper)) == reinterpret_cast<const void *>(instanceType)) @@ -1462,20 +1461,67 @@ SbkObject *findColocatedChild(SbkObject *wrapper, return nullptr; } -PyObject *newObject(SbkObjectType *instanceType, +// Legacy, for compatibility only. +PyObject *newObject(PyTypeObject *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); + 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; @@ -1505,7 +1551,7 @@ PyObject *newObject(SbkObjectType *instanceType, } if (shouldCreate) { - self = reinterpret_cast<SbkObject *>(SbkObjectTpNew(reinterpret_cast<PyTypeObject *>(instanceType), nullptr, nullptr)); + self = reinterpret_cast<SbkObject *>(SbkObject_tp_new(instanceType, nullptr, nullptr)); self->d->cptr[0] = cptr; self->d->hasOwnership = hasOwnership; self->d->validCppObject = 1; @@ -1684,15 +1730,13 @@ void deallocData(SbkObject *self, bool cleanup) } 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); + PepExt_TypeCallFree(reinterpret_cast<PyObject *>(self)); } void setTypeUserData(SbkObject *wrapper, void *userData, DeleteUserDataFunc d_func) { - SbkObjectTypePrivate *sotp = PepType_SOTP(Py_TYPE(wrapper)); + auto *type = Py_TYPE(wrapper); + auto *sotp = PepType_SOTP(type); if (sotp->user_data) sotp->d_func(sotp->user_data); @@ -1702,7 +1746,8 @@ void setTypeUserData(SbkObject *wrapper, void *userData, DeleteUserDataFunc d_fu void *getTypeUserData(SbkObject *wrapper) { - return PepType_SOTP(Py_TYPE(wrapper))->user_data; + auto *type = Py_TYPE(wrapper); + return PepType_SOTP(type)->user_data; } static inline bool isNone(const PyObject *o) @@ -1768,22 +1813,66 @@ void clearReferences(SbkObject *self) 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) { - 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))); + const std::vector<PyTypeObject *> bases = getBases(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] << ' '; - } + for (size_t i = 0, size = bases.size(); i < size; ++i) + s << bases[i]->tp_name << '/' << self->d->cptr[i] << ' '; s << "\n"; } else { @@ -1793,8 +1882,9 @@ std::string info(SbkObject *self) s << "hasOwnership...... " << bool(self->d->hasOwnership) << "\n" "containsCppWrapper " << self->d->containsCppWrapper << "\n" "validCppObject.... " << self->d->validCppObject << "\n" - "wasCreatedByPython " << self->d->cppObjectCreated << "\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............ "; @@ -1812,17 +1902,17 @@ std::string info(SbkObject *self) } if (self->d->referredObjects && !self->d->referredObjects->empty()) { - Shiboken::RefCountMap &map = *self->d->referredObjects; + const 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) { + for (const auto &p : map) { + if (p.first != lastKey) { if (!lastKey.empty()) s << " "; - s << '"' << it->first << "\" => "; - lastKey = it->first; + s << '"' << p.first << "\" => "; + lastKey = p.first; } - Shiboken::AutoDecRef obj(PyObject_Str(it->second)); + Shiboken::AutoDecRef obj(PyObject_Str(p.second)); s << String::toCString(obj) << ' '; } s << '\n'; |