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.cpp528
1 files changed, 352 insertions, 176 deletions
diff --git a/sources/shiboken6/libshiboken/basewrapper.cpp b/sources/shiboken6/libshiboken/basewrapper.cpp
index d81160eea..0ce80d0c6 100644
--- a/sources/shiboken6/libshiboken/basewrapper.cpp
+++ b/sources/shiboken6/libshiboken/basewrapper.cpp
@@ -5,9 +5,12 @@
#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"
@@ -24,7 +27,9 @@
#include "signature_p.h"
#include "voidptr.h"
+#include <string>
#include <iostream>
+#include <sstream>
#if defined(__APPLE__)
#include <dlfcn.h>
@@ -34,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;
@@ -43,6 +114,8 @@ static void callDestructor(const Shiboken::DtorAccumulatorVisitor::DestructorEnt
}
}
+} // namespace Shiboken
+
extern "C"
{
@@ -54,7 +127,7 @@ 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 SbkObjectType_tp_dealloc(PyTypeObject *pyType);
@@ -71,6 +144,7 @@ void setDestroyQApplication(DestroyQAppHook func)
// PYSIDE-535: Use the C API in PyPy instead of `op->ob_dict`, directly
LIBSHIBOKEN_API PyObject *SbkObject_GetDict_NoRef(PyObject *op)
{
+ assert(Shiboken::Object::checkType(op));
#ifdef PYPY_VERSION
Shiboken::GilState state;
auto *ret = PyObject_GenericGetDict(op, nullptr);
@@ -109,7 +183,8 @@ 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
@@ -122,31 +197,53 @@ static PyGetSetDef SbkObjectType_tp_getset[] = {
{nullptr, nullptr, nullptr, nullptr, nullptr} // Sentinel
};
-static 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 meaning that a
-// `__len__` field must be defined. Not doing so creates a hard-to-find crash.
-static PyType_Spec SbkObjectType_Type_spec = {
- "1:Shiboken.ObjectType",
- 0,
- 0, // sizeof(PyMemberDef), not for PyPy without a __len__ defined
- 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 auto *type = SbkType_FromSpec(&SbkObjectType_Type_spec);
+ static auto *type = createObjectTypeType();
return type;
}
@@ -183,10 +280,8 @@ static int SbkObject_tp_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;
}
@@ -206,40 +301,53 @@ static int SbkObject_tp_clear(PyObject *self)
return 0;
}
-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_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}
-};
-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 const char *SbkObject_SignatureStrings[] = {
- "Shiboken.Object(self)",
- nullptr}; // Sentinel
+static PyTypeObject *createObjectType()
+{
+ PyType_Slot SbkObject_Type_slots[] = {
+ {Py_tp_getattro, reinterpret_cast<void *>(SbkObject_GenericGetAttr)},
+ {Py_tp_setattro, reinterpret_cast<void *>(SbkObject_GenericSetAttr)},
+ {Py_tp_dealloc, reinterpret_cast<void *>(SbkDeallocWrapperWithPrivateDtor)},
+ {Py_tp_traverse, reinterpret_cast<void *>(SbkObject_tp_traverse)},
+ {Py_tp_clear, reinterpret_cast<void *>(SbkObject_tp_clear)},
+ // unsupported: {Py_tp_weaklistoffset, (void *)offsetof(SbkObject, weakreflist)},
+ {Py_tp_getset, reinterpret_cast<void *>(SbkObject_tp_getset)},
+ // unsupported: {Py_tp_dictoffset, (void *)offsetof(SbkObject, ob_dict)},
+ {0, nullptr}
+ };
+
+ PyType_Spec SbkObject_Type_spec = {
+ "1:Shiboken.Object",
+ sizeof(SbkObject),
+ 0,
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC,
+ SbkObject_Type_slots,
+ };
+
+ // PYSIDE-2230: When creating this type, we cannot easily handle the metaclass.
+ // In versions < Python 3.12, the metaclass can only be set
+ // indirectly by a base which has that metaclass.
+ // But before 3.12 is the minimum version, we cannot use the new
+ // function, although we would need this for 3.12 :-D
+ // We do a special patching here that is triggered through Py_None.
+ auto *type = SbkType_FromSpec_BMDWB(&SbkObject_Type_spec,
+ Py_None, // bases, spectial flag!
+ SbkObjectType_TypeF(),
+ offsetof(SbkObject, ob_dict),
+ offsetof(SbkObject, weakreflist),
+ nullptr); // bufferprocs
+ return type;
+}
PyTypeObject *SbkObject_TypeF(void)
{
- static auto *type = SbkType_FromSpec_BMDWB(&SbkObject_Type_spec,
- nullptr, // bases
- SbkObjectType_TypeF(),
- offsetof(SbkObject, ob_dict),
- offsetof(SbkObject, weakreflist),
- nullptr); // bufferprocs
+ static auto *type = createObjectType(); // bufferprocs
return type;
}
+static const char *SbkObject_SignatureStrings[] = {
+ "Shiboken.Object(self)",
+ nullptr}; // Sentinel
+
static int mainThreadDeletionHandler(void *)
{
if (Py_IsInitialized())
@@ -302,9 +410,8 @@ static void SbkDeallocWrapperCommon(PyObject *pyObj, bool canDelete)
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]};
@@ -322,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);
@@ -385,7 +491,7 @@ void SbkObjectType_tp_dealloc(PyTypeObject *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
@@ -403,7 +509,7 @@ void SbkObjectType_tp_dealloc(PyTypeObject *sbkType)
Shiboken::Conversions::deleteConverter(sotp->converter);
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
@@ -474,7 +580,7 @@ static PyTypeObject *SbkObjectType_tp_new(PyTypeObject *metatype, PyObject *args
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))
@@ -482,22 +588,26 @@ static PyTypeObject *SbkObjectType_tp_new(PyTypeObject *metatype, PyObject *args
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 reinterpret_cast<PyTypeObject *>(SbkDummyNew(metatype, args, kwds));
}
}
- // 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 = PepType_Type_tp_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;
@@ -595,9 +705,9 @@ PyObject *SbkQApp_tp_new(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;
}
@@ -628,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"
@@ -652,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()))) {
- result = PepType_SOTP(type)->is_user_type
- ? walkThroughClassHierarchy(type, visitor) : visitor->visit(type);
- }
- }
- return result;
-}
// Wrapper metatype and base type ----------------------------------------------------------
-HierarchyVisitor::HierarchyVisitor() = default;
-HierarchyVisitor::~HierarchyVisitor() = default;
-
-bool BaseCountVisitor::visit(PyTypeObject *)
-{
- m_count++;
- return false;
-}
-
-bool BaseAccumulatorVisitor::visit(PyTypeObject *node)
-{
- m_bases.push_back(node);
- return false;
-}
-
-bool GetIndexVisitor::visit(PyTypeObject *node)
-{
- m_index++;
- return PyType_IsSubtype(node, m_desiredType);
-}
+void _initMainThreadId(); // helper.cpp
-bool DtorAccumulatorVisitor::visit(PyTypeObject *node)
+static std::string msgFailedToInitializeType(const char *description)
{
- auto *sotp = PepType_SOTP(node);
- m_entries.push_back(DestructorEntry{sotp->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()
@@ -715,14 +801,13 @@ void init()
//Init private data
Pep384_Init();
- if (PyType_Ready(SbkEnumType_TypeF()) < 0)
- Py_FatalError("[libshiboken] Failed to initialize Shiboken.SbkEnumType metatype.");
-
- if (PyType_Ready(SbkObjectType_TypeF()) < 0)
- Py_FatalError("[libshiboken] Failed to initialize Shiboken.BaseWrapperType metatype.");
+ auto *type = SbkObjectType_TypeF();
+ if (type == nullptr || PyType_Ready(type) < 0)
+ Py_FatalError(msgFailedToInitializeType("Shiboken.BaseWrapperType metatype").c_str());
- if (PyType_Ready(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();
@@ -753,6 +838,32 @@ void setErrorAboutWrongArguments(PyObject *args, const char *funcName, PyObject
SetError_Argument(args, funcName, info);
}
+PyObject *returnWrongArguments(PyObject *args, const char *funcName, PyObject *info)
+{
+ setErrorAboutWrongArguments(args, funcName, info);
+ return {};
+}
+
+int returnWrongArguments_Zero(PyObject *args, const char *funcName, PyObject *info)
+{
+ setErrorAboutWrongArguments(args, funcName, info);
+ return 0;
+}
+
+int returnWrongArguments_MinusOne(PyObject *args, const char *funcName, PyObject *info)
+{
+ setErrorAboutWrongArguments(args, funcName, info);
+ return -1;
+}
+
+PyObject *returnFromRichCompare(PyObject *result)
+{
+ if (result && !PyErr_Occurred())
+ return result;
+ Shiboken::Errors::setOperatorNotImplemented();
+ return {};
+}
+
PyObject *checkInvalidArgumentCount(Py_ssize_t numArgs, Py_ssize_t minArgs, Py_ssize_t maxArgs)
{
PyObject *result = nullptr;
@@ -770,20 +881,6 @@ PyObject *checkInvalidArgumentCount(Py_ssize_t numArgs, Py_ssize_t minArgs, Py_s
return result;
}
-class FindBaseTypeVisitor : public HierarchyVisitor
-{
-public:
- explicit FindBaseTypeVisitor(PyTypeObject *typeToFind) : m_typeToFind(typeToFind) {}
-
- bool visit(PyTypeObject *node) override
- {
- return node == m_typeToFind;
- }
-
-private:
- PyTypeObject *m_typeToFind;
-};
-
std::vector<SbkObject *> splitPyObject(PyObject *pyObj)
{
std::vector<SbkObject *> result;
@@ -824,8 +921,8 @@ 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;
}
@@ -897,31 +994,34 @@ introduceWrapperType(PyObject *enclosingObject,
const char *originalName,
PyType_Spec *typeSpec,
ObjectDestructor cppObjDtor,
- PyTypeObject *baseType,
- PyObject *baseTypes,
+ PyObject *bases,
unsigned wrapperFlags)
{
- auto *base = baseType ? baseType : SbkObject_TypeF();
- typeSpec->slots[0].pfunc = reinterpret_cast<void *>(base);
- auto *bases = baseTypes ? baseTypes : PyTuple_Pack(1, base);
+ assert(PySequence_Fast_GET_SIZE(bases) > 0);
+ typeSpec->slots[0].pfunc = PySequence_Fast_GET_ITEM(bases, 0);
auto *type = SbkType_FromSpecBasesMeta(typeSpec, bases, SbkObjectType_TypeF());
- for (int i = 0; i < PySequence_Fast_GET_SIZE(bases); ++i) {
- auto *st = reinterpret_cast<PyTypeObject *>(PySequence_Fast_GET_ITEM(bases, i));
- BindingManager::instance().addClassInheritance(st, 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);
@@ -936,16 +1036,19 @@ introduceWrapperType(PyObject *enclosingObject,
void setSubTypeInitHook(PyTypeObject *type, SubTypeInitHook func)
{
+ assert(SbkObjectType_Check(type));
PepType_SOTP(type)->subtype_init = func;
}
void *getTypeUserData(PyTypeObject *type)
{
+ assert(SbkObjectType_Check(type));
return PepType_SOTP(type)->user_data;
}
void setTypeUserData(PyTypeObject *type, void *userData, DeleteUserDataFunc d_func)
{
+ assert(SbkObjectType_Check(type));
auto *sotp = PepType_SOTP(type);
sotp->user_data = userData;
sotp->d_func = d_func;
@@ -968,6 +1071,26 @@ bool hasSpecialCastFunction(PyTypeObject *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
@@ -1056,9 +1179,7 @@ void callCppDestructors(SbkObject *pyObj)
PyTypeObject *type = Py_TYPE(pyObj);
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();
@@ -1207,11 +1328,10 @@ 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));
}
}
}
@@ -1247,7 +1367,8 @@ bool setCppPointer(SbkObject *sbkObj, PyTypeObject *desiredType, void *cptr)
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;
@@ -1339,20 +1460,67 @@ SbkObject *findColocatedChild(SbkObject *wrapper,
return nullptr;
}
+// 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 (PyTypeObject *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;
@@ -1561,7 +1729,7 @@ void deallocData(SbkObject *self, bool cleanup)
}
delete self->d; // PYSIDE-205: always delete d.
Py_XDECREF(self->ob_dict);
- Py_TYPE(self)->tp_free(self);
+ PepExt_TypeCallFree(reinterpret_cast<PyObject *>(self));
}
void setTypeUserData(SbkObject *wrapper, void *userData, DeleteUserDataFunc d_func)
@@ -1653,6 +1821,11 @@ static std::vector<PyTypeObject *> getBases(SbkObject *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);
@@ -1676,6 +1849,8 @@ void _debugFormat(std::ostream &s, SbkObject *self)
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
@@ -1706,8 +1881,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............ ";
@@ -1725,17 +1901,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';