aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/libshiboken/basewrapper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/libshiboken/basewrapper.cpp')
-rw-r--r--sources/shiboken6/libshiboken/basewrapper.cpp1074
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';