diff options
Diffstat (limited to 'sources/shiboken2/libshiboken/sbkenum.cpp')
-rw-r--r-- | sources/shiboken2/libshiboken/sbkenum.cpp | 249 |
1 files changed, 193 insertions, 56 deletions
diff --git a/sources/shiboken2/libshiboken/sbkenum.cpp b/sources/shiboken2/libshiboken/sbkenum.cpp index 71fcf5f64..ad3513163 100644 --- a/sources/shiboken2/libshiboken/sbkenum.cpp +++ b/sources/shiboken2/libshiboken/sbkenum.cpp @@ -39,6 +39,8 @@ #include "sbkenum.h" #include "sbkstring.h" +#include "sbkstaticstrings.h" +#include "sbkstaticstrings_p.h" #include "sbkconverter.h" #include "basewrapper.h" #include "sbkdbg.h" @@ -105,16 +107,18 @@ static PyObject *SbkEnum_tp_new(PyTypeObject *type, PyObject *args, PyObject *) if (!self) return nullptr; self->ob_value = itemValue; - PyObject *item = Shiboken::Enum::getEnumItemFromValue(type, itemValue); - if (item) { - self->ob_name = SbkEnumObject_name(item, nullptr); - Py_XDECREF(item); - } else { - self->ob_name = nullptr; - } + Shiboken::AutoDecRef item(Shiboken::Enum::getEnumItemFromValue(type, itemValue)); + self->ob_name = item.object() ? SbkEnumObject_name(item, nullptr) : nullptr; return reinterpret_cast<PyObject *>(self); } +void enum_object_dealloc(PyObject *ob) +{ + auto self = reinterpret_cast<SbkEnumObject *>(ob); + Py_XDECREF(self->ob_name); + Sbk_object_dealloc(ob); +} + static PyObject *enum_op(enum_func f, PyObject *a, PyObject *b) { PyObject *valA = a; PyObject *valB = b; @@ -258,6 +262,23 @@ static PyGetSetDef SbkEnumGetSetList[] = { {nullptr, nullptr, nullptr, nullptr, nullptr} // Sentinel }; +#if PY_VERSION_HEX < 0x03000000 + +static PyObject *SbkEnumType_repr(PyObject *type) +{ + Shiboken::AutoDecRef mod(PyObject_GetAttr(type, Shiboken::PyMagicName::module())); + if (mod.isNull()) + return nullptr; + Shiboken::AutoDecRef name(PyObject_GetAttr(type, Shiboken::PyMagicName::qualname())); + if (name.isNull()) + return nullptr; + return PyString_FromFormat("<class '%s.%s'>", + PyString_AS_STRING(mod.object()), + PyString_AS_STRING(name.object())); +} + +#endif // PY_VERSION_HEX < 0x03000000 + static void SbkEnumTypeDealloc(PyObject *pyObj); static PyObject *SbkEnumTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *kwds); @@ -285,10 +306,13 @@ static PyType_Slot SbkEnumType_Type_slots[] = { {Py_tp_alloc, (void *)PyType_GenericAlloc}, {Py_tp_new, (void *)SbkEnumTypeTpNew}, {Py_tp_free, (void *)PyObject_GC_Del}, +#if PY_VERSION_HEX < 0x03000000 + {Py_tp_repr, (void *)SbkEnumType_repr}, +#endif {0, nullptr} }; static PyType_Spec SbkEnumType_Type_spec = { - "Shiboken.EnumType", + "1:Shiboken.EnumType", 0, // filled in later sizeof(PyMemberDef), Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, @@ -302,7 +326,7 @@ PyTypeObject *SbkEnumType_TypeF(void) if (!type) { SbkEnumType_Type_spec.basicsize = PepHeapType_SIZE + sizeof(SbkEnumTypePrivate); - type = (PyTypeObject *)PyType_FromSpec(&SbkEnumType_Type_spec); + type = reinterpret_cast<PyTypeObject *>(SbkType_FromSpec(&SbkEnumType_Type_spec)); } return type; } @@ -321,6 +345,11 @@ void SbkEnumTypeDealloc(PyObject *pyObj) #ifndef Py_LIMITED_API Py_TRASHCAN_SAFE_END(pyObj); #endif + if (PepRuntime_38_flag) { + // PYSIDE-939: Handling references correctly. + // This was not needed before Python 3.8 (Python issue 35810) + Py_DECREF(Py_TYPE(pyObj)); + } } PyObject *SbkEnumTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *kwds) @@ -334,6 +363,107 @@ PyObject *SbkEnumTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *kwd } // extern "C" +/////////////////////////////////////////////////////////////// +// +// PYSIDE-15: Pickling Support for Qt Enum objects +// This works very well and fixes the issue. +// +extern "C" { + +static void init_enum(); // forward + +static PyObject *enum_unpickler = nullptr; + +// Pickling: reduce the Qt Enum object +static PyObject *enum___reduce__(PyObject *obj) +{ + init_enum(); + return Py_BuildValue("O(Ni)", + enum_unpickler, + Py_BuildValue("s", Py_TYPE(obj)->tp_name), + PyInt_AS_LONG(obj)); +} + +} // extern "C" + +namespace Shiboken { namespace Enum { + +// Unpickling: rebuild the Qt Enum object +PyObject *unpickleEnum(PyObject *enum_class_name, PyObject *value) +{ + Shiboken::AutoDecRef parts(PyObject_CallMethod(enum_class_name, + const_cast<char *>("split"), const_cast<char *>("s"), ".")); + if (parts.isNull()) + return nullptr; + PyObject *top_name = PyList_GetItem(parts, 0); // borrowed ref + if (top_name == nullptr) + return nullptr; + PyObject *module = PyImport_GetModule(top_name); + if (module == nullptr) { + PyErr_Format(PyExc_ImportError, "could not import module %.200s", + Shiboken::String::toCString(top_name)); + return nullptr; + } + Shiboken::AutoDecRef cur_thing(module); + int len = PyList_Size(parts); + for (int idx = 1; idx < len; ++idx) { + PyObject *name = PyList_GetItem(parts, idx); // borrowed ref + PyObject *thing = PyObject_GetAttr(cur_thing, name); + if (thing == nullptr) { + PyErr_Format(PyExc_ImportError, "could not import Qt Enum type %.200s", + Shiboken::String::toCString(enum_class_name)); + return nullptr; + } + cur_thing.reset(thing); + } + PyObject *klass = cur_thing; + return PyObject_CallFunctionObjArgs(klass, value, nullptr); +} + +} // namespace Enum +} // namespace Shiboken + +extern "C" { + +// Initialization +static bool _init_enum() +{ + static PyObject *shiboken_name = Py_BuildValue("s", "shiboken2"); + if (shiboken_name == nullptr) + return false; + Shiboken::AutoDecRef shibo(PyImport_GetModule(shiboken_name)); + if (shibo.isNull()) + return false; + Shiboken::AutoDecRef sub(PyObject_GetAttr(shibo, shiboken_name)); + PyObject *mod = sub.object(); + if (mod == nullptr) { + // We are in the build dir and already in shiboken. + PyErr_Clear(); + mod = shibo.object(); + } + enum_unpickler = PyObject_GetAttrString(mod, "_unpickle_enum"); + if (enum_unpickler == nullptr) + return false; + return true; +} + +static void init_enum() +{ + if (!(enum_unpickler || _init_enum())) + Py_FatalError("could not load enum pickling helper function"); +} + +static PyMethodDef SbkEnumObject_Methods[] = { + {const_cast<char *>("__reduce__"), reinterpret_cast<PyCFunction>(enum___reduce__), + METH_NOARGS, nullptr}, + {nullptr, nullptr, 0, nullptr} // Sentinel +}; + +} // extern "C" + +// +/////////////////////////////////////////////////////////////// + namespace Shiboken { class DeclaredEnumTypes @@ -364,12 +494,12 @@ PyObject *getEnumItemFromValue(PyTypeObject *enumType, long itemValue) { PyObject *key, *value; Py_ssize_t pos = 0; - PyObject *values = PyDict_GetItemString(enumType->tp_dict, const_cast<char *>("values")); + PyObject *values = PyDict_GetItem(enumType->tp_dict, Shiboken::PyName::values()); while (PyDict_Next(values, &pos, &key, &value)) { auto *obj = reinterpret_cast<SbkEnumObject *>(value); if (obj->ob_value == itemValue) { - Py_INCREF(obj); + Py_INCREF(value); return value; } } @@ -377,75 +507,77 @@ 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, flagsType); - if (PyType_Ready(enumType) < 0) + if (PyType_Ready(enumType) < 0) { + Py_XDECREF(enumType); return nullptr; + } return enumType; } PyTypeObject *createGlobalEnum(PyObject *module, const char *name, const char *fullName, const char *cppName, PyTypeObject *flagsType) { - PyTypeObject *enumType = createEnum(fullName, cppName, name, flagsType); - if (enumType && PyModule_AddObject(module, name, reinterpret_cast<PyObject *>(enumType)) < 0) + PyTypeObject *enumType = createEnum(fullName, cppName, flagsType); + if (enumType && PyModule_AddObject(module, name, reinterpret_cast<PyObject *>(enumType)) < 0) { + Py_DECREF(enumType); return nullptr; + } if (flagsType && PyModule_AddObject(module, PepType_GetNameStr(flagsType), - reinterpret_cast<PyObject *>(flagsType)) < 0) + reinterpret_cast<PyObject *>(flagsType)) < 0) { + Py_DECREF(enumType); return nullptr; + } return enumType; } PyTypeObject *createScopedEnum(SbkObjectType *scope, const char *name, const char *fullName, const char *cppName, PyTypeObject *flagsType) { - PyTypeObject *enumType = createEnum(fullName, cppName, name, flagsType); + PyTypeObject *enumType = createEnum(fullName, cppName, flagsType); if (enumType && PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(scope)->tp_dict, name, - reinterpret_cast<PyObject *>(enumType)) < 0) + reinterpret_cast<PyObject *>(enumType)) < 0) { + Py_DECREF(enumType); return nullptr; + } if (flagsType && PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(scope)->tp_dict, PepType_GetNameStr(flagsType), - reinterpret_cast<PyObject *>(flagsType)) < 0) + reinterpret_cast<PyObject *>(flagsType)) < 0) { + Py_DECREF(enumType); 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(enumType->tp_dict, itemName, enumItem) < 0) { + Py_DECREF(enumItem); return nullptr; - Py_DECREF(enumItem); + } return enumItem; } bool createGlobalEnumItem(PyTypeObject *enumType, PyObject *module, const char *itemName, long itemValue) { PyObject *enumItem = createEnumItem(enumType, itemName, itemValue); - if (enumItem) { - if (PyModule_AddObject(module, itemName, enumItem) < 0) - return false; - // @TODO This Py_DECREF causes crashes on exit with a debug Python interpreter, essentially - // causing a use-after-free in the GC. This is now commented out to cause a memory leak - // instead of a crash. Proper memory management of Enum types and items should be - // implemented. See PYSIDE-488. This will require proper allocation and deallocation of - // the underlying Enum PyHeapType, which is currently just deallocated at application exit. - // Py_DECREF(enumItem); - return true; - } - return false; + if (!enumItem) + return false; + int ok = PyModule_AddObject(module, itemName, enumItem); + Py_DECREF(enumItem); + return ok >= 0; } bool createScopedEnumItem(PyTypeObject *enumType, PyTypeObject *scope, const char *itemName, long itemValue) { - if (PyObject *enumItem = createEnumItem(enumType, itemName, itemValue)) { - if (PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(scope)->tp_dict, itemName, enumItem) < 0) - return false; - Py_DECREF(enumItem); - return true; - } - return false; + PyObject *enumItem = createEnumItem(enumType, itemName, itemValue); + if (!enumItem) + return false; + int ok = PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(scope)->tp_dict, itemName, enumItem); + Py_DECREF(enumItem); + return ok >= 0; } bool createScopedEnumItem(PyTypeObject *enumType, SbkObjectType *scope, const char *itemName, long itemValue) @@ -475,11 +607,16 @@ newItem(PyTypeObject *enumType, long itemValue, const char *itemName) enumObj->ob_value = itemValue; if (newValue) { - PyObject *values = PyDict_GetItemString(enumType->tp_dict, const_cast<char *>("values")); - if (!values) { - values = PyDict_New(); - PyDict_SetItemString(enumType->tp_dict, const_cast<char *>("values"), values); - Py_DECREF(values); // ^ values still alive, because setitemstring incref it + auto dict = enumType->tp_dict; // Note: 'values' is borrowed + PyObject *values = PyDict_GetItemWithError(dict, Shiboken::PyName::values()); + if (values == nullptr) { + if (PyErr_Occurred()) + return nullptr; + Shiboken::AutoDecRef new_values(values = PyDict_New()); + if (values == nullptr) + return nullptr; + if (PyDict_SetItem(dict, Shiboken::PyName::values(), values) < 0) + return nullptr; } PyDict_SetItemString(values, itemName, reinterpret_cast<PyObject *>(enumObj)); } @@ -491,6 +628,7 @@ static PyType_Slot SbkNewType_slots[] = { {Py_tp_repr, (void *)SbkEnumObject_repr}, {Py_tp_str, (void *)SbkEnumObject_repr}, {Py_tp_getset, (void *)SbkEnumGetSetList}, + {Py_tp_methods, (void *)SbkEnumObject_Methods}, {Py_tp_new, (void *)SbkEnum_tp_new}, {Py_nb_add, (void *)enum_add}, {Py_nb_subtract, (void *)enum_subtract}, @@ -512,7 +650,7 @@ static PyType_Slot SbkNewType_slots[] = { {Py_nb_index, (void *)enum_int}, {Py_tp_richcompare, (void *)enum_richcompare}, {Py_tp_hash, (void *)enum_hash}, - {Py_tp_dealloc, (void *)object_dealloc}, + {Py_tp_dealloc, (void *)enum_object_dealloc}, {0, nullptr} }; static PyType_Spec SbkNewType_spec = { @@ -584,13 +722,13 @@ newTypeWithName(const char *name, const char *cppName, PyTypeObject *numbers_fromFlag) { - // Careful: PyType_FromSpec does not allocate the string. + // Careful: SbkType_FromSpec does not allocate the string. PyType_Slot newslots[99] = {}; // enough but not too big for the stack - auto *newspec = new PyType_Spec; - newspec->name = strdup(name); - newspec->basicsize = SbkNewType_spec.basicsize; - newspec->itemsize = SbkNewType_spec.itemsize; - newspec->flags = SbkNewType_spec.flags; + PyType_Spec newspec; + 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) { @@ -600,10 +738,9 @@ newTypeWithName(const char *name, } if (numbers_fromFlag) copyNumberMethods(numbers_fromFlag, newslots, &idx); - newspec->slots = newslots; - auto *type = reinterpret_cast<PyTypeObject *>(PyType_FromSpec(newspec)); + newspec.slots = newslots; + auto *type = reinterpret_cast<PyTypeObject *>(SbkType_FromSpec(&newspec)); Py_TYPE(type) = SbkEnumType_TypeF(); - Py_INCREF(Py_TYPE(type)); auto *enumType = reinterpret_cast<SbkEnumType *>(type); PepType_SETP(enumType)->cppName = cppName; @@ -649,7 +786,7 @@ DeclaredEnumTypes::DeclaredEnumTypes() = default; DeclaredEnumTypes::~DeclaredEnumTypes() { /* - * PYSIDE-595: This was "delete *it;" before introducing 'PyType_FromSpec'. + * PYSIDE-595: This was "delete *it;" before introducing 'SbkType_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! |