From 18dc31becdd994c53a9f894087cf1ef99fbd0232 Mon Sep 17 00:00:00 2001 From: Christian Tismer Date: Sun, 17 Dec 2017 19:12:56 +0100 Subject: PEP 384-squash: Implement PEP 384 This is the condensed checkin of 18 commits which created the implementation of PEP 384. Task-number: PYSIDE-560 Change-Id: I834c659af4c2b55b268f8e8dc4cfa53f02502409 Reviewed-by: Qt CI Bot Reviewed-by: Alexandru Croitor --- sources/shiboken2/libshiboken/CMakeLists.txt | 6 + sources/shiboken2/libshiboken/autodecref.h | 2 + sources/shiboken2/libshiboken/basewrapper.cpp | 500 +++++------ sources/shiboken2/libshiboken/basewrapper.h | 38 +- sources/shiboken2/libshiboken/bindingmanager.cpp | 29 +- sources/shiboken2/libshiboken/bufferprocs27.cpp | 397 +++++++++ sources/shiboken2/libshiboken/bufferprocs27.h | 144 ++++ sources/shiboken2/libshiboken/pep384impl.cpp | 924 +++++++++++++++++++++ sources/shiboken2/libshiboken/pep384impl.h | 571 +++++++++++++ sources/shiboken2/libshiboken/python25compat.h | 2 +- sources/shiboken2/libshiboken/qapp_macro.cpp | 4 +- sources/shiboken2/libshiboken/qt_attribution.json | 12 + .../shiboken2/libshiboken/sbkarrayconverter.cpp | 2 +- sources/shiboken2/libshiboken/sbkconverter.cpp | 50 +- sources/shiboken2/libshiboken/sbkconverter.h | 23 +- sources/shiboken2/libshiboken/sbkenum.cpp | 375 +++++---- sources/shiboken2/libshiboken/sbkenum.h | 11 +- sources/shiboken2/libshiboken/sbkpython.h | 11 +- sources/shiboken2/libshiboken/sbkstring.cpp | 4 +- sources/shiboken2/libshiboken/sbkversion.h.in | 3 + sources/shiboken2/libshiboken/shibokenbuffer.cpp | 4 +- sources/shiboken2/libshiboken/signature.cpp | 181 +++- .../tmp-referencetopython/sbkconverter.cpp | 2 +- .../tmp-referencetopython/sbkconverter.h | 2 +- sources/shiboken2/libshiboken/typespec.cpp | 776 +++++++++++++++++ sources/shiboken2/libshiboken/typespec.h | 153 ++++ sources/shiboken2/libshiboken/voidptr.cpp | 166 +--- sources/shiboken2/libshiboken/voidptr.h | 4 +- 28 files changed, 3738 insertions(+), 658 deletions(-) create mode 100644 sources/shiboken2/libshiboken/bufferprocs27.cpp create mode 100644 sources/shiboken2/libshiboken/bufferprocs27.h create mode 100644 sources/shiboken2/libshiboken/pep384impl.cpp create mode 100644 sources/shiboken2/libshiboken/pep384impl.h create mode 100644 sources/shiboken2/libshiboken/qt_attribution.json create mode 100644 sources/shiboken2/libshiboken/typespec.cpp create mode 100644 sources/shiboken2/libshiboken/typespec.h (limited to 'sources/shiboken2/libshiboken') diff --git a/sources/shiboken2/libshiboken/CMakeLists.txt b/sources/shiboken2/libshiboken/CMakeLists.txt index e87cf07fd..b5ba78e15 100644 --- a/sources/shiboken2/libshiboken/CMakeLists.txt +++ b/sources/shiboken2/libshiboken/CMakeLists.txt @@ -48,7 +48,10 @@ threadstatesaver.cpp shibokenbuffer.cpp signature.cpp qapp_macro.cpp +pep384impl.cpp voidptr.cpp +typespec.cpp +bufferprocs27.cpp ) get_numpy_location() @@ -89,9 +92,12 @@ install(FILES threadstatesaver.h shibokenbuffer.h sbkpython.h + pep384impl.h signature.h qapp_macro.h voidptr.h + typespec.h + bufferprocs27.h "${CMAKE_CURRENT_BINARY_DIR}/sbkversion.h" DESTINATION include/shiboken2${shiboken2_SUFFIX}) install(TARGETS libshiboken EXPORT shiboken2 diff --git a/sources/shiboken2/libshiboken/autodecref.h b/sources/shiboken2/libshiboken/autodecref.h index 1f3f41eab..7b6aa47da 100644 --- a/sources/shiboken2/libshiboken/autodecref.h +++ b/sources/shiboken2/libshiboken/autodecref.h @@ -79,7 +79,9 @@ public: /// Returns the pointer of the Python object being held. inline PyObject* object() { return m_pyObj; } inline operator PyObject*() { return m_pyObj; } +#ifndef Py_LIMITED_API inline operator PyTupleObject*() { return reinterpret_cast(m_pyObj); } +#endif inline operator bool() const { return m_pyObj != 0; } inline PyObject* operator->() { return m_pyObj; } diff --git a/sources/shiboken2/libshiboken/basewrapper.cpp b/sources/shiboken2/libshiboken/basewrapper.cpp index 21f6933d2..0e2712ec8 100644 --- a/sources/shiboken2/libshiboken/basewrapper.cpp +++ b/sources/shiboken2/libshiboken/basewrapper.cpp @@ -66,55 +66,34 @@ extern "C" static void SbkObjectTypeDealloc(PyObject* pyObj); static PyObject* SbkObjectTypeTpNew(PyTypeObject* metatype, PyObject* args, PyObject* kwds); -PyTypeObject SbkObjectType_Type = { - PyVarObject_HEAD_INIT(0, 0) - /*tp_name*/ "Shiboken.ObjectType", - /*tp_basicsize*/ sizeof(SbkObjectType), - /*tp_itemsize*/ 0, - /*tp_dealloc*/ SbkObjectTypeDealloc, - /*tp_print*/ 0, - /*tp_getattr*/ 0, - /*tp_setattr*/ 0, - /*tp_compare*/ 0, - /*tp_repr*/ 0, - /*tp_as_number*/ 0, - /*tp_as_sequence*/ 0, - /*tp_as_mapping*/ 0, - /*tp_hash*/ 0, - /*tp_call*/ 0, - /*tp_str*/ 0, - /*tp_getattro*/ 0, - /*tp_setattro*/ PyObject_GenericSetAttr, - /*tp_as_buffer*/ 0, - /*tp_flags*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, - /*tp_doc*/ 0, - /*tp_traverse*/ 0, - /*tp_clear*/ 0, - /*tp_richcompare*/ 0, - /*tp_weaklistoffset*/ 0, - /*tp_iter*/ 0, - /*tp_iternext*/ 0, - /*tp_methods*/ 0, - /*tp_members*/ 0, - /*tp_getset*/ 0, - /*tp_base*/ &PyType_Type, - /*tp_dict*/ 0, - /*tp_descr_get*/ 0, - /*tp_descr_set*/ 0, - /*tp_dictoffset*/ 0, - /*tp_init*/ 0, - /*tp_alloc*/ PyType_GenericAlloc, - /*tp_new*/ SbkObjectTypeTpNew, - /*tp_free*/ PyObject_GC_Del, - /*tp_is_gc*/ 0, - /*tp_bases*/ 0, - /*tp_mro*/ 0, - /*tp_cache*/ 0, - /*tp_subclasses*/ 0, - /*tp_weaklist*/ 0, - /*tp_del*/ 0, - /*tp_version_tag*/ 0 +static PyType_Slot SbkObjectType_Type_slots[] = { + {Py_tp_dealloc, (void *)SbkObjectTypeDealloc}, + {Py_tp_setattro, (void *)PyObject_GenericSetAttr}, + {Py_tp_base, (void *)&PyType_Type}, + {Py_tp_alloc, (void *)PyType_GenericAlloc}, + {Py_tp_new, (void *)SbkObjectTypeTpNew}, + {Py_tp_free, (void *)PyObject_GC_Del}, + {0, 0} }; +static PyType_Spec SbkObjectType_Type_spec = { + "Shiboken.ObjectType", + 0, // basicsize (inserted later) + sizeof(PyMemberDef), + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + SbkObjectType_Type_slots, +}; + + +PyTypeObject *SbkObjectType_TypeF(void) +{ + static PyTypeObject *type = nullptr; + if (!type) { + SbkObjectType_Type_spec.basicsize = + PepHeapType_SIZE + sizeof(SbkObjectTypePrivate); + type = reinterpret_cast(PyType_FromSpec(&SbkObjectType_Type_spec)); + } + return type; +} static PyObject *SbkObjectGetDict(PyObject* pObj, void *) { @@ -176,57 +155,36 @@ static int SbkObject_clear(PyObject* self) return 0; } -SbkObjectType SbkObject_Type = { { { - PyVarObject_HEAD_INIT(&SbkObjectType_Type, 0) - /*tp_name*/ "Shiboken.Object", - /*tp_basicsize*/ sizeof(SbkObject), - /*tp_itemsize*/ 0, - /*tp_dealloc*/ SbkDeallocWrapperWithPrivateDtor, - /*tp_print*/ 0, - /*tp_getattr*/ 0, - /*tp_setattr*/ 0, - /*tp_compare*/ 0, - /*tp_repr*/ 0, - /*tp_as_number*/ 0, - /*tp_as_sequence*/ 0, - /*tp_as_mapping*/ 0, - /*tp_hash*/ 0, - /*tp_call*/ 0, - /*tp_str*/ 0, - /*tp_getattro*/ 0, - /*tp_setattro*/ 0, - /*tp_as_buffer*/ 0, - /*tp_flags*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, - /*tp_doc*/ 0, - /*tp_traverse*/ SbkObject_traverse, - /*tp_clear*/ SbkObject_clear, - /*tp_richcompare*/ 0, - /*tp_weaklistoffset*/ offsetof(SbkObject, weakreflist), - /*tp_iter*/ 0, - /*tp_iternext*/ 0, - /*tp_methods*/ 0, - /*tp_members*/ 0, - /*tp_getset*/ SbkObjectGetSetList, - /*tp_base*/ 0, - /*tp_dict*/ 0, - /*tp_descr_get*/ 0, - /*tp_descr_set*/ 0, - /*tp_dictoffset*/ offsetof(SbkObject, ob_dict), - /*tp_init*/ 0, - /*tp_alloc*/ 0, - /*tp_new*/ 0, - /*tp_free*/ 0, - /*tp_is_gc*/ 0, - /*tp_bases*/ 0, - /*tp_mro*/ 0, - /*tp_cache*/ 0, - /*tp_subclasses*/ 0, - /*tp_weaklist*/ 0, - /*tp_del*/ 0, - /*tp_version_tag*/ 0 -}, }, - /*priv_data*/ 0 +static PyType_Slot SbkObject_Type_slots[] = { + {Py_tp_dealloc, (void *)SbkDeallocWrapperWithPrivateDtor}, + {Py_tp_traverse, (void *)SbkObject_traverse}, + {Py_tp_clear, (void *)SbkObject_clear}, + // unsupported: {Py_tp_weaklistoffset, (void *)offsetof(SbkObject, weakreflist)}, + {Py_tp_getset, (void *)SbkObjectGetSetList}, + // unsupported: {Py_tp_dictoffset, (void *)offsetof(SbkObject, ob_dict)}, + {0, 0} }; +static PyType_Spec SbkObject_Type_spec = { + "Shiboken.Object", + sizeof(SbkObject), + 0, + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, + SbkObject_Type_slots, +}; + + +SbkObjectType *SbkObject_TypeF(void) +{ + static PyTypeObject *type = nullptr; + if (!type) { + type = reinterpret_cast(PyType_FromSpec(&SbkObject_Type_spec)); + Py_TYPE(type) = SbkObjectType_TypeF(); + Py_INCREF(Py_TYPE(type)); + PepType(type)->tp_weaklistoffset = offsetof(SbkObject, weakreflist); + PepType(type)->tp_dictoffset = offsetof(SbkObject, ob_dict); + } + return reinterpret_cast(type); +} static void SbkDeallocWrapperCommon(PyObject* pyObj, bool canDelete) @@ -237,8 +195,8 @@ 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 = (pyType->tp_dealloc == SbkDeallocWrapper - || pyType->tp_dealloc == SbkDeallocWrapperWithPrivateDtor); + bool needTypeDecref = (PyType_GetSlot(pyType, Py_tp_dealloc) == SbkDeallocWrapper + || PyType_GetSlot(pyType, Py_tp_dealloc) == SbkDeallocWrapperWithPrivateDtor); // Ensure that the GC is no longer tracking this object to avoid a // possible reentrancy problem. Since there are multiple steps involved @@ -257,10 +215,10 @@ static void SbkDeallocWrapperCommon(PyObject* pyObj, bool canDelete) // If I have ownership and is valid delete C++ pointer if (canDelete && sbkObj->d->hasOwnership && sbkObj->d->validCppObject) { - SbkObjectType* sbkType = reinterpret_cast(pyType); - if (sbkType->d->is_multicpp) { + SbkObjectTypePrivate *sotp = PepType_SOTP(pyType); + if (sotp->is_multicpp) { Shiboken::DeallocVisitor visitor(sbkObj); - Shiboken::walkThroughClassHierarchy(pyObj->ob_type, &visitor); + Shiboken::walkThroughClassHierarchy(Py_TYPE(pyObj), &visitor); } else { void* cptr = sbkObj->d->cptr[0]; Shiboken::Object::deallocData(sbkObj, true); @@ -268,7 +226,7 @@ static void SbkDeallocWrapperCommon(PyObject* pyObj, bool canDelete) Shiboken::ThreadStateSaver threadSaver; if (Py_IsInitialized()) threadSaver.save(); - sbkType->d->cpp_dtor(cptr); + sotp->cpp_dtor(cptr); } } else { Shiboken::Object::deallocData(sbkObj, true); @@ -297,91 +255,103 @@ void SbkDeallocWrapperWithPrivateDtor(PyObject* self) void SbkObjectTypeDealloc(PyObject* pyObj) { - SbkObjectType* sbkType = reinterpret_cast(pyObj); + SbkObjectTypePrivate *sotp = PepType_SOTP(pyObj); + PyTypeObject *type = reinterpret_cast(pyObj); PyObject_GC_UnTrack(pyObj); Py_TRASHCAN_SAFE_BEGIN(pyObj); - if (sbkType->d) { - if(sbkType->d->user_data && sbkType->d->d_func) { - sbkType->d->d_func(sbkType->d->user_data); - sbkType->d->user_data = 0; + if (sotp) { + if (sotp->user_data && sotp->d_func) { + sotp->d_func(sotp->user_data); + sotp->user_data = nullptr; } - free(sbkType->d->original_name); - sbkType->d->original_name = 0; - if (!Shiboken::ObjectType::isUserType(reinterpret_cast(sbkType))) - Shiboken::Conversions::deleteConverter(sbkType->d->converter); - delete sbkType->d; - sbkType->d = 0; + free(sotp->original_name); + sotp->original_name = nullptr; + if (!Shiboken::ObjectType::isUserType(type)) + Shiboken::Conversions::deleteConverter(sotp->converter); + delete sotp; + sotp = nullptr; } Py_TRASHCAN_SAFE_END(pyObj); } PyObject* SbkObjectTypeTpNew(PyTypeObject* metatype, PyObject* args, PyObject* kwds) { -#ifndef IS_PY3K // Check if all bases are new style before calling type.tp_new // Was causing gc assert errors in test_bug704.py when // this check happened after creating the type object. // Argument parsing take from type.tp_new code. + + // PYSIDE-595: Also check if all bases allow inheritance. + // Before we changed to heap types, it was sufficient to remove the + // Py_TPFLAGS_BASETYPE flag. That does not work, because PySide does + // not respect this flag itself! PyObject* name; PyObject* pyBases; PyObject* dict; static const char* kwlist[] = { "name", "bases", "dict", 0}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "SO!O!:sbktype", (char**)kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO!O!:sbktype", (char**)kwlist, &name, &PyTuple_Type, &pyBases, &PyDict_Type, &dict)) return NULL; - for(int i=0, i_max=PyTuple_GET_SIZE(pyBases); i < i_max; i++) { + for (int i=0, i_max=PyTuple_GET_SIZE(pyBases); i < i_max; i++) { PyObject* baseType = PyTuple_GET_ITEM(pyBases, i); +#ifndef IS_PY3K if (PyClass_Check(baseType)) { - PyErr_Format(PyExc_TypeError, "Invalid base class used in type %s. PySide only support multiple inheritance from python new style class.", metatype->tp_name); + PyErr_Format(PyExc_TypeError, "Invalid base class used in type %s. " + "PySide only support multiple inheritance from python new style class.", PepType(metatype)->tp_name); return 0; } - } #endif + if (PepType(reinterpret_cast(baseType))->tp_new == SbkDummyNew) { + // PYSIDE-595: A base class does not allow inheritance. + return SbkDummyNew(metatype, args, kwds); + } + } // The meta type creates a new type when the Python programmer extends a wrapped C++ class. - SbkObjectType* newType = reinterpret_cast(PyType_Type.tp_new(metatype, args, kwds)); + newfunc type_new = reinterpret_cast(PepType(&PyType_Type)->tp_new); + SbkObjectType *newType = reinterpret_cast(type_new(metatype, args, kwds)); if (!newType) return 0; Shiboken::ObjectType::initPrivateData(newType); - SbkObjectTypePrivate* d = newType->d; + SbkObjectTypePrivate *sotp = PepType_SOTP(newType); std::list bases = Shiboken::getCppBaseClasses(reinterpret_cast(newType)); if (bases.size() == 1) { - SbkObjectTypePrivate* parentType = bases.front()->d; - d->mi_offsets = parentType->mi_offsets; - d->mi_init = parentType->mi_init; - d->mi_specialcast = parentType->mi_specialcast; - d->type_discovery = parentType->type_discovery; - d->cpp_dtor = parentType->cpp_dtor; - d->is_multicpp = 0; - d->converter = parentType->converter; + SbkObjectTypePrivate *parentType = PepType_SOTP(bases.front()); + sotp->mi_offsets = parentType->mi_offsets; + sotp->mi_init = parentType->mi_init; + sotp->mi_specialcast = parentType->mi_specialcast; + sotp->type_discovery = parentType->type_discovery; + sotp->cpp_dtor = parentType->cpp_dtor; + sotp->is_multicpp = 0; + sotp->converter = parentType->converter; } else { - d->mi_offsets = 0; - d->mi_init = 0; - d->mi_specialcast = 0; - d->type_discovery = 0; - d->cpp_dtor = 0; - d->is_multicpp = 1; - d->converter = 0; + sotp->mi_offsets = nullptr; + sotp->mi_init = nullptr; + sotp->mi_specialcast = nullptr; + sotp->type_discovery = nullptr; + sotp->cpp_dtor = nullptr; + sotp->is_multicpp = 1; + sotp->converter = nullptr; } if (bases.size() == 1) - d->original_name = strdup(bases.front()->d->original_name); + sotp->original_name = strdup(PepType_SOTP(bases.front())->original_name); else - d->original_name = strdup("object"); - d->user_data = 0; - d->d_func = 0; - d->is_user_type = 1; + sotp->original_name = strdup("object"); + sotp->user_data = nullptr; + sotp->d_func = nullptr; + sotp->is_user_type = 1; std::list::const_iterator it = bases.begin(); for (; it != bases.end(); ++it) { - if ((*it)->d->subtype_init) - (*it)->d->subtype_init(newType, args, kwds); + if (PepType_SOTP(*it)->subtype_init) + PepType_SOTP(*it)->subtype_init(newType, args, kwds); } return reinterpret_cast(newType); @@ -392,18 +362,19 @@ static PyObject *_setupNew(SbkObject *self, PyTypeObject *subtype) Py_INCREF(reinterpret_cast(subtype)); SbkObjectPrivate* d = new SbkObjectPrivate; - SbkObjectType* sbkType = reinterpret_cast(subtype); - int numBases = ((sbkType->d && sbkType->d->is_multicpp) ? Shiboken::getNumberOfCppBaseClasses(subtype) : 1); + SbkObjectTypePrivate * sotp = PepType_SOTP(subtype); + int numBases = ((sotp && sotp->is_multicpp) ? + Shiboken::getNumberOfCppBaseClasses(subtype) : 1); d->cptr = new void*[numBases]; - std::memset(d->cptr, 0, sizeof(void*)*numBases); + std::memset(d->cptr, 0, sizeof(void*) * size_t(numBases)); d->hasOwnership = 1; d->containsCppWrapper = 0; d->validCppObject = 0; - d->parentInfo = 0; - d->referredObjects = 0; + d->parentInfo = nullptr; + d->referredObjects = nullptr; d->cppObjectCreated = 0; - self->ob_dict = 0; - self->weakreflist = 0; + self->ob_dict = nullptr; + self->weakreflist = nullptr; self->d = d; return reinterpret_cast(self); } @@ -422,18 +393,37 @@ PyObject* SbkQAppTpNew(PyTypeObject* subtype, PyObject *, PyObject *) // For qApp, we need to create a singleton Python object. // We cannot track this with the GC, because it is a static variable! - // Python2 has a weird handling of flags in derived classes that Python3 + // Python 2 has a weird handling of flags in derived classes that Python 3 // does not have. Observed with bug_307.py. // But it could theoretically also happen with Python3. // Therefore we enforce that there is no GC flag, ever! + + // PYSIDE-560: + // We avoid to use this in Python 3, because we have a hard time to get + // write access to these flags +#ifndef IS_PY3K if (PyType_HasFeature(subtype, Py_TPFLAGS_HAVE_GC)) { subtype->tp_flags &= ~Py_TPFLAGS_HAVE_GC; subtype->tp_free = PyObject_Del; } +#endif SbkObject* self = reinterpret_cast(MakeSingletonQAppWrapper(subtype)); return self == 0 ? 0 : _setupNew(self, subtype); } +void +SbkDummyDealloc(PyObject *) +{} + +PyObject * +SbkDummyNew(PyTypeObject *type, PyObject*, PyObject*) +{ + // PYSIDE-595: Give the same error as type_call does when tp_new is NULL. + PyErr_Format(PyExc_TypeError, + "cannot create '%.100s' instances ¯\\_(ツ)_/¯", + PepType(type)->tp_name); + return nullptr; +} } //extern "C" @@ -464,16 +454,16 @@ static void decRefPyObjectList(const std::list &pyObj, PyObject* skip static void _walkThroughClassHierarchy(PyTypeObject* currentType, HierarchyVisitor* visitor) { - PyObject* bases = currentType->tp_bases; + PyObject* bases = PepType(currentType)->tp_bases; Py_ssize_t numBases = PyTuple_GET_SIZE(bases); for (int i = 0; i < numBases; ++i) { PyTypeObject* type = reinterpret_cast(PyTuple_GET_ITEM(bases, i)); - if (!PyType_IsSubtype(type, reinterpret_cast(&SbkObject_Type))) { + if (!PyType_IsSubtype(type, reinterpret_cast(SbkObject_TypeF()))) { continue; } else { SbkObjectType* sbkType = reinterpret_cast(type); - if (sbkType->d->is_user_type) + if (PepType_SOTP(sbkType)->is_user_type) _walkThroughClassHierarchy(type, visitor); else visitor->visit(sbkType); @@ -532,7 +522,7 @@ void DtorCallerVisitor::done() for (; it != m_ptrs.end(); ++it) { Shiboken::ThreadStateSaver threadSaver; threadSaver.save(); - it->second->d->cpp_dtor(it->first); + PepType_SOTP(it->second)->cpp_dtor(it->first); } } @@ -555,15 +545,17 @@ void init() PyEval_InitThreads(); //Init private data - Shiboken::ObjectType::initPrivateData(&SbkObject_Type); + Pep_Init(); + + Shiboken::ObjectType::initPrivateData(SbkObject_TypeF()); - if (PyType_Ready(&SbkEnumType_Type) < 0) + if (PyType_Ready(SbkEnumType_TypeF()) < 0) Py_FatalError("[libshiboken] Failed to initialise Shiboken.SbkEnumType metatype."); - if (PyType_Ready(&SbkObjectType_Type) < 0) + if (PyType_Ready(SbkObjectType_TypeF()) < 0) Py_FatalError("[libshiboken] Failed to initialise Shiboken.BaseWrapperType metatype."); - if (PyType_Ready(reinterpret_cast(&SbkObject_Type)) < 0) + if (PyType_Ready(reinterpret_cast(SbkObject_TypeF())) < 0) Py_FatalError("[libshiboken] Failed to initialise Shiboken.BaseWrapper type."); VoidPtr::init(); @@ -581,10 +573,10 @@ void setErrorAboutWrongArguments(PyObject* args, const char* funcName, const cha if (i) params += ", "; PyObject* arg = PyTuple_GET_ITEM(args, i); - params += arg->ob_type->tp_name; + params += PepType((Py_TYPE(arg)))->tp_name; } } else { - params = args->ob_type->tp_name; + params = PepType((Py_TYPE(args)))->tp_name; } } @@ -660,12 +652,12 @@ namespace ObjectType bool checkType(PyTypeObject* type) { - return PyType_IsSubtype(type, reinterpret_cast(&SbkObject_Type)) != 0; + return PyType_IsSubtype(type, reinterpret_cast(SbkObject_TypeF())) != 0; } bool isUserType(PyTypeObject* type) { - return checkType(type) && reinterpret_cast(type)->d->is_user_type; + return checkType(type) && PepType_SOTP(type)->is_user_type; } bool canCallConstructor(PyTypeObject* myType, PyTypeObject* ctorType) @@ -673,7 +665,7 @@ bool canCallConstructor(PyTypeObject* myType, PyTypeObject* ctorType) FindBaseTypeVisitor visitor(ctorType); walkThroughClassHierarchy(myType, &visitor); if (!visitor.found()) { - PyErr_Format(PyExc_TypeError, "%s isn't a direct base class of %s", ctorType->tp_name, myType->tp_name); + PyErr_Format(PyExc_TypeError, "%s isn't a direct base class of %s", PepType(ctorType)->tp_name, PepType(myType)->tp_name); return false; } return true; @@ -681,114 +673,133 @@ bool canCallConstructor(PyTypeObject* myType, PyTypeObject* ctorType) bool hasCast(SbkObjectType* type) { - return type->d->mi_specialcast != 0; + return PepType_SOTP(type)->mi_specialcast != 0; } void* cast(SbkObjectType* sourceType, SbkObject* obj, PyTypeObject* targetType) { - return sourceType->d->mi_specialcast(Object::cppPointer(obj, targetType), reinterpret_cast(targetType)); + return PepType_SOTP(sourceType)->mi_specialcast(Object::cppPointer(obj, targetType), + reinterpret_cast(targetType)); } void setCastFunction(SbkObjectType* type, SpecialCastFunction func) { - type->d->mi_specialcast = func; + PepType_SOTP(type)->mi_specialcast = func; } -void setOriginalName(SbkObjectType* self, const char* name) +void setOriginalName(SbkObjectType* type, const char* name) { - if (self->d->original_name) - free(self->d->original_name); - self->d->original_name = strdup(name); + SbkObjectTypePrivate *sotp = PepType_SOTP(type); + if (sotp->original_name) + free(sotp->original_name); + sotp->original_name = strdup(name); } -const char* getOriginalName(SbkObjectType* self) +const char* getOriginalName(SbkObjectType* type) { - return self->d->original_name; + return PepType_SOTP(type)->original_name; } -void setTypeDiscoveryFunctionV2(SbkObjectType* self, TypeDiscoveryFuncV2 func) +void setTypeDiscoveryFunctionV2(SbkObjectType* type, TypeDiscoveryFuncV2 func) { - self->d->type_discovery = func; + PepType_SOTP(type)->type_discovery = func; } -void copyMultimpleheritance(SbkObjectType* self, SbkObjectType* other) +void copyMultimpleheritance(SbkObjectType* type, SbkObjectType* other) { - self->d->mi_init = other->d->mi_init; - self->d->mi_offsets = other->d->mi_offsets; - self->d->mi_specialcast = other->d->mi_specialcast; + PepType_SOTP(type)->mi_init = PepType_SOTP(other)->mi_init; + PepType_SOTP(type)->mi_offsets = PepType_SOTP(other)->mi_offsets; + PepType_SOTP(type)->mi_specialcast = PepType_SOTP(other)->mi_specialcast; } -void setMultipleIheritanceFunction(SbkObjectType* self, MultipleInheritanceInitFunction function) +void setMultipleInheritanceFunction(SbkObjectType* type, MultipleInheritanceInitFunction function) { - self->d->mi_init = function; + PepType_SOTP(type)->mi_init = function; } -MultipleInheritanceInitFunction getMultipleIheritanceFunction(SbkObjectType* self) +MultipleInheritanceInitFunction getMultipleIheritanceFunction(SbkObjectType* type) { - return self->d->mi_init; + return PepType_SOTP(type)->mi_init; } -void setDestructorFunction(SbkObjectType* self, ObjectDestructor func) +void setDestructorFunction(SbkObjectType* type, ObjectDestructor func) { - self->d->cpp_dtor = func; + PepType_SOTP(type)->cpp_dtor = func; } -void initPrivateData(SbkObjectType* self) +void initPrivateData(SbkObjectType* type) { - self->d = new SbkObjectTypePrivate; - memset(self->d, 0, sizeof(SbkObjectTypePrivate)); + PepType_SOTP(type) = new SbkObjectTypePrivate; + memset(PepType_SOTP(type), 0, sizeof(SbkObjectTypePrivate)); } -bool introduceWrapperType(PyObject *enclosingObject, - const char *typeName, const char *originalName, - SbkObjectType *type, - const char *signaturesString, - ObjectDestructor cppObjDtor, - SbkObjectType *baseType, PyObject *baseTypes, - bool isInnerClass) +SbkObjectType * +introduceWrapperType(PyObject *enclosingObject, + const char *typeName, + const char *originalName, + PyType_Spec *typeSpec, + const char *signaturesString, + ObjectDestructor cppObjDtor, + SbkObjectType *baseType, + PyObject *baseTypes, + bool isInnerClass) { - initPrivateData(type); - setOriginalName(type, originalName); - setDestructorFunction(type, cppObjDtor); - if (baseType) { - type->super.ht_type.tp_base = reinterpret_cast(baseType); + typeSpec->slots[0].pfunc = reinterpret_cast(baseType); + } + else { + typeSpec->slots[0].pfunc = reinterpret_cast(SbkObject_TypeF()); + } + PyObject *heaptype = PyType_FromSpecWithBases(typeSpec, baseTypes); + Py_TYPE(heaptype) = SbkObjectType_TypeF(); + Py_INCREF(Py_TYPE(heaptype)); + SbkObjectType *type = reinterpret_cast(heaptype); + if (baseType) { if (baseTypes) { for (int i = 0; i < PySequence_Fast_GET_SIZE(baseTypes); ++i) BindingManager::instance().addClassInheritance(reinterpret_cast(PySequence_Fast_GET_ITEM(baseTypes, i)), type); - type->super.ht_type.tp_bases = baseTypes; } else { BindingManager::instance().addClassInheritance(baseType, type); } } - - // PySide-510 - // here is the single change to support signatures. + // PYSIDE-510: Here is the single change to support signatures. if (SbkSpecial_Type_Ready(enclosingObject, reinterpret_cast(type), signaturesString) < 0) - return false; + return nullptr; - if (isInnerClass) - return PyDict_SetItemString(enclosingObject, typeName, reinterpret_cast(type)) == 0; + initPrivateData(type); + setOriginalName(type, originalName); + setDestructorFunction(type, cppObjDtor); + + if (isInnerClass) { + if (PyDict_SetItemString(enclosingObject, typeName, reinterpret_cast(type)) == 0) + return type; + else + return nullptr; + } //PyModule_AddObject steals type's reference. Py_INCREF(reinterpret_cast(type)); - return PyModule_AddObject(enclosingObject, typeName, reinterpret_cast(type)) == 0; + if (PyModule_AddObject(enclosingObject, typeName, reinterpret_cast(type)) == 0) { + return type; + } + return nullptr; } -void setSubTypeInitHook(SbkObjectType* self, SubTypeInitHook func) +void setSubTypeInitHook(SbkObjectType* type, SubTypeInitHook func) { - self->d->subtype_init = func; + PepType_SOTP(type)->subtype_init = func; } -void* getTypeUserData(SbkObjectType* self) +void* getTypeUserData(SbkObjectType* type) { - return self->d->user_data; + return PepType_SOTP(type)->user_data; } -void setTypeUserData(SbkObjectType* self, void* userData, DeleteUserDataFunc d_func) +void setTypeUserData(SbkObjectType* type, void* userData, DeleteUserDataFunc d_func) { - self->d->user_data = userData; - self->d->d_func = d_func; + SbkObjectTypePrivate *sotp = PepType_SOTP(type); + sotp->user_data = userData; + sotp->d_func = d_func; } } // namespace ObjectType @@ -801,12 +812,12 @@ static void recursive_invalidate(SbkObject* self, std::set& seen); bool checkType(PyObject* pyObj) { - return ObjectType::checkType(pyObj->ob_type); + return ObjectType::checkType(Py_TYPE(pyObj)); } bool isUserType(PyObject* pyObj) { - return ObjectType::isUserType(pyObj->ob_type); + return ObjectType::isUserType(Py_TYPE(pyObj)); } Py_hash_t hash(PyObject* pyObj) @@ -858,14 +869,15 @@ bool wasCreatedByPython(SbkObject* pyObj) void callCppDestructors(SbkObject* pyObj) { - SbkObjectType* sbkType = reinterpret_cast(Py_TYPE(pyObj)); - if (sbkType->d->is_multicpp) { + PyTypeObject *type = Py_TYPE(pyObj); + SbkObjectTypePrivate * sotp = PepType_SOTP(type); + if (sotp->is_multicpp) { Shiboken::DtorCallerVisitor visitor(pyObj); - Shiboken::walkThroughClassHierarchy(Py_TYPE(pyObj), &visitor); + Shiboken::walkThroughClassHierarchy(type, &visitor); } else { Shiboken::ThreadStateSaver threadSaver; threadSaver.save(); - sbkType->d->cpp_dtor(pyObj->d->cptr[0]); + sotp->cpp_dtor(pyObj->d->cptr[0]); } /* invalidate needs to be called before deleting pointer array because @@ -916,7 +928,7 @@ void releaseOwnership(SbkObject* self) { // skip if the ownership have already moved to c++ SbkObjectType* selfType = reinterpret_cast(Py_TYPE(self)); - if (!self->d->hasOwnership || Shiboken::Conversions::pythonTypeIsValueType(selfType->d->converter)) + if (!self->d->hasOwnership || Shiboken::Conversions::pythonTypeIsValueType(PepType_SOTP(selfType)->converter)) return; // remove object ownership @@ -1037,7 +1049,7 @@ void* cppPointer(SbkObject* pyObj, PyTypeObject* desiredType) { PyTypeObject* type = Py_TYPE(pyObj); int idx = 0; - if (reinterpret_cast(type)->d->is_multicpp) + if (PepType_SOTP(reinterpret_cast(type))->is_multicpp) idx = getTypeIndexOnHierarchy(type, desiredType); if (pyObj->d->cptr) return pyObj->d->cptr[idx]; @@ -1057,8 +1069,9 @@ std::vector cppPointers(SbkObject* pyObj) bool setCppPointer(SbkObject* sbkObj, PyTypeObject* desiredType, void* cptr) { int idx = 0; - if (reinterpret_cast(Py_TYPE(sbkObj))->d->is_multicpp) - idx = getTypeIndexOnHierarchy(Py_TYPE(sbkObj), desiredType); + PyTypeObject *type = Py_TYPE(sbkObj); + if (PepType_SOTP(type)->is_multicpp) + idx = getTypeIndexOnHierarchy(type, desiredType); const bool alreadyInitialized = sbkObj->d->cptr[idx] != 0; if (alreadyInitialized) @@ -1073,19 +1086,21 @@ bool setCppPointer(SbkObject* sbkObj, PyTypeObject* desiredType, void* cptr) bool isValid(PyObject* pyObj) { if (!pyObj || pyObj == Py_None - || Py_TYPE(pyObj->ob_type) != &SbkObjectType_Type) { + || Py_TYPE(Py_TYPE(pyObj)) != SbkObjectType_TypeF()) { return true; } SbkObjectPrivate* priv = reinterpret_cast(pyObj)->d; if (!priv->cppObjectCreated && isUserType(pyObj)) { - PyErr_Format(PyExc_RuntimeError, "'__init__' method of object's base class (%s) not called.", pyObj->ob_type->tp_name); + PyErr_Format(PyExc_RuntimeError, "'__init__' method of object's base class (%s) not called.", + PepType((Py_TYPE(pyObj)))->tp_name); return false; } if (!priv->validCppObject) { - PyErr_Format(PyExc_RuntimeError, "Internal C++ object (%s) already deleted.", pyObj->ob_type->tp_name); + PyErr_Format(PyExc_RuntimeError, "Internal C++ object (%s) already deleted.", + PepType((Py_TYPE(pyObj)))->tp_name); return false; } @@ -1100,13 +1115,15 @@ bool isValid(SbkObject* pyObj, bool throwPyError) SbkObjectPrivate* priv = pyObj->d; if (!priv->cppObjectCreated && isUserType(reinterpret_cast(pyObj))) { if (throwPyError) - PyErr_Format(PyExc_RuntimeError, "Base constructor of the object (%s) not called.", Py_TYPE(pyObj)->tp_name); + PyErr_Format(PyExc_RuntimeError, "Base constructor of the object (%s) not called.", + PepType((Py_TYPE(pyObj)))->tp_name); return false; } if (!priv->validCppObject) { if (throwPyError) - PyErr_Format(PyExc_RuntimeError, "Internal C++ object (%s) already deleted.", Py_TYPE(pyObj)->tp_name); + PyErr_Format(PyExc_RuntimeError, "Internal C++ object (%s) already deleted.", + PepType((Py_TYPE(pyObj)))->tp_name); return false; } @@ -1116,7 +1133,7 @@ bool isValid(SbkObject* pyObj, bool throwPyError) bool isValid(PyObject* pyObj, bool throwPyError) { if (!pyObj || pyObj == Py_None || - !PyType_IsSubtype(pyObj->ob_type, reinterpret_cast(&SbkObject_Type))) { + !PyType_IsSubtype(Py_TYPE(pyObj), reinterpret_cast(SbkObject_TypeF()))) { return true; } return isValid(reinterpret_cast(pyObj), throwPyError); @@ -1384,24 +1401,25 @@ 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((PyObject*)self)) - Py_TYPE(self)->tp_free(self); + if (PyObject_IS_GC(reinterpret_cast(self))) + PepType(Py_TYPE(self))->tp_free(self); } void setTypeUserData(SbkObject* wrapper, void* userData, DeleteUserDataFunc d_func) { - SbkObjectType* ob_type = reinterpret_cast(Py_TYPE(wrapper)); - if (ob_type->d->user_data) - ob_type->d->d_func(ob_type->d->user_data); + SbkObjectTypePrivate *sotp = PepType_SOTP(Py_TYPE(wrapper)); + if (sotp->user_data) + sotp->d_func(sotp->user_data); - ob_type->d->d_func = d_func; - ob_type->d->user_data = userData; + sotp->d_func = d_func; + sotp->user_data = userData; } void* getTypeUserData(SbkObject* wrapper) { - return reinterpret_cast(Py_TYPE(wrapper))->d->user_data; + return PepType_SOTP(Py_TYPE(wrapper))->user_data; } void keepReference(SbkObject* self, const char* key, PyObject* referredObject, bool append) @@ -1483,7 +1501,7 @@ std::string info(SbkObject* self) s << "C++ address....... "; std::list::const_iterator it = bases.begin(); for (int i = 0; it != bases.end(); ++it, ++i) - s << reinterpret_cast(*it)->tp_name << '/' << self->d->cptr[i] << ' '; + s << PepType((reinterpret_cast(*it)))->tp_name << '/' << self->d->cptr[i] << ' '; s << "\n"; } else { diff --git a/sources/shiboken2/libshiboken/basewrapper.h b/sources/shiboken2/libshiboken/basewrapper.h index fc553cf8c..755058e8b 100644 --- a/sources/shiboken2/libshiboken/basewrapper.h +++ b/sources/shiboken2/libshiboken/basewrapper.h @@ -93,22 +93,34 @@ typedef void (*ObjectDestructor)(void*); typedef void (*SubTypeInitHook)(SbkObjectType*, PyObject*, PyObject*); -extern LIBSHIBOKEN_API PyTypeObject SbkObjectType_Type; -extern LIBSHIBOKEN_API SbkObjectType SbkObject_Type; +extern LIBSHIBOKEN_API PyTypeObject *SbkObjectType_TypeF(void); +extern LIBSHIBOKEN_API SbkObjectType *SbkObject_TypeF(void); struct SbkObjectTypePrivate; /// PyTypeObject extended with C++ multiple inheritance information. struct LIBSHIBOKEN_API SbkObjectType { - PyHeapTypeObject super; - SbkObjectTypePrivate* d; + PepTypeObject type; }; LIBSHIBOKEN_API PyObject* SbkObjectTpNew(PyTypeObject* subtype, PyObject*, PyObject*); // the special case of a switchable singleton LIBSHIBOKEN_API PyObject* SbkQAppTpNew(PyTypeObject *subtype, PyObject *args, PyObject *kwds); +/** + * PYSIDE-595: Use a null deallocator instead of nullptr. + * + * When moving to heaptypes, we were struck by a special default behavior of + * PyType_FromSpecWithBases that inserts subtype_dealloc when tp_dealloc is + * nullptr. To prevent inserting this, we use a null deallocator that is there + * as a placeholder. + * + * The same holds for a null tp_new. We use one that raises the right error. + */ +LIBSHIBOKEN_API void SbkDummyDealloc(PyObject*); +LIBSHIBOKEN_API PyObject *SbkDummyNew(PyTypeObject *type, PyObject*, PyObject*); + } // extern "C" namespace Shiboken @@ -173,7 +185,7 @@ LIBSHIBOKEN_API const char* getOriginalName(SbkObjectType* self); LIBSHIBOKEN_API void setTypeDiscoveryFunctionV2(SbkObjectType* self, TypeDiscoveryFuncV2 func); LIBSHIBOKEN_API void copyMultimpleheritance(SbkObjectType* self, SbkObjectType* other); -LIBSHIBOKEN_API void setMultipleIheritanceFunction(SbkObjectType* self, MultipleInheritanceInitFunction func); +LIBSHIBOKEN_API void setMultipleInheritanceFunction(SbkObjectType* self, MultipleInheritanceInitFunction func); LIBSHIBOKEN_API MultipleInheritanceInitFunction getMultipleIheritanceFunction(SbkObjectType* self); LIBSHIBOKEN_API void setDestructorFunction(SbkObjectType* self, ObjectDestructor func); @@ -197,13 +209,15 @@ LIBSHIBOKEN_API void initPrivateData(SbkObjectType* self); * wrapper type. * \returns true if the initialization went fine, false otherwise. */ -LIBSHIBOKEN_API bool introduceWrapperType(PyObject* enclosingObject, - const char* typeName, const char* originalName, - SbkObjectType* type, - const char* signaturesString, - ObjectDestructor cppObjDtor = 0, - SbkObjectType* baseType = 0, PyObject* baseTypes = 0, - bool isInnerClass = false); +LIBSHIBOKEN_API SbkObjectType *introduceWrapperType(PyObject *enclosingObject, + const char *typeName, + const char *originalName, + PyType_Spec *typeSpec, + const char *signaturesString, + ObjectDestructor cppObjDtor, + SbkObjectType *baseType, + PyObject *baseTypes, + bool isInnerClass); /** * Set the subtype init hook for a type. diff --git a/sources/shiboken2/libshiboken/bindingmanager.cpp b/sources/shiboken2/libshiboken/bindingmanager.cpp index de3458ab5..5a3283ab5 100644 --- a/sources/shiboken2/libshiboken/bindingmanager.cpp +++ b/sources/shiboken2/libshiboken/bindingmanager.cpp @@ -83,8 +83,10 @@ public: SbkObjectType* node1 = i->first; const NodeList& nodeList = i->second; NodeList::const_iterator j = nodeList.begin(); - for (; j != nodeList.end(); ++j) - file << '"' << (*j)->super.ht_type.tp_name << "\" -> \"" << node1->super.ht_type.tp_name << "\"\n"; + for (; j != nodeList.end(); ++j) { + file << '"' << PepType(*j)->tp_name << "\" -> \"" + << PepType(node1)->tp_name << "\"\n"; + } } file << "}\n"; } @@ -102,7 +104,10 @@ public: return newType; } } - void* typeFound = ((type->d && type->d->type_discovery) ? type->d->type_discovery(*cptr, baseType) : 0); + void *typeFound = nullptr; + if (PepType_SOTP(type) && PepType_SOTP(type)->type_discovery) { + typeFound = PepType_SOTP(type)->type_discovery(*cptr, baseType); + } if (typeFound) { // This "typeFound != type" is needed for backwards compatibility with old modules using a newer version of // libshiboken because old versions of type_discovery function used to return a SbkObjectType* instead of @@ -111,7 +116,7 @@ public: *cptr = typeFound; return type; } else { - return 0; + return nullptr; } } }; @@ -128,7 +133,7 @@ static void showWrapperMap(const WrapperMap& wrapperMap) const SbkObject *sbkObj = iter->second; fprintf(stderr, "key: %p, value: %p (%s, refcnt: %d)\n", iter->first, static_cast(sbkObj), - Py_TYPE(sbkObj)->tp_name, + PepType((Py_TYPE(sbkObj)))->tp_name, int(reinterpret_cast(sbkObj)->ob_refcnt)); } fprintf(stderr, "-------------------------------\n"); @@ -210,7 +215,7 @@ bool BindingManager::hasWrapper(const void* cptr) void BindingManager::registerWrapper(SbkObject* pyObj, void* cptr) { SbkObjectType* instanceType = reinterpret_cast(Py_TYPE(pyObj)); - SbkObjectTypePrivate* d = instanceType->d; + SbkObjectTypePrivate* d = PepType_SOTP(instanceType); if (!d) return; @@ -231,7 +236,7 @@ void BindingManager::registerWrapper(SbkObject* pyObj, void* cptr) void BindingManager::releaseWrapper(SbkObject* sbkObj) { SbkObjectType* sbkType = reinterpret_cast(Py_TYPE(sbkObj)); - SbkObjectTypePrivate* d = sbkType->d; + SbkObjectTypePrivate* d = PepType_SOTP(sbkType); int numBases = ((d && d->is_multicpp) ? getNumberOfCppBaseClasses(Py_TYPE(sbkObj)) : 1); void** cptrs = reinterpret_cast(sbkObj)->d->cptr; @@ -278,17 +283,17 @@ PyObject* BindingManager::getOverride(const void* cptr, const char* methodName) PyObject *method = PyObject_GetAttr(reinterpret_cast(wrapper), pyMethodName); if (method && PyMethod_Check(method) - && reinterpret_cast(method)->im_self == reinterpret_cast(wrapper)) { + && PyMethod_GET_SELF(method) == reinterpret_cast(wrapper)) { PyObject* defaultMethod; - PyObject* mro = Py_TYPE(wrapper)->tp_mro; + PyObject* mro = PepType(Py_TYPE(wrapper))->tp_mro; // The first class in the mro (index 0) is the class being checked and it should not be tested. // The last class in the mro (size - 1) is the base Python object class which should not be tested also. for (int i = 1; i < PyTuple_GET_SIZE(mro) - 1; i++) { PyTypeObject* parent = reinterpret_cast(PyTuple_GET_ITEM(mro, i)); - if (parent->tp_dict) { - defaultMethod = PyDict_GetItem(parent->tp_dict, pyMethodName); - if (defaultMethod && reinterpret_cast(method)->im_func != defaultMethod) { + if (PepType(parent)->tp_dict) { + defaultMethod = PyDict_GetItem(PepType(parent)->tp_dict, pyMethodName); + if (defaultMethod && PyMethod_GET_FUNCTION(method) != defaultMethod) { Py_DECREF(pyMethodName); return method; } diff --git a/sources/shiboken2/libshiboken/bufferprocs27.cpp b/sources/shiboken2/libshiboken/bufferprocs27.cpp new file mode 100644 index 000000000..168a28a96 --- /dev/null +++ b/sources/shiboken2/libshiboken/bufferprocs27.cpp @@ -0,0 +1,397 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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$ +** +****************************************************************************/ + +/***************************************************************************** + * + * Copied from abstract.c + * + * Py_buffer has been replaced by Pep_buffer + * + */ + +#ifdef Py_LIMITED_API + +#include "pep384impl.h" +/* Buffer C-API for Python 3.0 */ + +int +PyObject_GetBuffer(PyObject *obj, Pep_buffer *view, int flags) +{ + PyBufferProcs *pb = PepType_AS_BUFFER(Py_TYPE(obj)); + + if (pb == NULL || pb->bf_getbuffer == NULL) { + PyErr_Format(PyExc_TypeError, + "a bytes-like object is required, not '%.100s'", + PepType((Py_TYPE(obj)))->tp_name); + return -1; + } + return (*pb->bf_getbuffer)(obj, view, flags); +} + +static int +_IsFortranContiguous(const Pep_buffer *view) +{ + Py_ssize_t sd, dim; + int i; + + /* 1) len = product(shape) * itemsize + 2) itemsize > 0 + 3) len = 0 <==> exists i: shape[i] = 0 */ + if (view->len == 0) return 1; + if (view->strides == NULL) { /* C-contiguous by definition */ + /* Trivially F-contiguous */ + if (view->ndim <= 1) return 1; + + /* ndim > 1 implies shape != NULL */ + assert(view->shape != NULL); + + /* Effectively 1-d */ + sd = 0; + for (i=0; indim; i++) { + if (view->shape[i] > 1) sd += 1; + } + return sd <= 1; + } + + /* strides != NULL implies both of these */ + assert(view->ndim > 0); + assert(view->shape != NULL); + + sd = view->itemsize; + for (i=0; indim; i++) { + dim = view->shape[i]; + if (dim > 1 && view->strides[i] != sd) { + return 0; + } + sd *= dim; + } + return 1; +} + +static int +_IsCContiguous(const Pep_buffer *view) +{ + Py_ssize_t sd, dim; + int i; + + /* 1) len = product(shape) * itemsize + 2) itemsize > 0 + 3) len = 0 <==> exists i: shape[i] = 0 */ + if (view->len == 0) return 1; + if (view->strides == NULL) return 1; /* C-contiguous by definition */ + + /* strides != NULL implies both of these */ + assert(view->ndim > 0); + assert(view->shape != NULL); + + sd = view->itemsize; + for (i=view->ndim-1; i>=0; i--) { + dim = view->shape[i]; + if (dim > 1 && view->strides[i] != sd) { + return 0; + } + sd *= dim; + } + return 1; +} + +int +PyBuffer_IsContiguous(const Pep_buffer *view, char order) +{ + + if (view->suboffsets != NULL) return 0; + + if (order == 'C') + return _IsCContiguous(view); + else if (order == 'F') + return _IsFortranContiguous(view); + else if (order == 'A') + return (_IsCContiguous(view) || _IsFortranContiguous(view)); + return 0; +} + + +void* +PyBuffer_GetPointer(Pep_buffer *view, Py_ssize_t *indices) +{ + char* pointer; + int i; + pointer = (char *)view->buf; + for (i = 0; i < view->ndim; i++) { + pointer += view->strides[i]*indices[i]; + if ((view->suboffsets != NULL) && (view->suboffsets[i] >= 0)) { + pointer = *((char**)pointer) + view->suboffsets[i]; + } + } + return (void*)pointer; +} + + +void +_Py_add_one_to_index_F(int nd, Py_ssize_t *index, const Py_ssize_t *shape) +{ + int k; + + for (k=0; k=0; k--) { + if (index[k] < shape[k]-1) { + index[k]++; + break; + } + else { + index[k] = 0; + } + } +} + +int +PyBuffer_FromContiguous(Pep_buffer *view, void *buf, Py_ssize_t len, char fort) +{ + int k; + void (*addone)(int, Py_ssize_t *, const Py_ssize_t *); + Py_ssize_t *indices, elements; + char *src, *ptr; + + if (len > view->len) { + len = view->len; + } + + if (PyBuffer_IsContiguous(view, fort)) { + /* simplest copy is all that is needed */ + memcpy(view->buf, buf, len); + return 0; + } + + /* Otherwise a more elaborate scheme is needed */ + + /* view->ndim <= 64 */ + indices = (Py_ssize_t *)PyMem_Malloc(sizeof(Py_ssize_t)*(view->ndim)); + if (indices == NULL) { + PyErr_NoMemory(); + return -1; + } + for (k=0; kndim;k++) { + indices[k] = 0; + } + + if (fort == 'F') { + addone = _Py_add_one_to_index_F; + } + else { + addone = _Py_add_one_to_index_C; + } + src = (char *)buf; // patched by CT + /* XXX : This is not going to be the fastest code in the world + several optimizations are possible. + */ + elements = len / view->itemsize; + while (elements--) { + ptr = (char *)PyBuffer_GetPointer(view, indices); // patched by CT + memcpy(ptr, src, view->itemsize); + src += view->itemsize; + addone(view->ndim, indices, view->shape); + } + + PyMem_Free(indices); + return 0; +} + +int PyObject_CopyData(PyObject *dest, PyObject *src) +{ + Pep_buffer view_dest, view_src; + int k; + Py_ssize_t *indices, elements; + char *dptr, *sptr; + + if (!PyObject_CheckBuffer(dest) || + !PyObject_CheckBuffer(src)) { + PyErr_SetString(PyExc_TypeError, + "both destination and source must be "\ + "bytes-like objects"); + return -1; + } + + if (PyObject_GetBuffer(dest, &view_dest, PyBUF_FULL) != 0) return -1; + if (PyObject_GetBuffer(src, &view_src, PyBUF_FULL_RO) != 0) { + PyBuffer_Release(&view_dest); + return -1; + } + + if (view_dest.len < view_src.len) { + PyErr_SetString(PyExc_BufferError, + "destination is too small to receive data from source"); + PyBuffer_Release(&view_dest); + PyBuffer_Release(&view_src); + return -1; + } + + if ((PyBuffer_IsContiguous(&view_dest, 'C') && + PyBuffer_IsContiguous(&view_src, 'C')) || + (PyBuffer_IsContiguous(&view_dest, 'F') && + PyBuffer_IsContiguous(&view_src, 'F'))) { + /* simplest copy is all that is needed */ + memcpy(view_dest.buf, view_src.buf, view_src.len); + PyBuffer_Release(&view_dest); + PyBuffer_Release(&view_src); + return 0; + } + + /* Otherwise a more elaborate copy scheme is needed */ + + /* XXX(nnorwitz): need to check for overflow! */ + indices = (Py_ssize_t *)PyMem_Malloc(sizeof(Py_ssize_t)*view_src.ndim); + if (indices == NULL) { + PyErr_NoMemory(); + PyBuffer_Release(&view_dest); + PyBuffer_Release(&view_src); + return -1; + } + for (k=0; k=0; k--) { + strides[k] = sd; + sd *= shape[k]; + } + } + return; +} + +int +PyBuffer_FillInfo(Pep_buffer *view, PyObject *obj, void *buf, Py_ssize_t len, + int readonly, int flags) +{ + if (view == NULL) { + PyErr_SetString(PyExc_BufferError, + "PyBuffer_FillInfo: view==NULL argument is obsolete"); + return -1; + } + + if (((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) && + (readonly == 1)) { + PyErr_SetString(PyExc_BufferError, + "Object is not writable."); + return -1; + } + + view->obj = obj; + if (obj) + Py_INCREF(obj); + view->buf = buf; + view->len = len; + view->readonly = readonly; + view->itemsize = 1; + view->format = NULL; + if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) + view->format = (char *)"B"; // patched by CT + view->ndim = 1; + view->shape = NULL; + if ((flags & PyBUF_ND) == PyBUF_ND) + view->shape = &(view->len); + view->strides = NULL; + if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) + view->strides = &(view->itemsize); + view->suboffsets = NULL; + view->internal = NULL; + return 0; +} + +void +PyBuffer_Release(Pep_buffer *view) +{ + PyObject *obj = view->obj; + PyBufferProcs *pb; + if (obj == NULL) + return; + pb = PepType_AS_BUFFER(Py_TYPE(obj)); + if (pb && pb->bf_releasebuffer) + pb->bf_releasebuffer(obj, view); + view->obj = NULL; + Py_DECREF(obj); +} + +#endif // Py_LIMITED_API diff --git a/sources/shiboken2/libshiboken/bufferprocs27.h b/sources/shiboken2/libshiboken/bufferprocs27.h new file mode 100644 index 000000000..83c4a4750 --- /dev/null +++ b/sources/shiboken2/libshiboken/bufferprocs27.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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$ +** +****************************************************************************/ + +/* +PSF LICENSE AGREEMENT FOR PYTHON 3.6.5¶ +1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and + the Individual or Organization ("Licensee") accessing and otherwise using Python + 3.6.2 software in source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby + grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, + analyze, test, perform and/or display publicly, prepare derivative works, + distribute, and otherwise use Python 3.6.2 alone or in any derivative + version, provided, however, that PSF's License Agreement and PSF's notice of + copyright, i.e., "Copyright © 2001-2017 Python Software Foundation; All Rights + Reserved" are retained in Python 3.6.2 alone or in any derivative version + prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on or + incorporates Python 3.6.2 or any part thereof, and wants to make the + derivative work available to others as provided herein, then Licensee hereby + agrees to include in any such work a brief summary of the changes made to Python + 3.6.2. + +4. PSF is making Python 3.6.2 available to Licensee on an "AS IS" basis. + PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF + EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR + WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE + USE OF PYTHON 3.6.2 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.6.2 + FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF + MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.6.2, OR ANY DERIVATIVE + THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material breach of + its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any relationship + of agency, partnership, or joint venture between PSF and Licensee. This License + Agreement does not grant permission to use PSF trademarks or trade name in a + trademark sense to endorse or promote products or services of Licensee, or any + third party. + +8. By copying, installing or otherwise using Python 3.6.2, Licensee agrees + to be bound by the terms and conditions of this License Agreement. +*/ + +#ifndef BUFFER_REENABLE_H +#define BUFFER_REENABLE_H + +/* buffer interface */ +// This has been renamed to Pep_buffer and will be used. +typedef struct bufferinfo { + void *buf; + PyObject *obj; /* owned reference */ + Py_ssize_t len; + Py_ssize_t itemsize; /* This is Py_ssize_t so it can be + pointed to by strides in simple case.*/ + int readonly; + int ndim; + char *format; + Py_ssize_t *shape; + Py_ssize_t *strides; + Py_ssize_t *suboffsets; + void *internal; +} Pep_buffer; + +typedef int (*getbufferproc)(PyObject *, Pep_buffer *, int); +typedef void (*releasebufferproc)(PyObject *, Pep_buffer *); + +/* Maximum number of dimensions */ +#define PyBUF_MAX_NDIM 64 + +/* Flags for getting buffers */ +#define PyBUF_SIMPLE 0 +#define PyBUF_WRITABLE 0x0001 +/* we used to include an E, backwards compatible alias */ +#define PyBUF_WRITEABLE PyBUF_WRITABLE +#define PyBUF_FORMAT 0x0004 +#define PyBUF_ND 0x0008 +#define PyBUF_STRIDES (0x0010 | PyBUF_ND) +#define PyBUF_C_CONTIGUOUS (0x0020 | PyBUF_STRIDES) +#define PyBUF_F_CONTIGUOUS (0x0040 | PyBUF_STRIDES) +#define PyBUF_ANY_CONTIGUOUS (0x0080 | PyBUF_STRIDES) +#define PyBUF_INDIRECT (0x0100 | PyBUF_STRIDES) + +#define PyBUF_CONTIG (PyBUF_ND | PyBUF_WRITABLE) +#define PyBUF_CONTIG_RO (PyBUF_ND) + +#define PyBUF_STRIDED (PyBUF_STRIDES | PyBUF_WRITABLE) +#define PyBUF_STRIDED_RO (PyBUF_STRIDES) + +#define PyBUF_RECORDS (PyBUF_STRIDES | PyBUF_WRITABLE | PyBUF_FORMAT) +#define PyBUF_RECORDS_RO (PyBUF_STRIDES | PyBUF_FORMAT) + +#define PyBUF_FULL (PyBUF_INDIRECT | PyBUF_WRITABLE | PyBUF_FORMAT) +#define PyBUF_FULL_RO (PyBUF_INDIRECT | PyBUF_FORMAT) + + +#define PyBUF_READ 0x100 +#define PyBUF_WRITE 0x200 + +/* End buffer interface */ +LIBSHIBOKEN_API PyObject *PyMemoryView_FromBuffer(Pep_buffer *info); +#define Py_buffer Pep_buffer + +#endif // BUFFER_REENABLE_H diff --git a/sources/shiboken2/libshiboken/pep384impl.cpp b/sources/shiboken2/libshiboken/pep384impl.cpp new file mode 100644 index 000000000..2707d3716 --- /dev/null +++ b/sources/shiboken2/libshiboken/pep384impl.cpp @@ -0,0 +1,924 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pep384impl.h" + +extern "C" +{ + +/********************************************************************** + ********************************************************************** + + + The New Type API + ================ + + After converting everything but the "object.h" file, we could not + believe our eyes: it suddenly was clear that we would have no more + access to type objects, and even more scary that all types which we + use have to be heap types, only! + + For PySide with it's intense use of heap type extensions in various + flavors, it seemed to be quite unsolvable. In the end, it was + nicely solved, but it took almost 3.5 months to get that right. + + Before we see how this is done, we will explain the differences + between the APIs and their consequences. + + + The Interface + ------------- + + The old type API of Python knows static types and heap types. + Static types are written down as a declaration of a PyTypeObject + structure with all its fields filled in. Here is for example + the definition of the Python type "object": + + PyTypeObject PyBaseObject_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "object", |* tp_name *| + sizeof(PyObject), |* tp_basicsize *| + 0, |* tp_itemsize *| + object_dealloc, |* tp_dealloc *| + 0, |* tp_print *| + 0, |* tp_getattr *| + 0, |* tp_setattr *| + 0, |* tp_reserved *| + object_repr, |* tp_repr *| + 0, |* tp_as_number *| + 0, |* tp_as_sequence *| + 0, |* tp_as_mapping *| + (hashfunc)_Py_HashPointer, |* tp_hash *| + 0, |* tp_call *| + object_str, |* tp_str *| + PyObject_GenericGetAttr, |* tp_getattro *| + PyObject_GenericSetAttr, |* tp_setattro *| + 0, |* tp_as_buffer *| + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, |* tp_flags *| + PyDoc_STR("object()\n--\n\nThe most base type"), |* tp_doc *| + 0, |* tp_traverse *| + 0, |* tp_clear *| + object_richcompare, |* tp_richcompare *| + 0, |* tp_weaklistoffset *| + 0, |* tp_iter *| + 0, |* tp_iternext *| + object_methods, |* tp_methods *| + 0, |* tp_members *| + object_getsets, |* tp_getset *| + 0, |* tp_base *| + 0, |* tp_dict *| + 0, |* tp_descr_get *| + 0, |* tp_descr_set *| + 0, |* tp_dictoffset *| + object_init, |* tp_init *| + PyType_GenericAlloc, |* tp_alloc *| + object_new, |* tp_new *| + PyObject_Del, |* tp_free *| + }; + + We can write the same structure in form of a PyType_Spec structure, + and there is even a tool that does this for us, but I had to fix a + few things because there is little support for this. + + The tool is XXX go home and continue..... + + + + + The Transition To Simpler Types + =============================== + + After all code has been converted to the limited API, there is the + PyHeapTypeObject remaining as a problem. + + Why a problem? Well, all the type structures in shiboken use + special extra fields at the end of the heap type object. This + currently enforces knowledge at compile time about how large the + heap type object is. In a clean implementation, we would only use + the PyTypeObject itself and access the fields "behind" the type + by a pointer that is computed at runtime. + + + Excursion: PepTypeObject + ------------------------ + + Before we are going into details, let us motivate the existence of + the PepTypeObject, an alias to PyTypeObject: + + Originally, we wanted to use PyTypeObject as an opaque type and + restrict ourselves to only use the access function PyType_GetSlot. + This function allows access to all fields which are supported by + the limited API. + + But this is a restriction, because we get no access to tp_dict, + which we need to support the signature extension. But we can work + around that. + + The real restriction is that PyType_GetSlot only works for heap + types. This makes the function quite useless, because we have + no access to PyType_Type, which is the most important type "type" + in Python. We need that for instance to compute the size of + PyHeapTypeObject dynamically. + + With much effort, it is possible to clone PyType_Type as a heap + type. But due to a bug in the Pep 384 support, we need + access to the nb_index field of a normal type. Cloning does not + help because PyNumberMethods fields are not inherited. + + After I realized this dead end, I changed the concept and did not + use PyType_GetSlot at all (except in function copyNumberMethods), + but created PepTypeObject as a remake of PyTypeObject with only + those fields defined that are needed in PySide. + + Is this breakage of the limited API? I don't think so. A special + function runs on program startup that checks the correct position + of the fields of PepHeapType, although a change in those fields is + more than unlikely. + The really crucial thing is to no longer use PyHeapTypeObject + explicitly because that _does_ change its layout over time. + + + Diversification + --------------- + + There are multiple SbkXXX structures which all use a "d" field + for their private data. This makes it not easy to find the right + fields when switching between types and objects. + + struct LIBSHIBOKEN_API SbkObjectType + { + PyHeapTypeObject super; + SbkObjectTypePrivate *d; + }; + + struct LIBSHIBOKEN_API SbkObject + { + PyObject_HEAD + PyObject* ob_dict; + PyObject* weakreflist; + SbkObjectPrivate* d; + }; + + The first step was to rename the SbkObjectTypePrivate from "d" to + "sotp". It was chosen to be short but easy to remember. + + + Abstraction + ----------- + + After renaming the type extension pointers to "sotp", I replaced + them by function-like macros which did the special access "behind" + the types, instead of those explicit fields. For instance, the + expression + + type->sotp->converter + + became + + PepType_SOTP(type)->converter + + The macro expression can be seen here: + + #define _genericTypeExtender(etype) \ + (reinterpret_cast(etype) + \ + (reinterpret_cast(&PyType_Type))->tp_basicsize) + + #define PepType_SOTP(etype) \ + (*reinterpret_cast(_genericTypeExtender(etype))) + + It looks complicated, but in the end there is only a single new + indirection via PyType_Type, which happens at runtime. This is the + key to fulfil what Pep 384 wants: No version-dependent fields. + + + Simplification + -------------- + + After all type extension fields were replaced by macro calls, we + could remove the version dependent definition + + typedef struct _pepheaptypeobject { + union { + PepTypeObject ht_type; + void *opaque[PY_HEAPTYPE_SIZE]; + }; + } PepHeapTypeObject; + + and the version dependent structure + + struct LIBSHIBOKEN_API SbkObjectType + { + PepHeapTypeObject super; + SbkObjectTypePrivate *sotp; + }; + + could be replaced by the simplified + + struct LIBSHIBOKEN_API SbkObjectType + { + PepTypeObject type; + }; + + which is no longer version-dependent. + + + Verification Of PepTypeObject + ============================= + + We have introduced PepTypeObject as a new alias for PyTypeObject, + and now we need to prove that we are allowed to do so. + + When using the limited API as intended, then types are completely + opaque, and access is only through PyType_FromSpec and (from + version 3.5 upwards) through PyType_GetSlot. + + Python then uses all the slot definitions in the type description + and produces a regular type object. + + + Unused Information + ------------------ + + But we know many things about types that are not explicitly said, + but they are inherently clear: + + a) The basic structure of a type is always the same, regardless + if it is a static type or a heap type. + + b) types are evolving very slowly, and a field is never replaced + by another field with different semantics. + + Inherent rule a) gives us the following information: If we calculate + the offsets of the fields, then this info is also usable for non- + -heap types. + + The validation checks if rule b) is still valid. + + + How it Works + ------------ + + The basic idea of the validation is to produce a new type using + PyType_FromSpec and to see where in the type structure these fields + show up. So we build a PyType_Slot structure with all the fields we + are using and make sure that these values are all unique in the + type. + + Most fields are not investigated by PyType_FromSpec, and so we + simply used some numeric value. Some fields are interpreted, like + tp_members. This field must really be a PyMemberDef. And there are + tp_base and tp_bases which have to be type objects and lists + thereof. It was easiest to not produce these fields from scratch + but use them from the "type" object PyType_Type. + + Then one would think to write a function that searches the known + values in the opaque type structure. + + But we can do better and use optimistically the observation (b): + We simply use the PepTypeObject structure and assume that every + field lands exactly where we are awaiting it. + + And that is the whole proof: If we find all the disjoint values at + the places where we expect them, thenthis is q.e.d. :) + + + About tp_dict + ------------- + + One word about the tp_dict field: This field is a bit special in + the proof, since it does not appear in the spec and cannot easily + be checked by "type.__dict__" because that creates a dictproxy + object. So how do we proove that is really the right dict? + + We have to create that PyMethodDef structure anyway, and instead of + leaving it empty, we insert a dummy function. Then we ask the + tp_dict field if it has that object in it, and that's q.e.d. + + + *********/ + + +/***************************************************************************** + * + * Support for object.h + * + */ + +/* + * Here is the verification code for PepTypeObject. + * We create a type object and check if its fields + * appear at the right offsets. + */ + +#define make_dummy_int(x) (x * sizeof(void*)) +#define make_dummy(x) (reinterpret_cast(make_dummy_int(x))) + +#ifdef Py_LIMITED_API +datetime_struc *PyDateTimeAPI = NULL; +#endif + +static PyObject * +dummy_func(PyObject *self, PyObject *args) +{ + Py_RETURN_NONE; +} + +static struct PyMethodDef probe_methoddef[] = { + {"dummy", dummy_func, METH_NOARGS}, + {0} +}; + +#define probe_tp_call make_dummy(1) +#define probe_tp_str make_dummy(2) +#define probe_tp_traverse make_dummy(3) +#define probe_tp_clear make_dummy(4) +#define probe_tp_methods probe_methoddef +#define probe_tp_descr_get make_dummy(6) +#define probe_tp_init make_dummy(7) +#define probe_tp_alloc make_dummy(8) +#define probe_tp_new make_dummy(9) +#define probe_tp_free make_dummy(10) +#define probe_tp_is_gc make_dummy(11) + +#define probe_tp_name "type.probe" +#define probe_tp_basicsize make_dummy_int(42) + +static PyType_Slot typeprobe_slots[] = { + {Py_tp_call, probe_tp_call}, + {Py_tp_str, probe_tp_str}, + {Py_tp_traverse, probe_tp_traverse}, + {Py_tp_clear, probe_tp_clear}, + {Py_tp_methods, probe_tp_methods}, + {Py_tp_descr_get, probe_tp_descr_get}, + {Py_tp_init, probe_tp_init}, + {Py_tp_alloc, probe_tp_alloc}, + {Py_tp_new, probe_tp_new}, + {Py_tp_free, probe_tp_free}, + {Py_tp_is_gc, probe_tp_is_gc}, + {0, 0} +}; +static PyType_Spec typeprobe_spec = { + probe_tp_name, + probe_tp_basicsize, + 0, + Py_TPFLAGS_DEFAULT, + typeprobe_slots, +}; + +static void +check_PepTypeObject_valid(void) +{ + PyObject *obtype = reinterpret_cast(&PyType_Type); + PyTypeObject *probe_tp_base = reinterpret_cast( + PyObject_GetAttrString(obtype, "__base__")); + PyObject *probe_tp_bases = PyObject_GetAttrString(obtype, "__bases__"); + PepTypeObject *check = reinterpret_cast( + PyType_FromSpecWithBases(&typeprobe_spec, probe_tp_bases)); + PepTypeObject *typetype = reinterpret_cast(obtype); + PyObject *w = PyObject_GetAttrString(obtype, "__weakrefoffset__"); + long probe_tp_weakrefoffset = PyLong_AsLong(w); + PyObject *d = PyObject_GetAttrString(obtype, "__dictoffset__"); + long probe_tp_dictoffset = PyLong_AsLong(d); + PyObject *probe_tp_mro = PyObject_GetAttrString(obtype, "__mro__"); + if (false + || probe_tp_name != check->tp_name + || probe_tp_basicsize != check->tp_basicsize + || probe_tp_call != check->tp_call + || probe_tp_str != check->tp_str + || probe_tp_traverse != check->tp_traverse + || probe_tp_clear != check->tp_clear + || probe_tp_weakrefoffset != typetype->tp_weaklistoffset + || probe_tp_methods != check->tp_methods + || probe_tp_base != typetype->tp_base + || !PyDict_Check(check->tp_dict) + || !PyDict_GetItemString(check->tp_dict, "dummy") + || probe_tp_descr_get != check->tp_descr_get + || probe_tp_dictoffset != typetype->tp_dictoffset + || probe_tp_init != check->tp_init + || probe_tp_alloc != check->tp_alloc + || probe_tp_new != check->tp_new + || probe_tp_free != check->tp_free + || probe_tp_is_gc != check->tp_is_gc + || probe_tp_bases != typetype->tp_bases + || probe_tp_mro != typetype->tp_mro) + Py_FatalError("The structure of type objects has changed!"); + Py_DECREF(check); + Py_DECREF(probe_tp_base); + Py_DECREF(w); + Py_DECREF(d); + Py_DECREF(probe_tp_bases); + Py_DECREF(probe_tp_mro); +} + + +#ifdef Py_LIMITED_API + +// This structure is only here because Python 3 has an error. +// I will fix that. + +typedef struct { + /* Number implementations must check *both* + arguments for proper type and implement the necessary conversions + in the slot functions themselves. */ + + binaryfunc nb_add; + binaryfunc nb_subtract; + binaryfunc nb_multiply; + binaryfunc nb_remainder; + binaryfunc nb_divmod; + ternaryfunc nb_power; + unaryfunc nb_negative; + unaryfunc nb_positive; + unaryfunc nb_absolute; + inquiry nb_bool; + unaryfunc nb_invert; + binaryfunc nb_lshift; + binaryfunc nb_rshift; + binaryfunc nb_and; + binaryfunc nb_xor; + binaryfunc nb_or; + unaryfunc nb_int; + void *nb_reserved; /* the slot formerly known as nb_long */ + unaryfunc nb_float; + + binaryfunc nb_inplace_add; + binaryfunc nb_inplace_subtract; + binaryfunc nb_inplace_multiply; + binaryfunc nb_inplace_remainder; + ternaryfunc nb_inplace_power; + binaryfunc nb_inplace_lshift; + binaryfunc nb_inplace_rshift; + binaryfunc nb_inplace_and; + binaryfunc nb_inplace_xor; + binaryfunc nb_inplace_or; + + binaryfunc nb_floor_divide; + binaryfunc nb_true_divide; + binaryfunc nb_inplace_floor_divide; + binaryfunc nb_inplace_true_divide; + + unaryfunc nb_index; + + binaryfunc nb_matrix_multiply; + binaryfunc nb_inplace_matrix_multiply; +} PyNumberMethods; + +// temporary structure until we have a generator for the offsets +typedef struct _oldtypeobject { + PyVarObject ob_base; + void *X01; // const char *tp_name; + void *X02; // Py_ssize_t tp_basicsize; + void *X03; // Py_ssize_t tp_itemsize; + void *X04; // destructor tp_dealloc; + void *X05; // printfunc tp_print; + void *X06; // getattrfunc tp_getattr; + void *X07; // setattrfunc tp_setattr; + void *X08; // PyAsyncMethods *tp_as_async; + void *X09; // reprfunc tp_repr; + PyNumberMethods *tp_as_number; + +} PyOldTypeObject; + +// There is a bug in Python 3.6 that turned the Index_Check function +// into a macro without taking care of the limited API. +// This leads to the single problem that we don't have +// access to PyLong_Type's nb_index field which is no heap type. +// We cannot easily create this function by inheritance since it is +// not inherited. +// +// Simple solution: Create the structure and write such a function. +// Long term: Submit a patch to python.org . + +unaryfunc +PepType_nb_index(PyTypeObject *type) +{ + return reinterpret_cast(type)->tp_as_number->nb_index; +} + +int PyIndex_Check(PyObject *obj) +{ + PyOldTypeObject *type = reinterpret_cast(Py_TYPE(obj)); + return type->tp_as_number != NULL && + type->tp_as_number->nb_index != NULL; +} + +/***************************************************************************** + * + * Support for unicodeobject.h + * + */ + +char * +_PepUnicode_AsString(PyObject *str) +{ + /* + * We need to keep the string alive but cannot borrow the Python object. + * Ugly easy way out: We re-code as an interned bytes string. This + * produces a pseudo-leak as long there are new strings. + * Typically, this function is used for name strings, and the dict size + * will not grow so much. + */ +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) +#define AT __FILE__ ":" TOSTRING(__LINE__) + + static PyObject *cstring_dict = NULL; + if (cstring_dict == NULL) { + cstring_dict = PyDict_New(); + if (cstring_dict == NULL) + Py_FatalError("Error in " AT); + } + PyObject *bytesStr = PyUnicode_AsEncodedString(str, "utf8", NULL); + PyObject *entry = PyDict_GetItem(cstring_dict, bytesStr); + if (entry == NULL) { + int e = PyDict_SetItem(cstring_dict, bytesStr, bytesStr); + if (e != 0) + Py_FatalError("Error in " AT); + entry = bytesStr; + } + else + Py_DECREF(bytesStr); + return PyBytes_AsString(entry); +} + +/***************************************************************************** + * + * Support for longobject.h + * + */ + +/* + * This is the original Python function _PyLong_AsInt() from longobject.c . + * We define it here because we are not allowed to use the function + * from Python with an underscore. + */ + +/* Get a C int from an int object or any object that has an __int__ + method. Return -1 and set an error if overflow occurs. */ + +int +_PepLong_AsInt(PyObject *obj) +{ + int overflow; + long result = PyLong_AsLongAndOverflow(obj, &overflow); + if (overflow || result > INT_MAX || result < INT_MIN) { + /* XXX: could be cute and give a different + message for overflow == -1 */ + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C int"); + return -1; + } + return (int)result; +} + +/***************************************************************************** + * + * Support for pydebug.h + * + */ +static PyObject *sys_flags = NULL; + +int +Pep_GetFlag(const char *name) +{ + static int initialized = 0; + int ret = -1; + + if (!initialized) { + sys_flags = PySys_GetObject("flags"); + // func gives no error if NULL is returned and does not incref. + Py_XINCREF(sys_flags); + initialized = 1; + } + if (sys_flags != NULL) { + PyObject *ob_ret = PyObject_GetAttrString(sys_flags, name); + if (ob_ret != NULL) { + long long_ret = PyLong_AsLong(ob_ret); + Py_DECREF(ob_ret); + ret = (int) long_ret; + } + } + return ret; +} + +int +Pep_GetVerboseFlag() +{ + static int initialized = 0; + static int verbose_flag = -1; + + if (!initialized) { + verbose_flag = Pep_GetFlag("verbose"); + if (verbose_flag != -1) + initialized = 1; + } + return verbose_flag; +} + +/***************************************************************************** + * + * Support for code.h + * + */ + +int +PepCode_Get(PyCodeObject *co, const char *name) +{ + PyObject *ob = (PyObject *)co; + PyObject *ob_ret; + int ret = -1; + + ob_ret = PyObject_GetAttrString(ob, name); + if (ob_ret != NULL) { + long long_ret = PyLong_AsLong(ob_ret); + Py_DECREF(ob_ret); + ret = (int) long_ret; + } + return ret; +} + +/***************************************************************************** + * + * Support for datetime.h + * + */ + +static PyTypeObject *dt_getCheck(const char *name) +{ + PyObject *op = PyObject_GetAttrString(PyDateTimeAPI->module, name); + if (op == NULL) { + fprintf(stderr, "datetime.%s not found\n", name); + Py_FatalError("aborting"); + } + return (PyTypeObject *)op; +} + +// init_DateTime is called earlier than our module init. +// We use the provided PyDateTime_IMPORT machinery. +datetime_struc * +init_DateTime(void) +{ + static int initialized = 0; + if (!initialized) { + PyDateTimeAPI = (datetime_struc *)malloc(sizeof(datetime_struc)); + if (PyDateTimeAPI == NULL) + Py_FatalError("PyDateTimeAPI malloc error, aborting"); + PyDateTimeAPI->module = PyImport_ImportModule("datetime"); + if (PyDateTimeAPI->module == NULL) + Py_FatalError("datetime module not found, aborting"); + PyDateTimeAPI->DateType = dt_getCheck("date"); + PyDateTimeAPI->DateTimeType = dt_getCheck("datetime"); + PyDateTimeAPI->TimeType = dt_getCheck("time"); + PyDateTimeAPI->DeltaType = dt_getCheck("timedelta"); + PyDateTimeAPI->TZInfoType = dt_getCheck("tzinfo"); + initialized = 1; + } + return PyDateTimeAPI; +} + +int +PyDateTime_Get(PyObject *ob, const char *name) +{ + PyObject *ob_ret; + int ret = -1; + + ob_ret = PyObject_GetAttrString(ob, name); + if (ob_ret != NULL) { + long long_ret = PyLong_AsLong(ob_ret); + Py_DECREF(ob_ret); + ret = (int) long_ret; + } + return ret; +} + +PyObject * +PyDate_FromDate(int year, int month, int day) +{ + return PyObject_CallFunction((PyObject *)PyDateTimeAPI->DateType, + (char *)"(iii)", year, month, day); +} + +PyObject * +PyDateTime_FromDateAndTime(int year, int month, int day, + int hour, int min, int sec, int usec) +{ + return PyObject_CallFunction((PyObject *)PyDateTimeAPI->DateTimeType, + (char *)"(iiiiiii)", year, month, day, + hour, min, sec, usec); +} + +PyObject * +PyTime_FromTime(int hour, int min, int sec, int usec) +{ + return PyObject_CallFunction((PyObject *)PyDateTimeAPI->TimeType, + (char *)"(iiii)", hour, min, sec, usec); +} + +/***************************************************************************** + * + * Support for pythonrun.h + * + */ + +// Flags are ignored in these simple helpers. +PyObject * +PyRun_String(const char *str, int start, PyObject *globals, PyObject *locals) +{ + PyObject* code = Py_CompileString(str, "pyscript", start); + PyObject* ret = NULL; + + if (code != NULL) { + ret = PyEval_EvalCode(code, globals, locals); + } + Py_XDECREF(code); + return ret; +} + +// This is only a simple local helper that returns a computed variable. +static PyObject * +PepRun_GetResult(const char *command, const char *resvar) +{ + PyObject *d, *v, *res; + + d = PyDict_New(); + if (d == NULL || PyDict_SetItemString(d, "__builtins__", + PyEval_GetBuiltins()) < 0) + return NULL; + v = PyRun_String(command, Py_file_input, d, d); + res = v ? PyDict_GetItemString(d, resvar) : NULL; + Py_XDECREF(v); + Py_DECREF(d); + return res; +} + +/***************************************************************************** + * + * Support for classobject.h + * + */ + +PyTypeObject *PepMethod_TypePtr = NULL; + +static PyTypeObject *getMethodType(void) +{ + static const char prog[] = + "class _C:\n" + " def _m(self): pass\n" + "MethodType = type(_C()._m)\n"; + return (PyTypeObject *) PepRun_GetResult(prog, "MethodType"); +} + +// We have no access to PyMethod_New and must call types.MethodType, instead. +PyObject * +PyMethod_New(PyObject *func, PyObject *self) +{ + return PyObject_CallFunction((PyObject *)PepMethod_TypePtr, + (char *)"(OO)", func, self); +} + +PyObject * +PyMethod_Function(PyObject *im) +{ + PyObject *ret = PyObject_GetAttrString(im, "__func__"); + + // We have to return a borrowed reference. + Py_DECREF(ret); + return ret; +} + +PyObject * +PyMethod_Self(PyObject *im) +{ + PyObject *ret = PyObject_GetAttrString(im, "__self__"); + + // We have to return a borrowed reference. + // If we don't obey that here, then we get a test error! + Py_DECREF(ret); + return ret; +} + +/***************************************************************************** + * + * Support for funcobject.h + * + */ + +PyObject * +PepFunction_Get(PyObject *ob, const char *name) +{ + PyObject *ret; + + // We have to return a borrowed reference. + ret = PyObject_GetAttrString(ob, name); + Py_XDECREF(ret); + return ret; +} + +/***************************************************************************** + * + * Support for funcobject.h + * + */ + +// this became necessary after Windows was activated. + +PyTypeObject *PepFunction_TypePtr = NULL; + +static PyTypeObject *getFunctionType(void) +{ + static const char prog[] = + "from types import FunctionType\n"; + return (PyTypeObject *) PepRun_GetResult(prog, "FunctionType"); +} + +/***************************************************************************** + * + * Extra support for signature.cpp + * + */ + +PyTypeObject *PepStaticMethod_TypePtr = NULL; + +static PyTypeObject *getStaticMethodType(void) +{ + static const char prog[] = + "StaticMethodType = type(str.__dict__['maketrans'])\n"; + return (PyTypeObject *) PepRun_GetResult(prog, "StaticMethodType"); +} + +#endif // Py_LIMITED_API + +/***************************************************************************** + * + * Common newly needed functions + * + */ + +// The introduction of heaptypes converted many type names to the +// dotted form, since PyType_FromSpec uses it to compute the module +// name. This function reverts this effect. +const char * +PepType_GetNameStr(PyTypeObject *type) +{ + const char *ret = PepType(type)->tp_name; + const char *nodots = strrchr(ret, '.'); + if (nodots) + ret = nodots + 1; + return ret; +} + +/***************************************************************************** + * + * Module Initialization + * + */ + +void +Pep_Init() +{ + check_PepTypeObject_valid(); +#ifdef Py_LIMITED_API + Pep_GetVerboseFlag(); + PepMethod_TypePtr = getMethodType(); + PepFunction_TypePtr = getFunctionType(); + PepStaticMethod_TypePtr = getStaticMethodType(); +#endif +} + +} // extern "C" diff --git a/sources/shiboken2/libshiboken/pep384impl.h b/sources/shiboken2/libshiboken/pep384impl.h new file mode 100644 index 000000000..fc0e3b40e --- /dev/null +++ b/sources/shiboken2/libshiboken/pep384impl.h @@ -0,0 +1,571 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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$ +** +****************************************************************************/ + +#ifndef PEP384IMPL_H +#define PEP384IMPL_H + +#include "sbkpython.h" + +extern "C" +{ + +/***************************************************************************** + * + * RESOLVED: memoryobject.h + * + */ + +// Extracted into bufferprocs27.h +#ifdef Py_LIMITED_API +#include "bufferprocs27.h" +#endif + +/***************************************************************************** + * + * RESOLVED: object.h + * + */ +#ifdef Py_LIMITED_API +// Why the hell is this useful debugging function not allowed? +LIBSHIBOKEN_API void _PyObject_Dump(PyObject *); +#endif + +/* + * There are a few structures that are needed, but cannot be used without + * breaking the API. We use some heuristics to get those fields anyway + * and validate that we really found them, see Pepresolve.cpp . + */ + +// PepType is just a typecast that allows direct access. This is +// often better to read than the reversal via the former macro +// functions PepType_tp_xxx. +#define PepType(o) (reinterpret_cast(o)) + +#ifdef Py_LIMITED_API + +/* + * These are the type object fields that we use. + * We will verify that they never change. + * The unused fields are intentionally named as "void *Xnn" because + * the chance is smaller to forget to validate a field. + * When we need more fields, we replace it back and add it to the + * validation. + */ +typedef struct _peptypeobject { + PyVarObject ob_base; + const char *tp_name; + Py_ssize_t tp_basicsize; + void *X03; // Py_ssize_t tp_itemsize; + void *X04; // destructor tp_dealloc; + void *X05; // printfunc tp_print; + void *X06; // getattrfunc tp_getattr; + void *X07; // setattrfunc tp_setattr; + void *X08; // PyAsyncMethods *tp_as_async; + void *X09; // reprfunc tp_repr; + void *X10; // PyNumberMethods *tp_as_number; + void *X11; // PySequenceMethods *tp_as_sequence; + void *X12; // PyMappingMethods *tp_as_mapping; + void *X13; // hashfunc tp_hash; + ternaryfunc tp_call; + reprfunc tp_str; + void *X16; // getattrofunc tp_getattro; + void *X17; // setattrofunc tp_setattro; + void *X18; // PyBufferProcs *tp_as_buffer; + void *X19; // unsigned long tp_flags; + void *X20; // const char *tp_doc; + traverseproc tp_traverse; + inquiry tp_clear; + void *X23; // richcmpfunc tp_richcompare; + Py_ssize_t tp_weaklistoffset; + void *X25; // getiterfunc tp_iter; + void *X26; // iternextfunc tp_iternext; + struct PyMethodDef *tp_methods; + void *X28; // struct PyMemberDef *tp_members; + void *X29; // struct PyGetSetDef *tp_getset; + struct _typeobject *tp_base; + PyObject *tp_dict; + descrgetfunc tp_descr_get; + void *X33; // descrsetfunc tp_descr_set; + Py_ssize_t tp_dictoffset; + initproc tp_init; + allocfunc tp_alloc; + newfunc tp_new; + freefunc tp_free; + inquiry tp_is_gc; /* For PyObject_IS_GC */ + PyObject *tp_bases; + PyObject *tp_mro; /* method resolution order */ + +} PepTypeObject; + +LIBSHIBOKEN_API unaryfunc PepType_nb_index(PyTypeObject *type); + +#undef PyIndex_Check + +LIBSHIBOKEN_API int PyIndex_Check(PyObject *obj); + +#undef PyObject_IS_GC +#define PyObject_IS_GC(o) (PyType_IS_GC(Py_TYPE(o)) && \ + ( PepType(Py_TYPE(o))->tp_is_gc == NULL || \ + PepType(Py_TYPE(o))->tp_is_gc(o) )) + +#else +#define PepTypeObject PyTypeObject +#define PepType_nb_index(o) (PepType(o)->nb_index) +#endif // Py_LIMITED_API + +struct SbkObjectTypePrivate; +struct PySideQFlagsTypePrivate; +struct _SbkGenericTypePrivate; + +#define PepHeapType_SIZE \ + (reinterpret_cast(&PyType_Type)->tp_basicsize) + +#define _genericTypeExtender(etype) \ + (reinterpret_cast(etype) + PepHeapType_SIZE) + +#define PepType_SOTP(etype) \ + (*reinterpret_cast(_genericTypeExtender(etype))) + +#define PepType_SETP(etype) \ + (reinterpret_cast(_genericTypeExtender(etype))) + +#define PepType_PFTP(etype) \ + (reinterpret_cast(_genericTypeExtender(etype))) + +#define PepType_SGTP(etype) \ + (reinterpret_cast<_SbkGenericTypePrivate*>(_genericTypeExtender(etype))) + +// functions used everywhere +LIBSHIBOKEN_API const char *PepType_GetNameStr(PyTypeObject *type); + +/***************************************************************************** + * + * RESOLVED: longobject.h + * + */ +#ifdef Py_LIMITED_API +LIBSHIBOKEN_API int _PepLong_AsInt(PyObject *); +#else +#define _PepLong_AsInt _PyLong_AsInt +#endif + +/***************************************************************************** + * + * RESOLVED: pydebug.h + * + */ +#ifdef Py_LIMITED_API +/* + * We have no direct access to Py_VerboseFlag because debugging is not + * supported. The python developers are partially a bit too rigorous. + * Instead, we compute the value and use a function call macro. + * Was before: extern LIBSHIBOKEN_API int Py_VerboseFlag; + */ +LIBSHIBOKEN_API int Pep_GetFlag(const char *name); +LIBSHIBOKEN_API int Pep_GetVerboseFlag(void); +#define Py_VerboseFlag Pep_GetVerboseFlag() +#endif + +/***************************************************************************** + * + * RESOLVED: unicodeobject.h + * + */ +#ifdef Py_LIMITED_API + +LIBSHIBOKEN_API char *_PepUnicode_AsString(PyObject *); + +#define PyUnicode_GET_SIZE(op) PyUnicode_GetSize((PyObject *)(op)) + +#else +#define _PepUnicode_AsString PyUnicode_AsUTF8 +#endif + +/***************************************************************************** + * + * RESOLVED: bytesobject.h + * + */ +#ifdef Py_LIMITED_API +#define PyBytes_AS_STRING(op) PyBytes_AsString(op) +#define PyBytes_GET_SIZE(op) PyBytes_Size(op) +#endif + +/***************************************************************************** + * + * RESOLVED: floatobject.h + * + */ +#ifdef Py_LIMITED_API +#define PyFloat_AS_DOUBLE(op) PyFloat_AsDouble(op) +#endif + +/***************************************************************************** + * + * RESOLVED: tupleobject.h + * + */ +#ifdef Py_LIMITED_API +#define PyTuple_GET_ITEM(op, i) PyTuple_GetItem((PyObject *)op, i) +#define PyTuple_GET_SIZE(op) PyTuple_Size((PyObject *)op) +#define PyTuple_SET_ITEM(op, i, v) PyTuple_SetItem(op, i, v) +#endif + +/***************************************************************************** + * + * RESOLVED: listobject.h + * + */ +#ifdef Py_LIMITED_API +#define PyList_GET_ITEM(op, i) PyList_GetItem(op, i) +#define PyList_SET_ITEM(op, i, v) PyList_SetItem(op, i, v) +#define PyList_GET_SIZE(op) PyList_Size(op) +#endif + +/***************************************************************************** + * + * RESOLVED: methodobject.h + * + */ + +#ifdef Py_LIMITED_API + +typedef struct _pycfunc PyCFunctionObject; +#define PyCFunction_GET_FUNCTION(func) PyCFunction_GetFunction((PyObject *)func) +#define PyCFunction_GET_SELF(func) PyCFunction_GetSelf((PyObject *)func) +#define PyCFunction_GET_FLAGS(func) PyCFunction_GetFlags((PyObject *)func) +#define PepCFunction_GET_NAMESTR(func) \ + _PepUnicode_AsString(PyObject_GetAttrString((PyObject *)func, "__name__")) +#else +#define PepCFunction_GET_NAMESTR(func) ((func)->m_ml->ml_name) +#endif + +/***************************************************************************** + * + * RESOLVED: descrobject.h + * + */ +#ifdef Py_LIMITED_API +typedef struct _methoddescr PyMethodDescrObject; +#endif + +/***************************************************************************** + * + * RESOLVED: pystate.h + * + */ + +/* + * pystate provides the data structure that is needed for the trashcan + * algorithm. Unfortunately, it is not included in the limited API. + * We have two options: + * + * (1) ignore trashcan and live without secured deeply nested structures, + * (2) maintain the structure ourselves and make sure it does not change. + * + * I have chosen the second option. + * + * When a new python version appears, you need to check compatibility of + * the PyThreadState structure (pystate.h) and the trashcan macros at the + * end of object.h . + */ + +#ifdef Py_LIMITED_API + +#define Py_TRASH_MIN_COMPATIBLE 0x03020400 +#define Py_TRASH_MAX_COMPATIBLE 0x030700A0 + +#if PY_VERSION_HEX >= Py_TRASH_MIN_COMPATIBLE && \ + PY_VERSION_HEX <= Py_TRASH_MAX_COMPATIBLE +typedef int (*Py_tracefunc)(PyObject *, struct _frame *, int, PyObject *); + +// This structure has the trashcan variables since Python 3.2.4. +// We renamed all but the trashcan fields to make sure that we don't use +// anything else somewhere. + +typedef struct _ts { + struct _ts *Pep_prev; + struct _ts *Pep_next; + PyInterpreterState *Pep_interp; + + struct _frame *Pep_frame; + int Pep_recursion_depth; + char Pep_overflowed; + char Pep_recursion_critical; + + int Pep_tracing; + int Pep_use_tracing; + + Py_tracefunc Pep_c_profilefunc; + Py_tracefunc Pep_c_tracefunc; + PyObject *Pep_c_profileobj; + PyObject *Pep_c_traceobj; + + PyObject *Pep_curexc_type; + PyObject *Pep_curexc_value; + PyObject *Pep_curexc_traceback; + + PyObject *Pep_exc_type; + PyObject *Pep_exc_value; + PyObject *Pep_exc_traceback; + + PyObject *Pep_dict; + + int Pep_gilstate_counter; + + PyObject *Pep_async_exc; + long Pep_thread_id; + // These two variables only are of interest to us. + int trash_delete_nesting; + PyObject *trash_delete_later; + // Here we cut away the rest of the reduced structure. +} PyThreadState; +#else +#error *** Please check compatibility of the trashcan code, see Pep.h *** +#endif + +#endif // Py_LIMITED_API + +/***************************************************************************** + * + * RESOLVED: pythonrun.h + * + */ +#ifdef Py_LIMITED_API +LIBSHIBOKEN_API PyObject *PyRun_String(const char *, int, PyObject *, PyObject *); +#endif + +/***************************************************************************** + * + * RESOLVED: abstract.h + * + */ +#ifdef Py_LIMITED_API + +// This definition breaks the limited API a little, because it re-enables the +// buffer functions. +// But this is no problem as we check it's validity for every version. + +#define PYTHON_BUFFER_VERSION_COMPATIBLE (PY_VERSION_HEX >= 0x03030000 && \ + PY_VERSION_HEX < 0X0306FFFF) +#if !PYTHON_BUFFER_VERSION_COMPATIBLE +# error Please check the buffer compatibility for this python version! +#endif + +typedef struct { + getbufferproc bf_getbuffer; + releasebufferproc bf_releasebuffer; +} PyBufferProcs; + +typedef struct _Pepbuffertype { + PyVarObject ob_base; + void *skip[17]; + PyBufferProcs *tp_as_buffer; +} PepBufferType; + +#define PepType_AS_BUFFER(type) \ + reinterpret_cast(type)->tp_as_buffer + +#define PyObject_CheckBuffer(obj) \ + ((PepType_AS_BUFFER(Py_TYPE(obj)) != NULL) && \ + (PepType_AS_BUFFER(Py_TYPE(obj))->bf_getbuffer != NULL)) + +LIBSHIBOKEN_API int PyObject_GetBuffer(PyObject *ob, Pep_buffer *view, int flags); +LIBSHIBOKEN_API void PyBuffer_Release(Pep_buffer *view); + +#else + +#define Pep_buffer Py_buffer + +#endif /* Py_LIMITED_API */ + +/***************************************************************************** + * + * RESOLVED: funcobject.h + * + */ +#ifdef Py_LIMITED_API +typedef struct _func PyFunctionObject; + +extern LIBSHIBOKEN_API PyTypeObject *PepFunction_TypePtr; +LIBSHIBOKEN_API PyObject *PepFunction_Get(PyObject *, const char *); + +#define PyFunction_Check(op) (Py_TYPE(op) == PepFunction_TypePtr) +#define PyFunction_GET_CODE(func) PyFunction_GetCode(func) + +#define PyFunction_GetCode(func) PepFunction_Get((PyObject *)func, "__code__") +#define PepFunction_GetName(func) PepFunction_Get((PyObject *)func, "__name__") +#else +#define PepFunction_GetName(func) (((PyFunctionObject *)func)->func_name) +#endif + +/***************************************************************************** + * + * RESOLVED: classobject.h + * + */ +#ifdef Py_LIMITED_API + +typedef struct _meth PyMethodObject; + +extern LIBSHIBOKEN_API PyTypeObject *PepMethod_TypePtr; + +LIBSHIBOKEN_API PyObject *PyMethod_New(PyObject *, PyObject *); +LIBSHIBOKEN_API PyObject *PyMethod_Function(PyObject *); +LIBSHIBOKEN_API PyObject *PyMethod_Self(PyObject *); + +#define PyMethod_Check(op) ((op)->ob_type == PepMethod_TypePtr) + +#define PyMethod_GET_SELF(op) PyMethod_Self(op) +#define PyMethod_GET_FUNCTION(op) PyMethod_Function(op) +#endif + +/***************************************************************************** + * + * RESOLVED: code.h + * + */ +#ifdef Py_LIMITED_API +/* Bytecode object */ + // we have to grab the code object from python +typedef struct _code PyCodeObject; + +LIBSHIBOKEN_API int PepCode_Get(PyCodeObject *co, const char *name); + +#define PepCode_GET_FLAGS(o) PepCode_Get(o, "co_flags") +#define PepCode_GET_ARGCOUNT(o) PepCode_Get(o, "co_argcount") + +/* Masks for co_flags above */ +#define CO_OPTIMIZED 0x0001 +#define CO_NEWLOCALS 0x0002 +#define CO_VARARGS 0x0004 +#define CO_VARKEYWORDS 0x0008 +#define CO_NESTED 0x0010 +#define CO_GENERATOR 0x0020 +#else +#define PepCode_GET_FLAGS(o) ((o)->co_flags) +#define PepCode_GET_ARGCOUNT(o) ((o)->co_argcount) +#endif + +/***************************************************************************** + * + * RESOLVED: datetime.h + * + */ +#ifdef Py_LIMITED_API + +LIBSHIBOKEN_API int PyDateTime_Get(PyObject *ob, const char *name); + +#define PyDateTime_GetYear(o) PyDateTime_Get(o, "year") +#define PyDateTime_GetMonth(o) PyDateTime_Get(o, "month") +#define PyDateTime_GetDay(o) PyDateTime_Get(o, "day") +#define PyDateTime_GetHour(o) PyDateTime_Get(o, "hour") +#define PyDateTime_GetMinute(o) PyDateTime_Get(o, "minute") +#define PyDateTime_GetSecond(o) PyDateTime_Get(o, "second") +#define PyDateTime_GetMicrosecond(o) PyDateTime_Get(o, "microsecond") +#define PyDateTime_GetFold(o) PyDateTime_Get(o, "fold") + +#define PyDateTime_GET_YEAR(o) PyDateTime_GetYear(o) +#define PyDateTime_GET_MONTH(o) PyDateTime_GetMonth(o) +#define PyDateTime_GET_DAY(o) PyDateTime_GetDay(o) + +#define PyDateTime_DATE_GET_HOUR(o) PyDateTime_GetHour(o) +#define PyDateTime_DATE_GET_MINUTE(o) PyDateTime_GetMinute(o) +#define PyDateTime_DATE_GET_SECOND(o) PyDateTime_GetSecond(o) +#define PyDateTime_DATE_GET_MICROSECOND(o) PyDateTime_GetMicrosecond(o) +#define PyDateTime_DATE_GET_FOLD(o) PyDateTime_GetFold(o) + +#define PyDateTime_TIME_GET_HOUR(o) PyDateTime_GetHour(o) +#define PyDateTime_TIME_GET_MINUTE(o) PyDateTime_GetMinute(o) +#define PyDateTime_TIME_GET_SECOND(o) PyDateTime_GetSecond(o) +#define PyDateTime_TIME_GET_MICROSECOND(o) PyDateTime_GetMicrosecond(o) +#define PyDateTime_TIME_GET_FOLD(o) PyDateTime_GetFold(o) + +/* Define structure slightly similar to C API. */ +typedef struct { + PyObject *module; + /* type objects */ + PyTypeObject *DateType; + PyTypeObject *DateTimeType; + PyTypeObject *TimeType; + PyTypeObject *DeltaType; + PyTypeObject *TZInfoType; +} datetime_struc; + +LIBSHIBOKEN_API datetime_struc *init_DateTime(void); + +#define PyDateTime_IMPORT PyDateTimeAPI = init_DateTime() + +extern LIBSHIBOKEN_API datetime_struc *PyDateTimeAPI; + +#define PyDate_Check(op) PyObject_TypeCheck(op, PyDateTimeAPI->DateType) +#define PyDateTime_Check(op) PyObject_TypeCheck(op, PyDateTimeAPI->DateTimeType) +#define PyTime_Check(op) PyObject_TypeCheck(op, PyDateTimeAPI->TimeType) + +LIBSHIBOKEN_API PyObject *PyDate_FromDate(int year, int month, int day); +LIBSHIBOKEN_API PyObject *PyDateTime_FromDateAndTime( + int year, int month, int day, int hour, int min, int sec, int usec); +LIBSHIBOKEN_API PyObject *PyTime_FromTime( + int hour, int minute, int second, int usecond); + +#endif /* Py_LIMITED_API */ + +/***************************************************************************** + * + * Extra support for signature.cpp + * + */ + +#ifdef Py_LIMITED_API +extern LIBSHIBOKEN_API PyTypeObject *PepStaticMethod_TypePtr; +#else +#define PepStaticMethod_TypePtr &PyStaticMethod_Type +#endif + +/***************************************************************************** + * + * Module Initialization + * + */ + +LIBSHIBOKEN_API void Pep_Init(void); + +} // extern "C" + +#endif // PEP384IMPL_H diff --git a/sources/shiboken2/libshiboken/python25compat.h b/sources/shiboken2/libshiboken/python25compat.h index 42f78481d..fc25aa3e5 100644 --- a/sources/shiboken2/libshiboken/python25compat.h +++ b/sources/shiboken2/libshiboken/python25compat.h @@ -39,7 +39,7 @@ #ifndef PYTHON25COMPAT_H #define PYTHON25COMPAT_H -#include +#include "sbkpython.h" #include /* diff --git a/sources/shiboken2/libshiboken/qapp_macro.cpp b/sources/shiboken2/libshiboken/qapp_macro.cpp index e6a877a32..f69d0f937 100644 --- a/sources/shiboken2/libshiboken/qapp_macro.cpp +++ b/sources/shiboken2/libshiboken/qapp_macro.cpp @@ -119,8 +119,8 @@ MakeSingletonQAppWrapper(PyTypeObject *type) if (type == NULL) type = Py_NONE_TYPE; if (!(type == Py_NONE_TYPE || Py_TYPE(qApp_content) == Py_NONE_TYPE)) { - const char *res_name = strrchr(Py_TYPE(qApp_content)->tp_name, '.')+1; - const char *type_name = strrchr(type->tp_name, '.')+1; + const char *res_name = PepType_GetNameStr(Py_TYPE(qApp_content)); + const char *type_name = PepType_GetNameStr(type); PyErr_Format(PyExc_RuntimeError, "Please destroy the %s singleton before" " creating a new %s instance.", res_name, type_name); return NULL; diff --git a/sources/shiboken2/libshiboken/qt_attribution.json b/sources/shiboken2/libshiboken/qt_attribution.json new file mode 100644 index 000000000..a90cc604b --- /dev/null +++ b/sources/shiboken2/libshiboken/qt_attribution.json @@ -0,0 +1,12 @@ +{ + "Id": "python", + "Name": "Python", + "QDocModule": "QtForPython", + "QtUsage": "Used for Qt for Python in the signature extension.", + "Description": "Qt for Python is an add-on for Python. The libshiboken packages of PySide uses certain parts of the source files (typespec.cpp, typespec.h, bufferprocs27.cpp, bufferprocs27.h). See the folder sources/shiboken2/libshiboken .", + "Homepage": "http://www.python.org/", + "Version": "3.6.5", + "License": "PSF LICENSE AGREEMENT FOR PYTHON 3.6.5", + "LicenseFile": "bufferprocs27.h", + "Copyright": "© Copyright 2001-2018, Python Software Foundation." +} diff --git a/sources/shiboken2/libshiboken/sbkarrayconverter.cpp b/sources/shiboken2/libshiboken/sbkarrayconverter.cpp index 1646c9117..58e0b18a8 100644 --- a/sources/shiboken2/libshiboken/sbkarrayconverter.cpp +++ b/sources/shiboken2/libshiboken/sbkarrayconverter.cpp @@ -162,7 +162,7 @@ static void sequenceToCppIntArray(PyObject *pyIn, void *cppOut) { ArrayHandle *handle = reinterpret_cast *>(cppOut); handle->allocate(PySequence_Size(pyIn)); - convertPySequence(pyIn, _PyLong_AsInt, handle->data()); + convertPySequence(pyIn, _PepLong_AsInt, handle->data()); } static PythonToCppFunc sequenceToCppIntArrayCheck(PyObject *pyIn, int dim1, int /* dim2 */) diff --git a/sources/shiboken2/libshiboken/sbkconverter.cpp b/sources/shiboken2/libshiboken/sbkconverter.cpp index 64884d601..d4d3ac899 100644 --- a/sources/shiboken2/libshiboken/sbkconverter.cpp +++ b/sources/shiboken2/libshiboken/sbkconverter.cpp @@ -112,6 +112,8 @@ SbkConverter *createConverterObject(PyTypeObject *type, { SbkConverter* converter = new SbkConverter; converter->pythonType = type; + // PYSIDE-595: All types are heaptypes now, so provide reference. + Py_XINCREF(type); converter->pointerToPython = pointerToPythonFunc; converter->copyToPython = copyToPythonFunc; @@ -133,7 +135,7 @@ SbkConverter* createConverter(SbkObjectType* type, createConverterObject(reinterpret_cast(type), toCppPointerConvFunc, toCppPointerCheckFunc, pointerToPythonFunc, copyToPythonFunc); - type->d->converter = converter; + PepType_SOTP(type)->converter = converter; return converter; } @@ -172,12 +174,12 @@ void addPythonToCppValueConversion(SbkObjectType* type, PythonToCppFunc pythonToCppFunc, IsConvertibleToCppFunc isConvertibleToCppFunc) { - addPythonToCppValueConversion(type->d->converter, pythonToCppFunc, isConvertibleToCppFunc); + addPythonToCppValueConversion(PepType_SOTP(type)->converter, pythonToCppFunc, isConvertibleToCppFunc); } -PyObject* pointerToPython(const SbkObjectType *type, const void *cppIn) +PyObject* pointerToPython(SbkObjectType *type, const void *cppIn) { - return pointerToPython(type->d->converter, cppIn); + return pointerToPython(PepType_SOTP(type)->converter, cppIn); } PyObject* pointerToPython(const SbkConverter *converter, const void *cppIn) @@ -187,15 +189,15 @@ PyObject* pointerToPython(const SbkConverter *converter, const void *cppIn) Py_RETURN_NONE; if (!converter->pointerToPython) { warning(PyExc_RuntimeWarning, 0, "pointerToPython(): SbkConverter::pointerToPython is null for \"%s\".", - converter->pythonType->tp_name); + PepType(converter->pythonType)->tp_name); Py_RETURN_NONE; } return converter->pointerToPython(cppIn); } -PyObject* referenceToPython(const SbkObjectType *type, const void *cppIn) +PyObject* referenceToPython(SbkObjectType *type, const void *cppIn) { - return referenceToPython(type->d->converter, cppIn); + return referenceToPython(PepType_SOTP(type)->converter, cppIn); } PyObject* referenceToPython(const SbkConverter *converter, const void *cppIn) @@ -209,7 +211,7 @@ PyObject* referenceToPython(const SbkConverter *converter, const void *cppIn) } if (!converter->pointerToPython) { warning(PyExc_RuntimeWarning, 0, "referenceToPython(): SbkConverter::pointerToPython is null for \"%s\".", - converter->pythonType->tp_name); + PepType(converter->pythonType)->tp_name); Py_RETURN_NONE; } return converter->pointerToPython(cppIn); @@ -221,24 +223,24 @@ static inline PyObject* CopyCppToPython(const SbkConverter *converter, const voi Py_RETURN_NONE; if (!converter->copyToPython) { warning(PyExc_RuntimeWarning, 0, "CopyCppToPython(): SbkConverter::copyToPython is null for \"%s\".", - converter->pythonType->tp_name); + PepType(converter->pythonType)->tp_name); Py_RETURN_NONE; } return converter->copyToPython(cppIn); } -PyObject* copyToPython(const SbkObjectType *type, const void *cppIn) +PyObject* copyToPython(SbkObjectType *type, const void *cppIn) { - return CopyCppToPython(type->d->converter, cppIn); + return CopyCppToPython(PepType_SOTP(type)->converter, cppIn); } PyObject* copyToPython(const SbkConverter *converter, const void *cppIn) { return CopyCppToPython(converter, cppIn); } -PythonToCppFunc isPythonToCppPointerConvertible(const SbkObjectType *type, PyObject *pyIn) +PythonToCppFunc isPythonToCppPointerConvertible(SbkObjectType *type, PyObject *pyIn) { assert(pyIn); - return type->d->converter->toCppPointerConversion.first(pyIn); + return PepType_SOTP(type)->converter->toCppPointerConversion.first(pyIn); } static inline PythonToCppFunc IsPythonToCppConvertible(const SbkConverter *converter, PyObject *pyIn) @@ -252,9 +254,9 @@ static inline PythonToCppFunc IsPythonToCppConvertible(const SbkConverter *conve } return 0; } -PythonToCppFunc isPythonToCppValueConvertible(const SbkObjectType *type, PyObject *pyIn) +PythonToCppFunc isPythonToCppValueConvertible(SbkObjectType *type, PyObject *pyIn) { - return IsPythonToCppConvertible(type->d->converter, pyIn); + return IsPythonToCppConvertible(PepType_SOTP(type)->converter, pyIn); } PythonToCppFunc isPythonToCppConvertible(const SbkConverter *converter, PyObject *pyIn) { @@ -272,7 +274,7 @@ PythonToCppFunc isPythonToCppConvertible(const SbkArrayConverter *converter, return nullptr; } -PythonToCppFunc isPythonToCppReferenceConvertible(const SbkObjectType *type, PyObject *pyIn) +PythonToCppFunc isPythonToCppReferenceConvertible(SbkObjectType *type, PyObject *pyIn) { if (pyIn != Py_None) { PythonToCppFunc toCpp = isPythonToCppPointerConvertible(type, pyIn); @@ -329,10 +331,10 @@ static void _pythonToCppCopy(const SbkConverter *converter, PyObject *pyIn, void toCpp(pyIn, cppOut); } -void pythonToCppCopy(const SbkObjectType *type, PyObject *pyIn, void *cppOut) +void pythonToCppCopy(SbkObjectType *type, PyObject *pyIn, void *cppOut) { assert(type); - _pythonToCppCopy(type->d->converter, pyIn, cppOut); + _pythonToCppCopy(PepType_SOTP(type)->converter, pyIn, cppOut); } void pythonToCppCopy(const SbkConverter *converter, PyObject *pyIn, void *cppOut) @@ -340,16 +342,16 @@ void pythonToCppCopy(const SbkConverter *converter, PyObject *pyIn, void *cppOut _pythonToCppCopy(converter, pyIn, cppOut); } -bool isImplicitConversion(const SbkObjectType *type, PythonToCppFunc toCppFunc) +bool isImplicitConversion(SbkObjectType *type, PythonToCppFunc toCppFunc) { // This is the Object Type or Value Type conversion that only // retrieves the C++ pointer held in the Python wrapper. - if (toCppFunc == type->d->converter->toCppPointerConversion.second) + if (toCppFunc == PepType_SOTP(type)->converter->toCppPointerConversion.second) return false; // Object Types doesn't have any kind of value conversion, // only C++ pointer retrieval. - if (type->d->converter->toCppConversions.empty()) + if (PepType_SOTP(type)->converter->toCppConversions.empty()) return false; // The first conversion of the non-pointer conversion list is @@ -359,7 +361,7 @@ bool isImplicitConversion(const SbkObjectType *type, PythonToCppFunc toCppFunc) // Note that we don't check if the Python to C++ conversion is in // the list of the type's conversions, for it is expected that the // caller knows what he's doing. - ToCppConversionList::iterator conv = type->d->converter->toCppConversions.begin(); + ToCppConversionList::iterator conv = PepType_SOTP(type)->converter->toCppConversions.begin(); return toCppFunc != (*conv).second; } @@ -411,10 +413,10 @@ bool convertibleSequenceTypes(const SbkConverter *converter, PyObject *pyIn) } return true; } -bool convertibleSequenceTypes(const SbkObjectType *type, PyObject *pyIn) +bool convertibleSequenceTypes(SbkObjectType *type, PyObject *pyIn) { assert(type); - return convertibleSequenceTypes(type->d->converter, pyIn); + return convertibleSequenceTypes(PepType_SOTP(type)->converter, pyIn); } bool checkPairTypes(PyTypeObject* firstType, PyTypeObject* secondType, PyObject* pyIn) diff --git a/sources/shiboken2/libshiboken/sbkconverter.h b/sources/shiboken2/libshiboken/sbkconverter.h index da71db5b5..0effebf57 100644 --- a/sources/shiboken2/libshiboken/sbkconverter.h +++ b/sources/shiboken2/libshiboken/sbkconverter.h @@ -191,7 +191,7 @@ LIBSHIBOKEN_API void addPythonToCppValueConversion(SbkObjectType* type, * TYPE* var; * PyObject* pyVar = pointerToPython(SBKTYPE, &var); */ -LIBSHIBOKEN_API PyObject* pointerToPython(const SbkObjectType *type, const void *cppIn); +LIBSHIBOKEN_API PyObject* pointerToPython(SbkObjectType *type, const void *cppIn); LIBSHIBOKEN_API PyObject* pointerToPython(const SbkConverter *converter, const void *cppIn); /** @@ -203,7 +203,7 @@ LIBSHIBOKEN_API PyObject* pointerToPython(const SbkConverter *converter, const v * TYPE& var = SOMETHING; * PyObject* pyVar = referenceToPython(SBKTYPE, &var); */ -LIBSHIBOKEN_API PyObject* referenceToPython(const SbkObjectType *type, const void *cppIn); +LIBSHIBOKEN_API PyObject* referenceToPython(SbkObjectType *type, const void *cppIn); LIBSHIBOKEN_API PyObject* referenceToPython(const SbkConverter *converter, const void *cppIn); /** @@ -213,7 +213,7 @@ LIBSHIBOKEN_API PyObject* referenceToPython(const SbkConverter *converter, const * TYPE var; * PyObject* pyVar = copyToPython(SBKTYPE, &var); */ -LIBSHIBOKEN_API PyObject* copyToPython(const SbkObjectType *type, const void *cppIn); +LIBSHIBOKEN_API PyObject* copyToPython(SbkObjectType *type, const void *cppIn); LIBSHIBOKEN_API PyObject* copyToPython(const SbkConverter *converter, const void *cppIn); // Python -> C++ --------------------------------------------------------------------------- @@ -222,7 +222,7 @@ LIBSHIBOKEN_API PyObject* copyToPython(const SbkConverter *converter, const void * Returns a Python to C++ conversion function if the Python object is convertible to a C++ pointer. * It returns NULL if the Python object is not convertible to \p type. */ -LIBSHIBOKEN_API PythonToCppFunc isPythonToCppPointerConvertible(const SbkObjectType *type, PyObject *pyIn); +LIBSHIBOKEN_API PythonToCppFunc isPythonToCppPointerConvertible(SbkObjectType *type, PyObject *pyIn); /** * Returns a Python to C++ conversion function if the Python object is convertible to a C++ value. @@ -230,7 +230,7 @@ LIBSHIBOKEN_API PythonToCppFunc isPythonToCppPointerConvertible(const SbkObjectT * convert the object to the expected \p type. * It returns NULL if the Python object is not convertible to \p type. */ -LIBSHIBOKEN_API PythonToCppFunc isPythonToCppValueConvertible(const SbkObjectType *type, PyObject *pyIn); +LIBSHIBOKEN_API PythonToCppFunc isPythonToCppValueConvertible(SbkObjectType *type, PyObject *pyIn); /** * Returns a Python to C++ conversion function if the Python object is convertible to a C++ reference. @@ -238,7 +238,7 @@ LIBSHIBOKEN_API PythonToCppFunc isPythonToCppValueConvertible(const SbkObjectTyp * or a new C++ value if it must be a implicit conversion. * It returns NULL if the Python object is not convertible to \p type. */ -LIBSHIBOKEN_API PythonToCppFunc isPythonToCppReferenceConvertible(const SbkObjectType *type, PyObject *pyIn); +LIBSHIBOKEN_API PythonToCppFunc isPythonToCppReferenceConvertible(SbkObjectType *type, PyObject *pyIn); /// This is the same as isPythonToCppValueConvertible function. LIBSHIBOKEN_API PythonToCppFunc isPythonToCppConvertible(const SbkConverter *converter, PyObject *pyIn); @@ -257,7 +257,7 @@ LIBSHIBOKEN_API void pythonToCppPointer(SbkObjectType* type, PyObject* pyIn, voi LIBSHIBOKEN_API void pythonToCppPointer(const SbkConverter *converter, PyObject *pyIn, void *cppOut); /// Converts a Python object \p pyIn to C++, and copies the result in the C++ variable passed in \p cppOut. -LIBSHIBOKEN_API void pythonToCppCopy(const SbkObjectType *type, PyObject *pyIn, void *cppOut); +LIBSHIBOKEN_API void pythonToCppCopy(SbkObjectType *type, PyObject *pyIn, void *cppOut); LIBSHIBOKEN_API void pythonToCppCopy(const SbkConverter *converter, PyObject *pyIn, void *cppOut); /** @@ -271,7 +271,7 @@ LIBSHIBOKEN_API void nonePythonToCppNullPtr(PyObject*, void* cppOut); * It is used when C++ expects a reference argument, so it may be the same object received * from Python, or another created through implicit conversion. */ -LIBSHIBOKEN_API bool isImplicitConversion(const SbkObjectType *type, PythonToCppFunc toCpp); +LIBSHIBOKEN_API bool isImplicitConversion(SbkObjectType *type, PythonToCppFunc toCpp); /// Registers a converter with a type name that may be used to retrieve the converter. LIBSHIBOKEN_API void registerConverterName(SbkConverter* converter, const char* typeName); @@ -289,7 +289,7 @@ LIBSHIBOKEN_API bool checkSequenceTypes(PyTypeObject* type, PyObject* pyIn); LIBSHIBOKEN_API bool convertibleSequenceTypes(const SbkConverter *converter, PyObject *pyIn); /// Returns true if a Python sequence is comprised of objects of a type convertible to \p type. -LIBSHIBOKEN_API bool convertibleSequenceTypes(const SbkObjectType *type, PyObject *pyIn); +LIBSHIBOKEN_API bool convertibleSequenceTypes(SbkObjectType *type, PyObject *pyIn); /// Returns true if a Python sequence can be converted to a C++ pair. LIBSHIBOKEN_API bool checkPairTypes(PyTypeObject* firstType, PyTypeObject* secondType, PyObject* pyIn); @@ -394,8 +394,9 @@ template<> inline PyTypeObject* SbkType() { return &PyInt_Type; #define PyObject_Check(X) true #define SbkChar_Check(X) (SbkNumber_Check(X) || Shiboken::String::checkChar(X)) -struct _SbkGenericType { PyHeapTypeObject super; SbkConverter** converter; }; -#define SBK_CONVERTER(pyType) (*reinterpret_cast<_SbkGenericType*>(pyType)->converter) +struct _SbkGenericTypePrivate { + SbkConverter** converter; +}; #endif // SBK_CONVERTER_H diff --git a/sources/shiboken2/libshiboken/sbkenum.cpp b/sources/shiboken2/libshiboken/sbkenum.cpp index 37649f6fa..5f753293c 100644 --- a/sources/shiboken2/libshiboken/sbkenum.cpp +++ b/sources/shiboken2/libshiboken/sbkenum.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt for Python. @@ -54,14 +54,18 @@ extern "C" { -struct SbkEnumType +struct SbkEnumTypePrivate { - PyHeapTypeObject super; SbkConverter** converterPtr; SbkConverter* converter; const char* cppName; }; +struct SbkEnumType +{ + PepTypeObject type; +}; + struct SbkEnumObject { PyObject_HEAD @@ -73,21 +77,9 @@ static PyObject* SbkEnumObject_repr(PyObject* self) { const SbkEnumObject *enumObj = reinterpret_cast(self); if (enumObj->ob_name) - return Shiboken::String::fromFormat("%s.%s", self->ob_type->tp_name, PyBytes_AS_STRING(enumObj->ob_name)); - else - return Shiboken::String::fromFormat("%s(%ld)", self->ob_type->tp_name, enumObj->ob_value); -} - -static int SbkEnumObject_print(PyObject* self, FILE* fp, int) -{ - Py_BEGIN_ALLOW_THREADS - const SbkEnumObject *enumObj = reinterpret_cast(self); - if (enumObj->ob_name) - fprintf(fp, "%s.%s", self->ob_type->tp_name, PyBytes_AS_STRING(enumObj->ob_name)); + return Shiboken::String::fromFormat("%s.%s", PepType((Py_TYPE(self)))->tp_name, PyBytes_AS_STRING(enumObj->ob_name)); else - fprintf(fp, "%s(%ld)", self->ob_type->tp_name, enumObj->ob_value); - Py_END_ALLOW_THREADS - return 0; + return Shiboken::String::fromFormat("%s(%ld)", PepType((Py_TYPE(self)))->tp_name, enumObj->ob_value); } static PyObject* SbkEnumObject_name(PyObject* self, void*) @@ -266,114 +258,54 @@ static PyGetSetDef SbkEnumGetSetList[] = { {0, 0, 0, 0, 0} // Sentinel }; -static PyNumberMethods enum_as_number = { - /* nb_add */ enum_add, - /* nb_subtract */ enum_subtract, - /* nb_multiply */ enum_multiply, -#ifndef IS_PY3K - /* nb_divide */ enum_divide, -#endif - /* nb_remainder */ 0, - /* nb_divmod */ 0, - /* nb_power */ 0, - /* nb_negative */ 0, - /* nb_positive */ enum_int, - /* nb_absolute */ 0, - /* nb_bool/nb_nonzero */ enum_bool, - /* nb_invert */ 0, - /* nb_lshift */ 0, - /* nb_rshift */ 0, - /* nb_and */ enum_and, - /* nb_xor */ enum_xor, - /* nb_or */ enum_or, +static void SbkEnumTypeDealloc(PyObject* pyObj); +static PyObject* SbkEnumTypeTpNew(PyTypeObject* metatype, PyObject* args, PyObject* kwds); + +static PyType_Slot SbkEnumType_Type_slots[] = { + {Py_tp_dealloc, (void *)SbkEnumTypeDealloc}, + {Py_nb_add, (void *)enum_add}, + {Py_nb_subtract, (void *)enum_subtract}, + {Py_nb_multiply, (void *)enum_multiply}, #ifndef IS_PY3K - /* nb_coerce */ 0, + {Py_nb_divide, (void *)enum_divide}, #endif - /* nb_int */ enum_int, + {Py_nb_positive, (void *)enum_int}, #ifdef IS_PY3K - /* nb_reserved */ 0, - /* nb_float */ 0, + {Py_nb_bool, (void *)enum_bool}, #else - /* nb_long */ enum_int, - /* nb_float */ 0, - /* nb_oct */ 0, - /* nb_hex */ 0, -#endif - - /* nb_inplace_add */ 0, - /* nb_inplace_subtract */ 0, - /* nb_inplace_multiply */ 0, -#ifndef IS_PY3K - /* nb_inplace_div */ 0, + {Py_nb_nonzero, (void *)enum_bool}, + {Py_nb_long, (void *)enum_int}, #endif - /* nb_inplace_remainder */ 0, - /* nb_inplace_power */ 0, - /* nb_inplace_lshift */ 0, - /* nb_inplace_rshift */ 0, - /* nb_inplace_and */ 0, - /* nb_inplace_xor */ 0, - /* nb_inplace_or */ 0, - - /* nb_floor_divide */ 0, - /* nb_true_divide */ 0, - /* nb_inplace_floor_divide */ 0, - /* nb_inplace_true_divide */ 0, - - /* nb_index */ enum_int + {Py_nb_and, (void *)enum_and}, + {Py_nb_xor, (void *)enum_xor}, + {Py_nb_or, (void *)enum_or}, + {Py_nb_int, (void *)enum_int}, + {Py_nb_index, (void *)enum_int}, + {Py_tp_base, (void *)&PyType_Type}, + {Py_tp_alloc, (void *)PyType_GenericAlloc}, + {Py_tp_new, (void *)SbkEnumTypeTpNew}, + {Py_tp_free, (void *)PyObject_GC_Del}, + {0, 0} +}; +static PyType_Spec SbkEnumType_Type_spec = { + "Shiboken.EnumType", + 0, // filled in later + sizeof(PyMemberDef), + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, + SbkEnumType_Type_slots, }; -static void SbkEnumTypeDealloc(PyObject* pyObj); -static PyObject* SbkEnumTypeTpNew(PyTypeObject* metatype, PyObject* args, PyObject* kwds); -PyTypeObject SbkEnumType_Type = { - PyVarObject_HEAD_INIT(0, 0) - /*tp_name*/ "Shiboken.EnumType", - /*tp_basicsize*/ sizeof(SbkEnumType), - /*tp_itemsize*/ 0, - /*tp_dealloc*/ SbkEnumTypeDealloc, - /*tp_print*/ 0, - /*tp_getattr*/ 0, - /*tp_setattr*/ 0, - /*tp_compare*/ 0, - /*tp_repr*/ 0, - /*tp_as_number*/ &enum_as_number, - /*tp_as_sequence*/ 0, - /*tp_as_mapping*/ 0, - /*tp_hash*/ 0, - /*tp_call*/ 0, - /*tp_str*/ 0, - /*tp_getattro*/ 0, - /*tp_setattro*/ 0, - /*tp_as_buffer*/ 0, - /*tp_flags*/ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, - /*tp_doc*/ 0, - /*tp_traverse*/ 0, - /*tp_clear*/ 0, - /*tp_richcompare*/ 0, - /*tp_weaklistoffset*/ 0, - /*tp_iter*/ 0, - /*tp_iternext*/ 0, - /*tp_methods*/ 0, - /*tp_members*/ 0, - /*tp_getset*/ 0, - /*tp_base*/ &PyType_Type, - /*tp_dict*/ 0, - /*tp_descr_get*/ 0, - /*tp_descr_set*/ 0, - /*tp_dictoffset*/ 0, - /*tp_init*/ 0, - /*tp_alloc*/ PyType_GenericAlloc, - /*tp_new*/ SbkEnumTypeTpNew, - /*tp_free*/ PyObject_GC_Del, - /*tp_is_gc*/ 0, - /*tp_bases*/ 0, - /*tp_mro*/ 0, - /*tp_cache*/ 0, - /*tp_subclasses*/ 0, - /*tp_weaklist*/ 0, - /*tp_del*/ 0, - /*tp_version_tag*/ 0 -}; +PyTypeObject *SbkEnumType_TypeF(void) +{ + static PyTypeObject *type = nullptr; + if (!type) { + SbkEnumType_Type_spec.basicsize = + PepHeapType_SIZE + sizeof(SbkEnumTypePrivate); + type = (PyTypeObject *)PyType_FromSpec(&SbkEnumType_Type_spec); + } + return type; +} void SbkEnumTypeDealloc(PyObject* pyObj) { @@ -381,15 +313,16 @@ void SbkEnumTypeDealloc(PyObject* pyObj) PyObject_GC_UnTrack(pyObj); Py_TRASHCAN_SAFE_BEGIN(pyObj); - if (sbkType->converter) { - Shiboken::Conversions::deleteConverter(sbkType->converter); + if (PepType_SETP(sbkType)->converter) { + Shiboken::Conversions::deleteConverter(PepType_SETP(sbkType)->converter); } Py_TRASHCAN_SAFE_END(pyObj); } PyObject* SbkEnumTypeTpNew(PyTypeObject* metatype, PyObject* args, PyObject* kwds) { - SbkEnumType* newType = reinterpret_cast(PyType_Type.tp_new(metatype, args, kwds)); + newfunc type_new = reinterpret_cast(PyType_GetSlot(&PyType_Type, Py_tp_new)); + SbkEnumType *newType = reinterpret_cast(type_new(metatype, args, kwds)); if (!newType) return 0; return reinterpret_cast(newType); @@ -417,14 +350,14 @@ namespace Enum { bool check(PyObject* pyObj) { - return Py_TYPE(pyObj->ob_type) == &SbkEnumType_Type; + return Py_TYPE(Py_TYPE(pyObj)) == SbkEnumType_TypeF(); } PyObject* getEnumItemFromValue(PyTypeObject* enumType, long itemValue) { PyObject *key, *value; Py_ssize_t pos = 0; - PyObject* values = PyDict_GetItemString(enumType->tp_dict, const_cast("values")); + PyObject *values = PyDict_GetItemString(PepType(enumType)->tp_dict, const_cast("values")); while (PyDict_Next(values, &pos, &key, &value)) { SbkEnumObject *obj = reinterpret_cast(value); @@ -438,9 +371,7 @@ PyObject* getEnumItemFromValue(PyTypeObject* enumType, long itemValue) static PyTypeObject* createEnum(const char* fullName, const char* cppName, const char* shortName, PyTypeObject* flagsType) { - PyTypeObject* enumType = newTypeWithName(fullName, cppName); - if (flagsType) - enumType->tp_as_number = flagsType->tp_as_number; + PyTypeObject* enumType = newTypeWithName(fullName, cppName, flagsType); if (PyType_Ready(enumType) < 0) return 0; return enumType; @@ -451,7 +382,8 @@ PyTypeObject* createGlobalEnum(PyObject* module, const char* name, const char* f PyTypeObject* enumType = createEnum(fullName, cppName, name, flagsType); if (enumType && PyModule_AddObject(module, name, reinterpret_cast(enumType)) < 0) return 0; - if (flagsType && PyModule_AddObject(module, flagsType->tp_name, reinterpret_cast(flagsType)) < 0) + if (flagsType && PyModule_AddObject(module, PepType_GetNameStr(flagsType), + reinterpret_cast(flagsType)) < 0) return 0; return enumType; } @@ -459,17 +391,20 @@ PyTypeObject* createGlobalEnum(PyObject* module, const char* name, const char* f PyTypeObject* createScopedEnum(SbkObjectType* scope, const char* name, const char* fullName, const char* cppName, PyTypeObject* flagsType) { PyTypeObject* enumType = createEnum(fullName, cppName, name, flagsType); - if (enumType && PyDict_SetItemString(scope->super.ht_type.tp_dict, name, reinterpret_cast(enumType)) < 0) - return 0; - if (flagsType && PyDict_SetItemString(scope->super.ht_type.tp_dict, flagsType->tp_name, reinterpret_cast(flagsType)) < 0) - return 0; + if (enumType && PyDict_SetItemString(PepType(scope)->tp_dict, name, + reinterpret_cast(enumType)) < 0) + return nullptr; + if (flagsType && PyDict_SetItemString(PepType(scope)->tp_dict, + PepType_GetNameStr(flagsType), + reinterpret_cast(flagsType)) < 0) + return nullptr; return enumType; } static PyObject* createEnumItem(PyTypeObject* enumType, const char* itemName, long itemValue) { PyObject* enumItem = newItem(enumType, itemValue, itemName); - if (PyDict_SetItemString(enumType->tp_dict, itemName, enumItem) < 0) + if (PyDict_SetItemString(PepType(enumType)->tp_dict, itemName, enumItem) < 0) return 0; Py_DECREF(enumItem); return enumItem; @@ -496,7 +431,7 @@ bool createScopedEnumItem(PyTypeObject *enumType, PyTypeObject *scope, const char *itemName, long itemValue) { if (PyObject *enumItem = createEnumItem(enumType, itemName, itemValue)) { - if (PyDict_SetItemString(scope->tp_dict, itemName, enumItem) < 0) + if (PyDict_SetItemString(PepType(scope)->tp_dict, itemName, enumItem) < 0) return false; Py_DECREF(enumItem); return true; @@ -506,15 +441,17 @@ bool createScopedEnumItem(PyTypeObject *enumType, PyTypeObject *scope, bool createScopedEnumItem(PyTypeObject* enumType, SbkObjectType* scope, const char* itemName, long itemValue) { - return createScopedEnumItem(enumType, &scope->super.ht_type, itemName, itemValue); + return createScopedEnumItem(enumType, reinterpret_cast(scope), itemName, itemValue); } -PyObject* newItem(PyTypeObject* enumType, long itemValue, const char* itemName) +PyObject * +newItem(PyTypeObject *enumType, long itemValue, const char *itemName) { bool newValue = true; SbkEnumObject* enumObj; if (!itemName) { - enumObj = reinterpret_cast(getEnumItemFromValue(enumType, itemValue)); + enumObj = reinterpret_cast( + getEnumItemFromValue(enumType, itemValue)); if (enumObj) return reinterpret_cast(enumObj); @@ -529,10 +466,10 @@ PyObject* newItem(PyTypeObject* enumType, long itemValue, const char* itemName) enumObj->ob_value = itemValue; if (newValue) { - PyObject* values = PyDict_GetItemString(enumType->tp_dict, const_cast("values")); + PyObject* values = PyDict_GetItemString(PepType(enumType)->tp_dict, const_cast("values")); if (!values) { values = PyDict_New(); - PyDict_SetItemString(enumType->tp_dict, const_cast("values"), values); + PyDict_SetItemString(PepType(enumType)->tp_dict, const_cast("values"), values); Py_DECREF(values); // ^ values still alive, because setitemstring incref it } PyDict_SetItemString(values, itemName, reinterpret_cast(enumObj)); @@ -541,39 +478,140 @@ PyObject* newItem(PyTypeObject* enumType, long itemValue, const char* itemName) return reinterpret_cast(enumObj); } -PyTypeObject* newType(const char* name) -{ - return newTypeWithName(name, ""); -} +static PyType_Slot SbkNewType_slots[] = { + {Py_tp_repr, (void *)SbkEnumObject_repr}, + {Py_tp_str, (void *)SbkEnumObject_repr}, + {Py_tp_getset, (void *)SbkEnumGetSetList}, + {Py_tp_new, (void *)SbkEnum_tp_new}, + {Py_nb_add, (void *)enum_add}, + {Py_nb_subtract, (void *)enum_subtract}, + {Py_nb_multiply, (void *)enum_multiply}, +#ifndef IS_PY3K + {Py_nb_divide, (void *)enum_divide}, +#endif + {Py_nb_positive, (void *)enum_int}, +#ifdef IS_PY3K + {Py_nb_bool, (void *)enum_bool}, +#else + {Py_nb_nonzero, (void *)enum_bool}, + {Py_nb_long, (void *)enum_int}, +#endif + {Py_nb_and, (void *)enum_and}, + {Py_nb_xor, (void *)enum_xor}, + {Py_nb_or, (void *)enum_or}, + {Py_nb_int, (void *)enum_int}, + {Py_nb_index, (void *)enum_int}, + {Py_tp_richcompare, (void *)enum_richcompare}, + {Py_tp_hash, (void *)enum_hash}, + {Py_tp_dealloc, (void *)SbkDummyDealloc}, + {0, 0} +}; +static PyType_Spec SbkNewType_spec = { + "missing Enum name", // to be inserted later + sizeof(SbkEnumObject), + 0, + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_CHECKTYPES, + SbkNewType_slots, +}; -PyTypeObject* newTypeWithName(const char* name, const char* cppName) +static void +copyNumberMethods(PyTypeObject *flagsType, + PyType_Slot number_slots[], + int *pidx) { - PyTypeObject* type = reinterpret_cast(new SbkEnumType); - ::memset(type, 0, sizeof(SbkEnumType)); - Py_TYPE(type) = &SbkEnumType_Type; - type->tp_basicsize = sizeof(SbkEnumObject); - type->tp_print = &SbkEnumObject_print; - type->tp_repr = &SbkEnumObject_repr; - type->tp_str = &SbkEnumObject_repr; - type->tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_CHECKTYPES; - type->tp_name = name; - type->tp_getset = SbkEnumGetSetList; - type->tp_new = SbkEnum_tp_new; - type->tp_as_number = &enum_as_number; - type->tp_richcompare = &enum_richcompare; - type->tp_hash = &enum_hash; + int idx = *pidx; +#ifdef IS_PY3K +# define SLOT slot +#else +# define SLOT slot_ +#endif +#define PUT_SLOT(name) \ + number_slots[idx].SLOT = (name); \ + number_slots[idx].pfunc = PyType_GetSlot(flagsType, (name)); \ + ++idx; + + PUT_SLOT(Py_nb_absolute); + PUT_SLOT(Py_nb_add); + PUT_SLOT(Py_nb_and); +#ifdef IS_PY3K + PUT_SLOT(Py_nb_bool); +#else + PUT_SLOT(Py_nb_nonzero); +#endif + PUT_SLOT(Py_nb_divmod); + PUT_SLOT(Py_nb_float); + PUT_SLOT(Py_nb_floor_divide); + PUT_SLOT(Py_nb_index); + PUT_SLOT(Py_nb_inplace_add); + PUT_SLOT(Py_nb_inplace_and); + PUT_SLOT(Py_nb_inplace_floor_divide); + PUT_SLOT(Py_nb_inplace_lshift); + PUT_SLOT(Py_nb_inplace_multiply); + PUT_SLOT(Py_nb_inplace_or); + PUT_SLOT(Py_nb_inplace_power); + PUT_SLOT(Py_nb_inplace_remainder); + PUT_SLOT(Py_nb_inplace_rshift); + PUT_SLOT(Py_nb_inplace_subtract); + PUT_SLOT(Py_nb_inplace_true_divide); + PUT_SLOT(Py_nb_inplace_xor); + PUT_SLOT(Py_nb_int); + PUT_SLOT(Py_nb_invert); + PUT_SLOT(Py_nb_lshift); + PUT_SLOT(Py_nb_multiply); + PUT_SLOT(Py_nb_negative); + PUT_SLOT(Py_nb_or); + PUT_SLOT(Py_nb_positive); + PUT_SLOT(Py_nb_power); + PUT_SLOT(Py_nb_remainder); + PUT_SLOT(Py_nb_rshift); + PUT_SLOT(Py_nb_subtract); + PUT_SLOT(Py_nb_true_divide); + PUT_SLOT(Py_nb_xor); +#ifndef IS_PY3K + PUT_SLOT(Py_nb_long); + PUT_SLOT(Py_nb_divide); +#endif +#undef PUT_SLOT + *pidx = idx; +} + +PyTypeObject * +newTypeWithName(const char* name, + const char* cppName, + PyTypeObject *numbers_fromFlag) +{ + // Careful: PyType_FromSpec does not allocate the string. + PyType_Slot newslots[99] = {}; // enough but not too big for the stack + PyType_Spec *newspec = new PyType_Spec; + newspec->name = strdup(name); + newspec->basicsize = SbkNewType_spec.basicsize; + newspec->itemsize = SbkNewType_spec.itemsize; + newspec->flags = SbkNewType_spec.flags; + // we must append all the number methods, so rebuild everything: + int idx = 0; + while (SbkNewType_slots[idx].SLOT) { + newslots[idx].SLOT = SbkNewType_slots[idx].SLOT; + newslots[idx].pfunc = SbkNewType_slots[idx].pfunc; + ++idx; + } + if (numbers_fromFlag) + copyNumberMethods(numbers_fromFlag, newslots, &idx); + newspec->slots = newslots; + PyTypeObject *type = reinterpret_cast(PyType_FromSpec(newspec)); + Py_TYPE(type) = SbkEnumType_TypeF(); + Py_INCREF(Py_TYPE(type)); SbkEnumType* enumType = reinterpret_cast(type); - enumType->cppName = cppName; - enumType->converterPtr = &enumType->converter; + PepType_SETP(enumType)->cppName = cppName; + PepType_SETP(enumType)->converterPtr = &PepType_SETP(enumType)->converter; DeclaredEnumTypes::instance().addEnumType(type); return type; } const char* getCppName(PyTypeObject* enumType) { - assert(Py_TYPE(enumType) == &SbkEnumType_Type); - return reinterpret_cast(enumType)->cppName;; + assert(Py_TYPE(enumType) == SbkEnumType_TypeF()); + return PepType_SETP(reinterpret_cast(enumType))->cppName; } long int getValue(PyObject* enumItem) @@ -585,13 +623,13 @@ long int getValue(PyObject* enumItem) void setTypeConverter(PyTypeObject* enumType, SbkConverter* converter) { //reinterpret_cast(enumType)->converter = converter; - SBK_CONVERTER(enumType) = converter; + *PepType_SGTP(enumType)->converter = converter; } SbkConverter* getTypeConverter(PyTypeObject* enumType) { //return reinterpret_cast(enumType)->converter; - return SBK_CONVERTER(enumType); + return *PepType_SGTP(enumType)->converter; } } // namespace Enum @@ -609,8 +647,17 @@ DeclaredEnumTypes::DeclaredEnumTypes() DeclaredEnumTypes::~DeclaredEnumTypes() { std::list::const_iterator it = m_enumTypes.begin(); - for (; it != m_enumTypes.end(); ++it) - delete *it; + for (; it != m_enumTypes.end(); ++it) { + /* + * PYSIDE-595: This was "delete *it;" before introducing 'PyType_FromSpec'. + * XXX what should I do now? + * Refcounts in tests are 30 or 0 at end. + * When I add the default tp_dealloc, we get negative refcounts! + * So right now I am doing nothing. Surely wrong but no crash. + * See also the comment in function 'createGlobalEnumItem'. + */ + //fprintf(stderr, "ttt %d %s\n", Py_REFCNT(*it), PepType(*it)->tp_name); + } m_enumTypes.clear(); } diff --git a/sources/shiboken2/libshiboken/sbkenum.h b/sources/shiboken2/libshiboken/sbkenum.h index 4e4665423..c1ec7c4c1 100644 --- a/sources/shiboken2/libshiboken/sbkenum.h +++ b/sources/shiboken2/libshiboken/sbkenum.h @@ -46,9 +46,11 @@ extern "C" { -extern LIBSHIBOKEN_API PyTypeObject SbkEnumType_Type; +extern LIBSHIBOKEN_API PyTypeObject *SbkEnumType_TypeF(void); struct SbkObjectType; struct SbkConverter; +struct SbkEnumType; +struct SbkEnumTypePrivate; } // extern "C" @@ -57,7 +59,7 @@ namespace Shiboken inline bool isShibokenEnum(PyObject* pyObj) { - return Py_TYPE(pyObj->ob_type) == &SbkEnumType_Type; + return Py_TYPE(Py_TYPE(pyObj)) == SbkEnumType_TypeF(); } namespace Enum @@ -101,9 +103,8 @@ namespace Enum LIBSHIBOKEN_API PyObject* newItem(PyTypeObject* enumType, long itemValue, const char* itemName = 0); - /// \deprecated Use 'newTypeWithName' - SBK_DEPRECATED(LIBSHIBOKEN_API PyTypeObject* newType(const char* name)); - LIBSHIBOKEN_API PyTypeObject* newTypeWithName(const char* name, const char* cppName); + LIBSHIBOKEN_API PyTypeObject* newTypeWithName(const char* name, const char* cppName, + PyTypeObject *numbers_fromFlag=nullptr); LIBSHIBOKEN_API const char* getCppName(PyTypeObject* type); LIBSHIBOKEN_API long getValue(PyObject* enumItem); diff --git a/sources/shiboken2/libshiboken/sbkpython.h b/sources/shiboken2/libshiboken/sbkpython.h index 6d90f4086..5fe364a29 100644 --- a/sources/shiboken2/libshiboken/sbkpython.h +++ b/sources/shiboken2/libshiboken/sbkpython.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt for Python. @@ -40,8 +40,15 @@ #ifndef SBKPYTHON_H #define SBKPYTHON_H -#include "Python.h" +#include "sbkversion.h" + +#include +#include +// Now we have the usual variables from Python.h . #include "python25compat.h" +#include "shibokenmacros.h" +#include "pep384impl.h" +#include "typespec.h" #if PY_MAJOR_VERSION >= 3 #define IS_PY3K diff --git a/sources/shiboken2/libshiboken/sbkstring.cpp b/sources/shiboken2/libshiboken/sbkstring.cpp index 58f58d286..b92674383 100644 --- a/sources/shiboken2/libshiboken/sbkstring.cpp +++ b/sources/shiboken2/libshiboken/sbkstring.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt for Python. @@ -108,7 +108,7 @@ const char* toCString(PyObject* str, Py_ssize_t* len) } // Return unicode from str instead of uniStr, because the lifetime of the returned pointer // depends on the lifetime of str. - return _PyUnicode_AsString(str); + return _PepUnicode_AsString(str); } #endif if (PyBytes_Check(str)) { diff --git a/sources/shiboken2/libshiboken/sbkversion.h.in b/sources/shiboken2/libshiboken/sbkversion.h.in index 447376c1b..99ee7f93e 100644 --- a/sources/shiboken2/libshiboken/sbkversion.h.in +++ b/sources/shiboken2/libshiboken/sbkversion.h.in @@ -46,5 +46,8 @@ #define SHIBOKEN_MICRO_VERSION @shiboken_MICRO_VERSION@ #define SHIBOKEN_RELEASE_LEVEL "final" #define SHIBOKEN_SERIAL 0 +#define PYTHON_VERSION_MAJOR @PYTHON_VERSION_MAJOR@ +#define PYTHON_VERSION_MINOR @PYTHON_VERSION_MINOR@ +#define PYTHON_VERSION_PATCH @PYTHON_VERSION_PATCH@ #endif diff --git a/sources/shiboken2/libshiboken/shibokenbuffer.cpp b/sources/shiboken2/libshiboken/shibokenbuffer.cpp index 2404aeb66..05b68dade 100644 --- a/sources/shiboken2/libshiboken/shibokenbuffer.cpp +++ b/sources/shiboken2/libshiboken/shibokenbuffer.cpp @@ -84,7 +84,9 @@ PyObject* Shiboken::Buffer::newObject(void* memory, Py_ssize_t size, Type type) view.itemsize = sizeof(char); Py_ssize_t shape[] = { size }; view.shape = shape; - return PyMemoryView_FromBuffer(&view); + // Pep384: This is way too complicated and impossible with the limited api: + //return PyMemoryView_FromBuffer(&view); + return PyMemoryView_FromMemory((char *)view.buf, size, type == ReadOnly ? PyBUF_READ : PyBUF_WRITE); #else return type == ReadOnly ? PyBuffer_FromMemory(memory, size) : PyBuffer_FromReadWriteMemory(memory, size); #endif diff --git a/sources/shiboken2/libshiboken/signature.cpp b/sources/shiboken2/libshiboken/signature.cpp index b266784c0..fc83f89cd 100644 --- a/sources/shiboken2/libshiboken/signature.cpp +++ b/sources/shiboken2/libshiboken/signature.cpp @@ -114,7 +114,7 @@ extern "C" #if EXTENSION_ENABLED // These constants were needed in former versions of the module: -#define PYTHON_HAS_QUALNAME (PY_VERSION_HEX >= 0x03060000) +#define PYTHON_HAS_QUALNAME (PY_VERSION_HEX >= 0x03030000) #define PYTHON_HAS_UNICODE (PY_VERSION_HEX >= 0x03000000) #define PYTHON_HAS_WEAKREF_PYCFUNCTION (PY_VERSION_HEX >= 0x030500A0) #define PYTHON_IS_PYTHON3 (PY_VERSION_HEX >= 0x03000000) @@ -124,15 +124,16 @@ extern "C" #define PYTHON_HAS_METH_REDUCE (PYTHON_HAS_DESCR_REDUCE) #define PYTHON_NEEDS_ITERATOR_FLAG (!PYTHON_IS_PYTHON3) #define PYTHON_EXPOSES_METHODDESCR (PYTHON_IS_PYTHON3) +#define PYTHON_NO_TYPE_IN_FUNCTIONS (!PYTHON_IS_PYTHON3 || Py_LIMITED_API) // These constants are still in use: #define PYTHON_USES_D_COMMON (PY_VERSION_HEX >= 0x03020000) -#define PYTHON_NO_TYPE_IN_FUNCTIONS (!PYTHON_IS_PYTHON3) typedef struct safe_globals_struc { // init part 1: get arg_dict PyObject *helper_module; PyObject *arg_dict; + PyObject *map_dict; // init part 2: run module PyObject *sigparse_func; PyObject *createsig_func; @@ -164,9 +165,9 @@ CreateSignature(PyObject *props, const char *sig_kind) } static PyObject * -pyside_cf_get___signature__(PyCFunctionObject *func) +pyside_cf_get___signature__(PyObject *func) { - return GetSignature_Function(func); + return GetSignature_Function((PyCFunctionObject *)func); } static PyObject * @@ -180,22 +181,107 @@ pyside_sm_get___signature__(PyObject *sm) return ret; } +#ifdef Py_LIMITED_API + +static int +build_qualname_to_func(PyObject *obtype) +{ + PyTypeObject *type = (PyTypeObject *)obtype; + PyMethodDef *meth = PepType(type)->tp_methods; + + if (meth == 0) + return 0; + + for (; meth->ml_name != NULL; meth++) { + PyObject *func = PyCFunction_NewEx(meth, obtype, NULL); + PyObject *qualname = PyObject_GetAttrString(func, "__qualname__"); + if (func == NULL || qualname == NULL) { + return -1; + } + if (PyDict_SetItem(pyside_globals->map_dict, qualname, func) < 0) { + return -1; + } + Py_DECREF(func); + Py_DECREF(qualname); + } + return 0; +} + static PyObject * -pyside_md_get___signature__(PyMethodDescrObject *descr) +qualname_to_typename(PyObject *qualname) { - PyCFunctionObject *func; - PyObject *result; + PyObject *func = PyObject_GetAttrString(qualname, "split"); + PyObject *list = func ? PyObject_CallFunction(func, (char *)"(s)", ".") + : NULL; + PyObject *res = list ? PyList_GetItem(list, 0) : NULL; + Py_XINCREF(res); + Py_XDECREF(func); + Py_XDECREF(list); + return res; +} + +static PyObject * +qualname_to_func(PyObject *ob) +{ + /* + * If we have __qualname__, then we can easily build a mapping + * from __qualname__ to PyCFunction. This is necessary when + * the limited API does not let us go easily from descriptor + * to PyMethodDef. + */ + PyObject *ret; + PyObject *qualname = PyObject_GetAttrString((PyObject *)ob, + "__qualname__"); + if (qualname != NULL) { + ret = PyDict_GetItem(pyside_globals->map_dict, qualname); + if (ret == NULL) { + // do a lazy initialization + PyObject *type_name = qualname_to_typename(qualname); + PyObject *type = PyDict_GetItem(pyside_globals->map_dict, + type_name); + Py_XDECREF(type_name); + if (type == NULL) + Py_RETURN_NONE; + if (build_qualname_to_func(type) < 0) + return NULL; + ret = PyDict_GetItem(pyside_globals->map_dict, qualname); + } + Py_XINCREF(ret); + Py_DECREF(qualname); + } + else + Py_RETURN_NONE; + return ret; +} +#endif - func = (PyCFunctionObject *) - PyCFunction_NewEx(descr->d_method, -#if PYTHON_USES_D_COMMON - (PyObject *)descr->d_common.d_type, NULL +static PyObject * +pyside_md_get___signature__(PyObject *ob) +{ + PyObject *func; + PyObject *result; +#ifndef Py_LIMITED_API + PyMethodDescrObject *descr = (PyMethodDescrObject *)ob; + +# if PYTHON_USES_D_COMMON + func = PyCFunction_NewEx(descr->d_method, + (PyObject *)descr->d_common.d_type, NULL); +# else + func = PyCFunction_NewEx(descr->d_method, + (PyObject *)descr->d_type, NULL); +# endif #else - (PyObject *)descr->d_type, NULL + /* + * With limited access, we cannot use the fields of a method descriptor, + * but in Python 3 we have the __qualname__ field which allows us to + * grab the method object from our registry. + */ + func = qualname_to_func(ob); #endif - ); + if (func == Py_None) + return Py_None; if (func == NULL) - return NULL; + Py_FatalError("missing mapping in MethodDescriptor"); result = pyside_cf_get___signature__(func); Py_DECREF(func); return result; @@ -215,16 +301,15 @@ GetSignature_Function(PyCFunctionObject *func) const char *sig_kind; int flags; - selftype = func->m_self; - if (selftype == NULL) { -#if PYTHON_NO_TYPE_IN_FUNCTIONS - selftype = PyDict_GetItem(pyside_globals->arg_dict, (PyObject *)func); - } + selftype = PyCFunction_GET_SELF((PyObject *)func); + if (selftype == NULL) + selftype = PyDict_GetItem(pyside_globals->map_dict, (PyObject *)func); if (selftype == NULL) { -#endif if (!PyErr_Occurred()) { PyErr_Format(PyExc_SystemError, - "the signature for \"%s\" should exist", func->m_ml->ml_name); + "the signature for \"%s\" should exist", + PepCFunction_GET_NAMESTR(func) + ); } return NULL; } @@ -251,7 +336,7 @@ GetSignature_Function(PyCFunctionObject *func) props = PyDict_GetItem(dict, func_name); if (props == NULL) Py_RETURN_NONE; - flags = PyCFunction_GET_FLAGS(func); + flags = PyCFunction_GET_FLAGS((PyObject *)func); if (flags & METH_CLASS) sig_kind = "classmethod"; else if (flags & METH_STATIC) @@ -347,6 +432,11 @@ init_phase_1(void) goto error; Py_DECREF(v); + // build a dict for diverse mappings + p->map_dict = PyDict_New(); + if (p->map_dict == NULL) + goto error; + // Build a dict for the prepared arguments p->arg_dict = PyDict_New(); if (p->arg_dict == NULL) @@ -387,7 +477,7 @@ error: static int add_more_getsets(PyTypeObject *type, PyGetSetDef *gsp) { - PyObject *dict = type->tp_dict; + PyObject *dict = PepType(type)->tp_dict; for (; gsp->name != NULL; gsp++) { PyObject *descr; @@ -479,16 +569,17 @@ PySideType_Ready(PyTypeObject *type) // PyMethodDescr_Type 'type(str.__dict__["split"])' // PyClassMethodDescr_Type. 'type(dict.__dict__["fromkeys"])' // The latter is not needed until we use class methods in PySide. - md = PyDict_GetItemString(PyString_Type.tp_dict, "split"); + md = PyObject_GetAttrString((PyObject *)&PyString_Type, "split"); if (md == NULL || PyType_Ready(Py_TYPE(md)) < 0 || add_more_getsets(Py_TYPE(md), new_PyMethodDescr_getsets) < 0 || add_more_getsets(&PyCFunction_Type, new_PyCFunction_getsets) < 0 - || add_more_getsets(&PyStaticMethod_Type, new_PyStaticMethod_getsets) < 0 + || add_more_getsets(PepStaticMethod_TypePtr, new_PyStaticMethod_getsets) < 0 || add_more_getsets(&PyType_Type, new_PyType_getsets) < 0) return -1; + Py_DECREF(md); #ifndef _WIN32 - // we enable the stack trace in CI, only. + // We enable the stack trace in CI, only. const char *testEnv = getenv("QTEST_ENVIRONMENT"); if (testEnv && strstr(testEnv, "ci")) signal(SIGSEGV, handler); // install our handler @@ -498,20 +589,12 @@ PySideType_Ready(PyTypeObject *type) return PyType_Ready(type); } -#if PYTHON_NO_TYPE_IN_FUNCTIONS - -typedef struct { - PyObject_HEAD - PyObject *sm_callable; - PyObject *sm_dict; -} staticmethod; - static int build_func_to_type(PyObject *obtype) { PyTypeObject *type = (PyTypeObject *)obtype; - PyObject *dict = type->tp_dict; - PyMethodDef *meth = type->tp_methods; + PyObject *dict = PepType(type)->tp_dict; + PyMethodDef *meth = PepType(type)->tp_methods; if (meth == 0) return 0; @@ -521,19 +604,16 @@ build_func_to_type(PyObject *obtype) PyObject *descr = PyDict_GetItemString(dict, meth->ml_name); if (descr == NULL) return -1; - staticmethod *sm = (staticmethod *)descr; - PyObject *cfunc = sm->sm_callable; - if (cfunc == NULL) - return -1; - if (PyDict_SetItem(pyside_globals->arg_dict, cfunc, obtype) < 0) + PyObject *func = PyObject_GetAttrString(descr, "__func__"); + if (func == NULL || + PyDict_SetItem(pyside_globals->map_dict, func, obtype) < 0) return -1; + Py_DECREF(func); } } return 0; } -#endif - static int PySide_BuildSignatureArgs(PyObject *module, PyObject *type, const char *signatures) @@ -574,6 +654,12 @@ PySide_BuildSignatureArgs(PyObject *module, PyObject *type, return -1; if (PyDict_SetItem(pyside_globals->arg_dict, type_name, arg_tup) < 0) return -1; + /* + * We record also a mapping from type name to type. This helps to lazily + * initialize the Py_LIMITED_API in qualname_to_func(). + */ + if (PyDict_SetItem(pyside_globals->map_dict, type_name, type) < 0) + return -1; return 0; } @@ -650,13 +736,16 @@ PySide_FinishSignatures(PyObject *module, const char *signatures) if (PySide_BuildSignatureArgs(module, module, signatures) < 0) return -1; -#if PYTHON_NO_TYPE_IN_FUNCTIONS /* * Python2 does not abuse the 'm_self' field for the type. So we need to * supply this for all static methods. * * Note: This function crashed when called from PySide_BuildSignatureArgs. * Probably this was too early. + * + * Pep384: We need to switch this always on since we have no access + * to the PyCFunction attributes. Therefore I simplified things + * and always use our own mapping. */ { PyObject *key, *value; @@ -668,12 +757,12 @@ PySide_FinishSignatures(PyObject *module, const char *signatures) while (PyDict_Next(dict, &pos, &key, &value)) { if (PyType_Check(value)) { - if (build_func_to_type(value) < 0) + PyObject *type = value; + if (build_func_to_type(type) < 0) return -1; } } } -#endif return 0; } #endif // EXTENSION_ENABLED diff --git a/sources/shiboken2/libshiboken/tmp-referencetopython/sbkconverter.cpp b/sources/shiboken2/libshiboken/tmp-referencetopython/sbkconverter.cpp index 7c20b9b58..8e351cedd 100644 --- a/sources/shiboken2/libshiboken/tmp-referencetopython/sbkconverter.cpp +++ b/sources/shiboken2/libshiboken/tmp-referencetopython/sbkconverter.cpp @@ -180,7 +180,7 @@ void pythonToCppPointer(SbkObjectType* type, PyObject* pyIn, void* cppOut) { assert(pyIn); assert(cppOut); - SbkObjectType* inType = (SbkObjectType*)pyIn->ob_type; + SbkObjectType* inType = (SbkObjectType*)Py_TYPE(pyIn); if (ObjectType::hasCast(inType)) *((void**)cppOut) = ObjectType::cast(inType, (SbkObject*)pyIn, (PyTypeObject*)type); else diff --git a/sources/shiboken2/libshiboken/tmp-referencetopython/sbkconverter.h b/sources/shiboken2/libshiboken/tmp-referencetopython/sbkconverter.h index f139a491a..cc9ea7a19 100644 --- a/sources/shiboken2/libshiboken/tmp-referencetopython/sbkconverter.h +++ b/sources/shiboken2/libshiboken/tmp-referencetopython/sbkconverter.h @@ -41,7 +41,7 @@ #define SBK_CONVERTER_H #include -#include +#include "sbkpython.h" #include "shibokenmacros.h" #include "basewrapper.h" diff --git a/sources/shiboken2/libshiboken/typespec.cpp b/sources/shiboken2/libshiboken/typespec.cpp new file mode 100644 index 000000000..d532c97ed --- /dev/null +++ b/sources/shiboken2/libshiboken/typespec.cpp @@ -0,0 +1,776 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "typespec.h" +#include + +#if PY_MAJOR_VERSION < 3 + +extern "C" +{ + +// for some reason python 2.7 needs this on Windows +#ifdef WIN32 +static PyGC_Head *_PyGC_generation0; +#endif + +// from pymacro.h +#ifndef Py_PYMACRO_H +#define Py_PYMACRO_H + +/* Minimum value between x and y */ +#define Py_MIN(x, y) (((x) > (y)) ? (y) : (x)) + +/* Maximum value between x and y */ +#define Py_MAX(x, y) (((x) > (y)) ? (x) : (y)) + +/* Absolute value of the number x */ +#define Py_ABS(x) ((x) < 0 ? -(x) : (x)) + +#define _Py_XSTRINGIFY(x) #x + +/* Convert the argument to a string. For example, Py_STRINGIFY(123) is replaced + with "123" by the preprocessor. Defines are also replaced by their value. + For example Py_STRINGIFY(__LINE__) is replaced by the line number, not + by "__LINE__". */ +#define Py_STRINGIFY(x) _Py_XSTRINGIFY(x) + +/* Get the size of a structure member in bytes */ +#define Py_MEMBER_SIZE(type, member) sizeof(((type *)0)->member) + +/* Argument must be a char or an int in [-128, 127] or [0, 255]. */ +#define Py_CHARMASK(c) ((unsigned char)((c) & 0xff)) + +/* Assert a build-time dependency, as an expression. + + Your compile will fail if the condition isn't true, or can't be evaluated + by the compiler. This can be used in an expression: its value is 0. + + Example: + + #define foo_to_char(foo) \ + ((char *)(foo) \ + + Py_BUILD_ASSERT_EXPR(offsetof(struct foo, string) == 0)) + + Written by Rusty Russell, public domain, http://ccodearchive.net/ */ +#define Py_BUILD_ASSERT_EXPR(cond) \ + (sizeof(char [1 - 2*!(cond)]) - 1) + +#define Py_BUILD_ASSERT(cond) do { \ + (void)Py_BUILD_ASSERT_EXPR(cond); \ + } while (0) + +/* Get the number of elements in a visible array + + This does not work on pointers, or arrays declared as [], or function + parameters. With correct compiler support, such usage will cause a build + error (see Py_BUILD_ASSERT_EXPR). + + Written by Rusty Russell, public domain, http://ccodearchive.net/ + + Requires at GCC 3.1+ */ +// Simplified by "0 &&" +#if 0 && (defined(__GNUC__) && !defined(__STRICT_ANSI__) && \ + (((__GNUC__ == 3) && (__GNU_MINOR__ >= 1)) || (__GNUC__ >= 4))) +/* Two gcc extensions. + &a[0] degrades to a pointer: a different type from an array */ +#define Py_ARRAY_LENGTH(array) \ + (sizeof(array) / sizeof((array)[0]) \ + + Py_BUILD_ASSERT_EXPR(!__builtin_types_compatible_p(typeof(array), \ + typeof(&(array)[0])))) +#else +#define Py_ARRAY_LENGTH(array) \ + (sizeof(array) / sizeof((array)[0])) +#endif + + +/* Define macros for inline documentation. */ +#define PyDoc_VAR(name) static char name[] +#define PyDoc_STRVAR(name,str) PyDoc_VAR(name) = PyDoc_STR(str) +#ifdef WITH_DOC_STRINGS +#define PyDoc_STR(str) str +#else +#define PyDoc_STR(str) "" +#endif + +/* Below "a" is a power of 2. */ +/* Round down size "n" to be a multiple of "a". */ +#define _Py_SIZE_ROUND_DOWN(n, a) ((size_t)(n) & ~(size_t)((a) - 1)) +/* Round up size "n" to be a multiple of "a". */ +#define _Py_SIZE_ROUND_UP(n, a) (((size_t)(n) + \ + (size_t)((a) - 1)) & ~(size_t)((a) - 1)) +/* Round pointer "p" down to the closest "a"-aligned address <= "p". */ +#define _Py_ALIGN_DOWN(p, a) ((void *)((uintptr_t)(p) & ~(uintptr_t)((a) - 1))) +/* Round pointer "p" up to the closest "a"-aligned address >= "p". */ +#define _Py_ALIGN_UP(p, a) ((void *)(((uintptr_t)(p) + \ + (uintptr_t)((a) - 1)) & ~(uintptr_t)((a) - 1))) +/* Check if pointer "p" is aligned to "a"-bytes boundary. */ +#define _Py_IS_ALIGNED(p, a) (!((uintptr_t)(p) & (uintptr_t)((a) - 1))) + +#ifdef __GNUC__ +#define Py_UNUSED(name) _unused_ ## name __attribute__((unused)) +#else +#define Py_UNUSED(name) _unused_ ## name +#endif + +#endif /* Py_PYMACRO_H */ + +// from typeobject.c +static int +extra_ivars(PyTypeObject *type, PyTypeObject *base) +{ + size_t t_size = type->tp_basicsize; + size_t b_size = base->tp_basicsize; + + assert(t_size >= b_size); /* Else type smaller than base! */ + if (type->tp_itemsize || base->tp_itemsize) { + /* If itemsize is involved, stricter rules */ + return t_size != b_size || + type->tp_itemsize != base->tp_itemsize; + } + if (type->tp_weaklistoffset && base->tp_weaklistoffset == 0 && + type->tp_weaklistoffset + sizeof(PyObject *) == t_size && + type->tp_flags & Py_TPFLAGS_HEAPTYPE) + t_size -= sizeof(PyObject *); + if (type->tp_dictoffset && base->tp_dictoffset == 0 && + type->tp_dictoffset + sizeof(PyObject *) == t_size && + type->tp_flags & Py_TPFLAGS_HEAPTYPE) + t_size -= sizeof(PyObject *); + + return t_size != b_size; +} + +static void +clear_slots(PyTypeObject *type, PyObject *self) +{ + Py_ssize_t i, n; + PyMemberDef *mp; + + n = Py_SIZE(type); + mp = PyHeapType_GET_MEMBERS((PyHeapTypeObject *)type); + for (i = 0; i < n; i++, mp++) { + if (mp->type == T_OBJECT_EX && !(mp->flags & READONLY)) { + char *addr = (char *)self + mp->offset; + PyObject *obj = *(PyObject **)addr; + if (obj != NULL) { + *(PyObject **)addr = NULL; + Py_DECREF(obj); + } + } + } +} + +static void +subtype_dealloc(PyObject *self) +{ + PyTypeObject *type, *base; + destructor basedealloc; + PyThreadState *tstate = PyThreadState_GET(); + + /* Extract the type; we expect it to be a heap type */ + type = Py_TYPE(self); + assert(type->tp_flags & Py_TPFLAGS_HEAPTYPE); + + /* Test whether the type has GC exactly once */ + + if (!PyType_IS_GC(type)) { + /* It's really rare to find a dynamic type that doesn't have + GC; it can only happen when deriving from 'object' and not + adding any slots or instance variables. This allows + certain simplifications: there's no need to call + clear_slots(), or DECREF the dict, or clear weakrefs. */ + + /* Maybe call finalizer; exit early if resurrected */ + if (type->tp_del) { + type->tp_del(self); + if (self->ob_refcnt > 0) + return; + } + + /* Find the nearest base with a different tp_dealloc */ + base = type; + while ((basedealloc = base->tp_dealloc) == subtype_dealloc) { + assert(Py_SIZE(base) == 0); + base = base->tp_base; + assert(base); + } + + /* Extract the type again; tp_del may have changed it */ + type = Py_TYPE(self); + + /* Call the base tp_dealloc() */ + assert(basedealloc); + basedealloc(self); + + /* Can't reference self beyond this point */ + Py_DECREF(type); + + /* Done */ + return; + } + + /* We get here only if the type has GC */ + + /* UnTrack and re-Track around the trashcan macro, alas */ + /* See explanation at end of function for full disclosure */ + PyObject_GC_UnTrack(self); + ++_PyTrash_delete_nesting; + ++ tstate->trash_delete_nesting; + Py_TRASHCAN_SAFE_BEGIN(self); + --_PyTrash_delete_nesting; + -- tstate->trash_delete_nesting; + /* DO NOT restore GC tracking at this point. weakref callbacks + * (if any, and whether directly here or indirectly in something we + * call) may trigger GC, and if self is tracked at that point, it + * will look like trash to GC and GC will try to delete self again. + */ + + /* Find the nearest base with a different tp_dealloc */ + base = type; + while ((basedealloc = base->tp_dealloc) == subtype_dealloc) { + base = base->tp_base; + assert(base); + } + + /* If we added a weaklist, we clear it. Do this *before* calling + the finalizer (__del__), clearing slots, or clearing the instance + dict. */ + + if (type->tp_weaklistoffset && !base->tp_weaklistoffset) + PyObject_ClearWeakRefs(self); + + /* Maybe call finalizer; exit early if resurrected */ + if (type->tp_del) { + _PyObject_GC_TRACK(self); + type->tp_del(self); + if (self->ob_refcnt > 0) + goto endlabel; /* resurrected */ + else + _PyObject_GC_UNTRACK(self); + /* New weakrefs could be created during the finalizer call. + If this occurs, clear them out without calling their + finalizers since they might rely on part of the object + being finalized that has already been destroyed. */ + if (type->tp_weaklistoffset && !base->tp_weaklistoffset) { + /* Modeled after GET_WEAKREFS_LISTPTR() */ + PyWeakReference **list = (PyWeakReference **) \ + PyObject_GET_WEAKREFS_LISTPTR(self); + while (*list) + _PyWeakref_ClearRef(*list); + } + } + + /* Clear slots up to the nearest base with a different tp_dealloc */ + base = type; + while (base->tp_dealloc == subtype_dealloc) { + if (Py_SIZE(base)) + clear_slots(base, self); + base = base->tp_base; + assert(base); + } + + /* If we added a dict, DECREF it */ + if (type->tp_dictoffset && !base->tp_dictoffset) { + PyObject **dictptr = _PyObject_GetDictPtr(self); + if (dictptr != NULL) { + PyObject *dict = *dictptr; + if (dict != NULL) { + Py_DECREF(dict); + *dictptr = NULL; + } + } + } + + /* Extract the type again; tp_del may have changed it */ + type = Py_TYPE(self); + + /* Call the base tp_dealloc(); first retrack self if + * basedealloc knows about gc. + */ + if (PyType_IS_GC(base)) + _PyObject_GC_TRACK(self); + assert(basedealloc); + basedealloc(self); + + /* Can't reference self beyond this point */ + Py_DECREF(type); + + endlabel: + ++_PyTrash_delete_nesting; + ++ tstate->trash_delete_nesting; + Py_TRASHCAN_SAFE_END(self); + --_PyTrash_delete_nesting; + -- tstate->trash_delete_nesting; + + /* Explanation of the weirdness around the trashcan macros: + + Q. What do the trashcan macros do? + + A. Read the comment titled "Trashcan mechanism" in object.h. + For one, this explains why there must be a call to GC-untrack + before the trashcan begin macro. Without understanding the + trashcan code, the answers to the following questions don't make + sense. + + Q. Why do we GC-untrack before the trashcan and then immediately + GC-track again afterward? + + A. In the case that the base class is GC-aware, the base class + probably GC-untracks the object. If it does that using the + UNTRACK macro, this will crash when the object is already + untracked. Because we don't know what the base class does, the + only safe thing is to make sure the object is tracked when we + call the base class dealloc. But... The trashcan begin macro + requires that the object is *untracked* before it is called. So + the dance becomes: + + GC untrack + trashcan begin + GC track + + Q. Why did the last question say "immediately GC-track again"? + It's nowhere near immediately. + + A. Because the code *used* to re-track immediately. Bad Idea. + self has a refcount of 0, and if gc ever gets its hands on it + (which can happen if any weakref callback gets invoked), it + looks like trash to gc too, and gc also tries to delete self + then. But we're already deleting self. Double deallocation is + a subtle disaster. + + Q. Why the bizarre (net-zero) manipulation of + _PyTrash_delete_nesting around the trashcan macros? + + A. Some base classes (e.g. list) also use the trashcan mechanism. + The following scenario used to be possible: + + - suppose the trashcan level is one below the trashcan limit + + - subtype_dealloc() is called + + - the trashcan limit is not yet reached, so the trashcan level + is incremented and the code between trashcan begin and end is + executed + + - this destroys much of the object's contents, including its + slots and __dict__ + + - basedealloc() is called; this is really list_dealloc(), or + some other type which also uses the trashcan macros + + - the trashcan limit is now reached, so the object is put on the + trashcan's to-be-deleted-later list + + - basedealloc() returns + + - subtype_dealloc() decrefs the object's type + + - subtype_dealloc() returns + + - later, the trashcan code starts deleting the objects from its + to-be-deleted-later list + + - subtype_dealloc() is called *AGAIN* for the same object + + - at the very least (if the destroyed slots and __dict__ don't + cause problems) the object's type gets decref'ed a second + time, which is *BAD*!!! + + The remedy is to make sure that if the code between trashcan + begin and end in subtype_dealloc() is called, the code between + trashcan begin and end in basedealloc() will also be called. + This is done by decrementing the level after passing into the + trashcan block, and incrementing it just before leaving the + block. + + But now it's possible that a chain of objects consisting solely + of objects whose deallocator is subtype_dealloc() will defeat + the trashcan mechanism completely: the decremented level means + that the effective level never reaches the limit. Therefore, we + *increment* the level *before* entering the trashcan block, and + matchingly decrement it after leaving. This means the trashcan + code will trigger a little early, but that's no big deal. + + Q. Are there any live examples of code in need of all this + complexity? + + A. Yes. See SF bug 668433 for code that crashed (when Python was + compiled in debug mode) before the trashcan level manipulations + were added. For more discussion, see SF patches 581742, 575073 + and bug 574207. + */ +} + +static PyTypeObject * +solid_base(PyTypeObject *type) +{ + PyTypeObject *base; + + if (type->tp_base) + base = solid_base(type->tp_base); + else + base = &PyBaseObject_Type; + if (extra_ivars(type, base)) + return type; + else + return base; +} + +/* Calculate the best base amongst multiple base classes. + This is the first one that's on the path to the "solid base". */ + +static PyTypeObject * +best_base(PyObject *bases) +{ + Py_ssize_t i, n; + PyTypeObject *base, *winner, *candidate, *base_i; + PyObject *base_proto; + + assert(PyTuple_Check(bases)); + n = PyTuple_GET_SIZE(bases); + assert(n > 0); + base = NULL; + winner = NULL; + for (i = 0; i < n; i++) { + base_proto = PyTuple_GET_ITEM(bases, i); + if (PyClass_Check(base_proto)) + continue; + if (!PyType_Check(base_proto)) { + PyErr_SetString( + PyExc_TypeError, + "bases must be types"); + return NULL; + } + base_i = (PyTypeObject *)base_proto; + if (base_i->tp_dict == NULL) { + if (PyType_Ready(base_i) < 0) + return NULL; + } + if (!PyType_HasFeature(base_i, Py_TPFLAGS_BASETYPE)) { + PyErr_Format(PyExc_TypeError, + "type '%.100s' is not an acceptable base type", + base_i->tp_name); + return NULL; + } + candidate = solid_base(base_i); + if (winner == NULL) { + winner = candidate; + base = base_i; + } + else if (PyType_IsSubtype(winner, candidate)) + ; + else if (PyType_IsSubtype(candidate, winner)) { + winner = candidate; + base = base_i; + } + else { + PyErr_SetString( + PyExc_TypeError, + "multiple bases have " + "instance lay-out conflict"); + return NULL; + } + } + if (base == NULL) + PyErr_SetString(PyExc_TypeError, + "a new-style class can't have only classic bases"); + return base; +} + +static const short slotoffsets[] = { + -1, /* invalid slot_ */ +/* Generated by typeslots.py */ +0, +0, +offsetof(PyHeapTypeObject, as_mapping.mp_ass_subscript), +offsetof(PyHeapTypeObject, as_mapping.mp_length), +offsetof(PyHeapTypeObject, as_mapping.mp_subscript), +offsetof(PyHeapTypeObject, as_number.nb_absolute), +offsetof(PyHeapTypeObject, as_number.nb_add), +offsetof(PyHeapTypeObject, as_number.nb_and), +offsetof(PyHeapTypeObject, as_number.nb_nonzero), +offsetof(PyHeapTypeObject, as_number.nb_divmod), +offsetof(PyHeapTypeObject, as_number.nb_float), +offsetof(PyHeapTypeObject, as_number.nb_floor_divide), +offsetof(PyHeapTypeObject, as_number.nb_index), +offsetof(PyHeapTypeObject, as_number.nb_inplace_add), +offsetof(PyHeapTypeObject, as_number.nb_inplace_and), +offsetof(PyHeapTypeObject, as_number.nb_inplace_floor_divide), +offsetof(PyHeapTypeObject, as_number.nb_inplace_lshift), +offsetof(PyHeapTypeObject, as_number.nb_inplace_multiply), +offsetof(PyHeapTypeObject, as_number.nb_inplace_or), +offsetof(PyHeapTypeObject, as_number.nb_inplace_power), +offsetof(PyHeapTypeObject, as_number.nb_inplace_remainder), +offsetof(PyHeapTypeObject, as_number.nb_inplace_rshift), +offsetof(PyHeapTypeObject, as_number.nb_inplace_subtract), +offsetof(PyHeapTypeObject, as_number.nb_inplace_true_divide), +offsetof(PyHeapTypeObject, as_number.nb_inplace_xor), +offsetof(PyHeapTypeObject, as_number.nb_int), +offsetof(PyHeapTypeObject, as_number.nb_invert), +offsetof(PyHeapTypeObject, as_number.nb_lshift), +offsetof(PyHeapTypeObject, as_number.nb_multiply), +offsetof(PyHeapTypeObject, as_number.nb_negative), +offsetof(PyHeapTypeObject, as_number.nb_or), +offsetof(PyHeapTypeObject, as_number.nb_positive), +offsetof(PyHeapTypeObject, as_number.nb_power), +offsetof(PyHeapTypeObject, as_number.nb_remainder), +offsetof(PyHeapTypeObject, as_number.nb_rshift), +offsetof(PyHeapTypeObject, as_number.nb_subtract), +offsetof(PyHeapTypeObject, as_number.nb_true_divide), +offsetof(PyHeapTypeObject, as_number.nb_xor), +offsetof(PyHeapTypeObject, as_sequence.sq_ass_item), +offsetof(PyHeapTypeObject, as_sequence.sq_concat), +offsetof(PyHeapTypeObject, as_sequence.sq_contains), +offsetof(PyHeapTypeObject, as_sequence.sq_inplace_concat), +offsetof(PyHeapTypeObject, as_sequence.sq_inplace_repeat), +offsetof(PyHeapTypeObject, as_sequence.sq_item), +offsetof(PyHeapTypeObject, as_sequence.sq_length), +offsetof(PyHeapTypeObject, as_sequence.sq_repeat), +offsetof(PyHeapTypeObject, ht_type.tp_alloc), +offsetof(PyHeapTypeObject, ht_type.tp_base), +offsetof(PyHeapTypeObject, ht_type.tp_bases), +offsetof(PyHeapTypeObject, ht_type.tp_call), +offsetof(PyHeapTypeObject, ht_type.tp_clear), +offsetof(PyHeapTypeObject, ht_type.tp_dealloc), +offsetof(PyHeapTypeObject, ht_type.tp_del), +offsetof(PyHeapTypeObject, ht_type.tp_descr_get), +offsetof(PyHeapTypeObject, ht_type.tp_descr_set), +offsetof(PyHeapTypeObject, ht_type.tp_doc), +offsetof(PyHeapTypeObject, ht_type.tp_getattr), +offsetof(PyHeapTypeObject, ht_type.tp_getattro), +offsetof(PyHeapTypeObject, ht_type.tp_hash), +offsetof(PyHeapTypeObject, ht_type.tp_init), +offsetof(PyHeapTypeObject, ht_type.tp_is_gc), +offsetof(PyHeapTypeObject, ht_type.tp_iter), +offsetof(PyHeapTypeObject, ht_type.tp_iternext), +offsetof(PyHeapTypeObject, ht_type.tp_methods), +offsetof(PyHeapTypeObject, ht_type.tp_new), +offsetof(PyHeapTypeObject, ht_type.tp_repr), +offsetof(PyHeapTypeObject, ht_type.tp_richcompare), +offsetof(PyHeapTypeObject, ht_type.tp_setattr), +offsetof(PyHeapTypeObject, ht_type.tp_setattro), +offsetof(PyHeapTypeObject, ht_type.tp_str), +offsetof(PyHeapTypeObject, ht_type.tp_traverse), +offsetof(PyHeapTypeObject, ht_type.tp_members), +offsetof(PyHeapTypeObject, ht_type.tp_getset), +offsetof(PyHeapTypeObject, ht_type.tp_free), +offsetof(PyHeapTypeObject, as_number.nb_long), +offsetof(PyHeapTypeObject, as_number.nb_divide), +offsetof(PyHeapTypeObject, as_sequence.sq_slice), +}; + +PyObject * +PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) +{ + PyHeapTypeObject *res = (PyHeapTypeObject*)PyType_GenericAlloc(&PyType_Type, 0); + PyTypeObject *type, *base; + PyObject *modname; + char *s; + char *res_start = (char*)res; + PyType_Slot *slot_; + + /* Set the type name and qualname */ + s = (char *)strrchr(spec->name, '.'); // C++11 + if (s == NULL) + s = (char*)spec->name; + else + s++; + + if (res == NULL) + return NULL; + type = &res->ht_type; + /* The flags must be initialized early, before the GC traverses us */ + type->tp_flags = spec->flags | Py_TPFLAGS_HEAPTYPE; + // was PyUnicode_FromString in Python 3 + res->ht_name = PyString_FromString(s); + if (!res->ht_name) + goto fail; + // no ht_qualname in Python 2 + // res->ht_qualname = res->ht_name; + // Py_INCREF(res->ht_qualname); + type->tp_name = spec->name; + if (!type->tp_name) + goto fail; + + /* Adjust for empty tuple bases */ + if (!bases) { + base = &PyBaseObject_Type; + /* See whether Py_tp_base(s) was specified */ + for (slot_ = spec->slots; slot_->slot_; slot_++) { + if (slot_->slot_ == Py_tp_base) + base = (PyTypeObject *)slot_->pfunc; // C++11 + else if (slot_->slot_ == Py_tp_bases) { + bases = (PyObject *)slot_->pfunc; // C++11 + Py_INCREF(bases); + } + } + if (!bases) + bases = PyTuple_Pack(1, base); + if (!bases) + goto fail; + } + else + Py_INCREF(bases); + + /* Calculate best base, and check that all bases are type objects */ + base = best_base(bases); + if (base == NULL) { + goto fail; + } + if (!PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) { + PyErr_Format(PyExc_TypeError, + "type '%.100s' is not an acceptable base type", + base->tp_name); + goto fail; + } + + /* Initialize essential fields */ + // no async in Python 2 + // type->tp_as_async = &res->as_async; + type->tp_as_number = &res->as_number; + type->tp_as_sequence = &res->as_sequence; + type->tp_as_mapping = &res->as_mapping; + type->tp_as_buffer = &res->as_buffer; + /* Set tp_base and tp_bases */ + type->tp_bases = bases; + bases = NULL; + Py_INCREF(base); + type->tp_base = base; + + type->tp_basicsize = spec->basicsize; + type->tp_itemsize = spec->itemsize; + + for (slot_ = spec->slots; slot_->slot_; slot_++) { + if (slot_->slot_ < 0 + || (size_t)slot_->slot_ >= Py_ARRAY_LENGTH(slotoffsets)) { + PyErr_SetString(PyExc_RuntimeError, "invalid slot_ offset"); + goto fail; + } + if (slot_->slot_ == Py_tp_base || slot_->slot_ == Py_tp_bases) + /* Processed above */ + continue; + *(void**)(res_start + slotoffsets[slot_->slot_]) = slot_->pfunc; + + /* need to make a copy of the docstring slot_, which usually + points to a static string literal */ + if (slot_->slot_ == Py_tp_doc) { + // No signature in Python 2 + // const char *old_doc = _PyType_DocWithoutSignature(type->tp_name, slot_->pfunc); + const char *old_doc = (const char *)slot_->pfunc; + size_t len = strlen(old_doc)+1; + char *tp_doc = (char *)PyObject_MALLOC(len); // C++11 + if (tp_doc == NULL) { + PyErr_NoMemory(); + goto fail; + } + memcpy(tp_doc, old_doc, len); + type->tp_doc = tp_doc; + } + } + if (type->tp_dealloc == NULL) { + /* It's a heap type, so needs the heap types' dealloc. + subtype_dealloc will call the base type's tp_dealloc, if + necessary. */ + type->tp_dealloc = subtype_dealloc; + } + + if (PyType_Ready(type) < 0) + goto fail; + + // no ht_hached_keys in Python 2 + // if (type->tp_dictoffset) { + // res->ht_cached_keys = _PyDict_NewKeysForClass(); + // } + + /* Set type.__module__ */ + s = (char *)strrchr(spec->name, '.'); // c++11 + if (s != NULL) { + int err; + // was PyUnicode_FromStringAndSize in Python 3 + modname = PyString_FromStringAndSize( + spec->name, (Py_ssize_t)(s - spec->name)); + if (modname == NULL) { + goto fail; + } + // no PyId_ things in Python 2 + // err = _PyDict_SetItemId(type->tp_dict, &PyId___module__, modname); + err = PyDict_SetItemString(type->tp_dict, "__module__", modname); + Py_DECREF(modname); + if (err != 0) + goto fail; + } else { + // no PyErr_WarnFormat in Python 2 + // if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, + // "builtin type %.200s has no __module__ attribute", + // spec->name)) + char msg[250]; + sprintf(msg, "builtin type %.200s has no __module__ attribute", spec->name); + if (PyErr_WarnEx(PyExc_DeprecationWarning, msg, 1)) + goto fail; + } + + return (PyObject*)res; + + fail: + Py_DECREF(res); + return NULL; +} + +PyObject * +PyType_FromSpec(PyType_Spec *spec) +{ + return PyType_FromSpecWithBases(spec, NULL); +} + +void * +PyType_GetSlot(PyTypeObject *type, int slot_) +{ + if (!PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE) || slot_ < 0) { + PyErr_BadInternalCall(); + return NULL; + } + if ((size_t)slot_ >= Py_ARRAY_LENGTH(slotoffsets)) { + /* Extension module requesting slot_ from a future version */ + return NULL; + } + return *(void**)(((char*)type) + slotoffsets[slot_]); +} + +} // extern "C" +#endif // PY_MAJOR_VERSION < 3 diff --git a/sources/shiboken2/libshiboken/typespec.h b/sources/shiboken2/libshiboken/typespec.h new file mode 100644 index 000000000..799fcb1b8 --- /dev/null +++ b/sources/shiboken2/libshiboken/typespec.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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$ +** +****************************************************************************/ + +#ifndef TYPESPEC_H +#define TYPESPEC_H + +#include +#include "shibokenmacros.h" + +#if PY_MAJOR_VERSION < 3 +extern "C" +{ + +typedef struct{ + int slot_; // slot is somehow reserved in Qt /* slot id, see below */ + void *pfunc; /* function pointer */ +} PyType_Slot; + +typedef struct{ + const char* name; + int basicsize; + int itemsize; + unsigned int flags; + PyType_Slot *slots; /* terminated by slot==0. */ +} PyType_Spec; + +LIBSHIBOKEN_API PyObject *PyType_FromSpec(PyType_Spec*); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 +LIBSHIBOKEN_API PyObject *PyType_FromSpecWithBases(PyType_Spec*, PyObject*); +#endif +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03040000 +LIBSHIBOKEN_API void* PyType_GetSlot(PyTypeObject*, int); +#endif + +// from typeslots.h +/* Do not renumber the file; these numbers are part of the stable ABI. */ +/* Disabled, see #10181 */ +#undef Py_bf_getbuffer +#undef Py_bf_releasebuffer +#define Py_mp_ass_subscript 3 +#define Py_mp_length 4 +#define Py_mp_subscript 5 +#define Py_nb_absolute 6 +#define Py_nb_add 7 +#define Py_nb_and 8 +#define Py_nb_nonzero 9 +#define Py_nb_divmod 10 +#define Py_nb_float 11 +#define Py_nb_floor_divide 12 +#define Py_nb_index 13 +#define Py_nb_inplace_add 14 +#define Py_nb_inplace_and 15 +#define Py_nb_inplace_floor_divide 16 +#define Py_nb_inplace_lshift 17 +#define Py_nb_inplace_multiply 18 +#define Py_nb_inplace_or 19 +#define Py_nb_inplace_power 20 +#define Py_nb_inplace_remainder 21 +#define Py_nb_inplace_rshift 22 +#define Py_nb_inplace_subtract 23 +#define Py_nb_inplace_true_divide 24 +#define Py_nb_inplace_xor 25 +#define Py_nb_int 26 +#define Py_nb_invert 27 +#define Py_nb_lshift 28 +#define Py_nb_multiply 29 +#define Py_nb_negative 30 +#define Py_nb_or 31 +#define Py_nb_positive 32 +#define Py_nb_power 33 +#define Py_nb_remainder 34 +#define Py_nb_rshift 35 +#define Py_nb_subtract 36 +#define Py_nb_true_divide 37 +#define Py_nb_xor 38 +#define Py_sq_ass_item 39 +#define Py_sq_concat 40 +#define Py_sq_contains 41 +#define Py_sq_inplace_concat 42 +#define Py_sq_inplace_repeat 43 +#define Py_sq_item 44 +#define Py_sq_length 45 +#define Py_sq_repeat 46 +#define Py_tp_alloc 47 +#define Py_tp_base 48 +#define Py_tp_bases 49 +#define Py_tp_call 50 +#define Py_tp_clear 51 +#define Py_tp_dealloc 52 +#define Py_tp_del 53 +#define Py_tp_descr_get 54 +#define Py_tp_descr_set 55 +#define Py_tp_doc 56 +#define Py_tp_getattr 57 +#define Py_tp_getattro 58 +#define Py_tp_hash 59 +#define Py_tp_init 60 +#define Py_tp_is_gc 61 +#define Py_tp_iter 62 +#define Py_tp_iternext 63 +#define Py_tp_methods 64 +#define Py_tp_new 65 +#define Py_tp_repr 66 +#define Py_tp_richcompare 67 +#define Py_tp_setattr 68 +#define Py_tp_setattro 69 +#define Py_tp_str 70 +#define Py_tp_traverse 71 +#define Py_tp_members 72 +#define Py_tp_getset 73 +#define Py_tp_free 74 +#define Py_nb_long 75 +#define Py_nb_divide 76 +#define Py_sq_slice 77 +} // extern "C" +#endif // PY_MAJOR_VERSION < 3 +#endif // TYPESPEC_H diff --git a/sources/shiboken2/libshiboken/voidptr.cpp b/sources/shiboken2/libshiboken/voidptr.cpp index 790297595..afb3f4040 100644 --- a/sources/shiboken2/libshiboken/voidptr.cpp +++ b/sources/shiboken2/libshiboken/voidptr.cpp @@ -55,7 +55,8 @@ typedef struct { PyObject *SbkVoidPtrObject_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - SbkVoidPtrObject *self = reinterpret_cast(type->tp_alloc(type, 0)); + SbkVoidPtrObject *self = + reinterpret_cast(PepType(type)->tp_alloc); if (self != 0) { self->cptr = 0; @@ -66,7 +67,7 @@ PyObject *SbkVoidPtrObject_new(PyTypeObject *type, PyObject *args, PyObject *kwd return reinterpret_cast(self); } -#define SbkVoidPtr_Check(op) (Py_TYPE(op) == &SbkVoidPtrType) +#define SbkVoidPtr_Check(op) (Py_TYPE(op) == SbkVoidPtrTypeF()) int SbkVoidPtrObject_init(PyObject *self, PyObject *args, PyObject *kwds) @@ -168,62 +169,6 @@ PyObject *SbkVoidPtrObject_int(PyObject *v) return PyLong_FromVoidPtr(sbkObject->cptr); } -static PyNumberMethods SbkVoidPtrObjectAsNumber = { - /* nb_add */ 0, - /* nb_subtract */ 0, - /* nb_multiply */ 0, -#ifndef IS_PY3K - /* nb_divide */ 0, -#endif - /* nb_remainder */ 0, - /* nb_divmod */ 0, - /* nb_power */ 0, - /* nb_negative */ 0, - /* nb_positive */ 0, - /* nb_absolute */ 0, - /* nb_bool/nb_nonzero */ 0, - /* nb_invert */ 0, - /* nb_lshift */ 0, - /* nb_rshift */ 0, - /* nb_and */ 0, - /* nb_xor */ 0, - /* nb_or */ 0, -#ifndef IS_PY3K - /* nb_coerce */ 0, -#endif - /* nb_int */ SbkVoidPtrObject_int, -#ifdef IS_PY3K - /* nb_reserved */ 0, - /* nb_float */ 0, -#else - /* nb_long */ 0, - /* nb_float */ 0, - /* nb_oct */ 0, - /* nb_hex */ 0, -#endif - - /* nb_inplace_add */ 0, - /* nb_inplace_subtract */ 0, - /* nb_inplace_multiply */ 0, -#ifndef IS_PY3K - /* nb_inplace_div */ 0, -#endif - /* nb_inplace_remainder */ 0, - /* nb_inplace_power */ 0, - /* nb_inplace_lshift */ 0, - /* nb_inplace_rshift */ 0, - /* nb_inplace_and */ 0, - /* nb_inplace_xor */ 0, - /* nb_inplace_or */ 0, - - /* nb_floor_divide */ 0, - /* nb_true_divide */ 0, - /* nb_inplace_floor_divide */ 0, - /* nb_inplace_true_divide */ 0, - - /* nb_index */ 0 -}; - static Py_ssize_t SbkVoidPtrObject_length(PyObject *v) { SbkVoidPtrObject *sbkObject = reinterpret_cast(v); @@ -235,19 +180,6 @@ static Py_ssize_t SbkVoidPtrObject_length(PyObject *v) return sbkObject->size; } -static PySequenceMethods SbkVoidPtrObjectAsSequence = { - /* sq_length */ SbkVoidPtrObject_length, - /* sq_concat */ 0, - /* sq_repeat */ 0, - /* sq_item */ 0, - /* sq_slice */ 0, - /* sq_ass_item */ 0, - /* sq_ass_slice */ 0, - /* sq_contains */ 0, - /* sq_inplace_concat */ 0, - /* sq_inplace_repeat */ 0 -}; - static const char trueString[] = "True" ; static const char falseString[] = "False" ; @@ -257,7 +189,7 @@ PyObject *SbkVoidPtrObject_repr(PyObject *v) SbkVoidPtrObject *sbkObject = reinterpret_cast(v); PyObject *s = PyBytes_FromFormat("%s(%p, %zd, %s)", - Py_TYPE(sbkObject)->tp_name, + PepType((Py_TYPE(sbkObject)))->tp_name, sbkObject->cptr, sbkObject->size, sbkObject->isWritable ? trueString : falseString); @@ -269,7 +201,7 @@ PyObject *SbkVoidPtrObject_str(PyObject *v) { SbkVoidPtrObject *sbkObject = reinterpret_cast(v); PyObject *s = PyBytes_FromFormat("%s(Address %p, Size %zd, isWritable %s)", - Py_TYPE(sbkObject)->tp_name, + PepType((Py_TYPE(sbkObject)))->tp_name, sbkObject->cptr, sbkObject->size, sbkObject->isWritable ? trueString : falseString); @@ -279,61 +211,35 @@ PyObject *SbkVoidPtrObject_str(PyObject *v) // Void pointer type definition. -PyTypeObject SbkVoidPtrType = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) /*ob_size*/ - "VoidPtr", /*tp_name*/ - sizeof(SbkVoidPtrObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - 0, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - SbkVoidPtrObject_repr, /*tp_repr*/ - &SbkVoidPtrObjectAsNumber, /*tp_as_number*/ - &SbkVoidPtrObjectAsSequence, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - SbkVoidPtrObject_str, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - "Void pointer wrapper", /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - SbkVoidPtrObject_richcmp, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - 0, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ - 0, /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - 0, /*tp_dictoffset*/ - SbkVoidPtrObject_init, /*tp_init*/ - 0, /*tp_alloc*/ - SbkVoidPtrObject_new, /*tp_new*/ - 0, /*tp_free*/ - 0, /*tp_is_gc*/ - 0, /*tp_bases*/ - 0, /*tp_mro*/ - 0, /*tp_cache*/ - 0, /*tp_subclasses*/ - 0, /*tp_weaklist*/ - 0, /*tp_del*/ - 0, /*tp_version_tag*/ -#if PY_MAJOR_VERSION > 3 || PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 4 - 0 /*tp_finalize*/ -#endif +static PyType_Slot SbkVoidPtrType_slots[] = { + {Py_tp_repr, (void *)SbkVoidPtrObject_repr}, + {Py_nb_int, (void *)SbkVoidPtrObject_int}, + {Py_sq_length, (void *)SbkVoidPtrObject_length}, + {Py_tp_str, (void *)SbkVoidPtrObject_str}, + {Py_tp_richcompare, (void *)SbkVoidPtrObject_richcmp}, + {Py_tp_init, (void *)SbkVoidPtrObject_init}, + {Py_tp_new, (void *)SbkVoidPtrObject_new}, + {Py_tp_dealloc, (void *)SbkDummyDealloc}, + {0, 0} }; +static PyType_Spec SbkVoidPtrType_spec = { + "shiboken2.libshiboken.VoidPtr", + sizeof(SbkVoidPtrObject), + 0, + Py_TPFLAGS_DEFAULT, + SbkVoidPtrType_slots, +}; + } +PyTypeObject *SbkVoidPtrTypeF(void) +{ + static PyTypeObject *type = nullptr; + if (!type) + type = (PyTypeObject *)PyType_FromSpec(&SbkVoidPtrType_spec); + return type; +} namespace VoidPtr { @@ -341,7 +247,7 @@ static int voidPointerInitialized = false; void init() { - if (PyType_Ready(reinterpret_cast(&SbkVoidPtrType)) < 0) + if (PyType_Ready(reinterpret_cast(SbkVoidPtrTypeF())) < 0) Py_FatalError("[libshiboken] Failed to initialize Shiboken.VoidPtr type."); else voidPointerInitialized = true; @@ -350,9 +256,9 @@ void init() void addVoidPtrToModule(PyObject *module) { if (voidPointerInitialized) { - Py_INCREF(&SbkVoidPtrType); - PyModule_AddObject(module, SbkVoidPtrType.tp_name, - reinterpret_cast(&SbkVoidPtrType)); + Py_INCREF(SbkVoidPtrTypeF()); + PyModule_AddObject(module, PepType_GetNameStr(SbkVoidPtrTypeF()), + reinterpret_cast(SbkVoidPtrTypeF())); } } @@ -361,7 +267,7 @@ static PyObject *createVoidPtr(void *cppIn, Py_ssize_t size = 0, bool isWritable if (!cppIn) Py_RETURN_NONE; - SbkVoidPtrObject *result = PyObject_NEW(SbkVoidPtrObject, &SbkVoidPtrType); + SbkVoidPtrObject *result = PyObject_New(SbkVoidPtrObject, SbkVoidPtrTypeF()); if (!result) Py_RETURN_NONE; @@ -434,7 +340,7 @@ static PythonToCppFunc PythonBufferToCppIsConvertible(PyObject *pyIn) SbkConverter *createConverter() { - SbkConverter *converter = Shiboken::Conversions::createConverter(&SbkVoidPtrType, toPython); + SbkConverter *converter = Shiboken::Conversions::createConverter(SbkVoidPtrTypeF(), toPython); Shiboken::Conversions::addPythonToCppValueConversion(converter, VoidPtrToCpp, VoidPtrToCppIsConvertible); diff --git a/sources/shiboken2/libshiboken/voidptr.h b/sources/shiboken2/libshiboken/voidptr.h index 240895df8..e74c1045e 100644 --- a/sources/shiboken2/libshiboken/voidptr.h +++ b/sources/shiboken2/libshiboken/voidptr.h @@ -40,7 +40,7 @@ #ifndef VOIDPTR_H #define VOIDPTR_H -#include +#include "sbkpython.h" #include "shibokenmacros.h" #include "sbkconverter.h" @@ -48,7 +48,7 @@ extern "C" { // Void pointer type declaration. -extern LIBSHIBOKEN_API PyTypeObject SbkVoidPtrType; +extern LIBSHIBOKEN_API PyTypeObject *SbkVoidPtrTypeF(void); } // extern "C" -- cgit v1.2.3