From 170d47f92d03b81e74e8623cf15db9282957452d Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Mon, 22 May 2017 17:50:30 +0200 Subject: move everying into sources/shiboken2 in preparation for a subtree merge. this should not be necessary to do in a separate commit, but git is a tad stupid about following history correctly without it. --- sources/shiboken2/libshiboken/sbkenum.cpp | 623 ++++++++++++++++++++++++++++++ 1 file changed, 623 insertions(+) create mode 100644 sources/shiboken2/libshiboken/sbkenum.cpp (limited to 'sources/shiboken2/libshiboken/sbkenum.cpp') diff --git a/sources/shiboken2/libshiboken/sbkenum.cpp b/sources/shiboken2/libshiboken/sbkenum.cpp new file mode 100644 index 000000000..009d9ab2f --- /dev/null +++ b/sources/shiboken2/libshiboken/sbkenum.cpp @@ -0,0 +1,623 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 "sbkenum.h" +#include "sbkstring.h" +#include "sbkconverter.h" +#include "basewrapper.h" +#include "sbkdbg.h" +#include "autodecref.h" +#include "typeresolver.h" +#include "sbkpython.h" + +#include +#include +#include + +#define SBK_ENUM(ENUM) reinterpret_cast(ENUM) + +extern "C" +{ + +struct SbkEnumType +{ + PyHeapTypeObject super; + SbkConverter** converterPtr; + SbkConverter* converter; + const char* cppName; +}; + +struct SbkEnumObject +{ + PyObject_HEAD + long ob_value; + PyObject* ob_name; +}; + +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)); + else + fprintf(fp, "%s(%ld)", self->ob_type->tp_name, enumObj->ob_value); + Py_END_ALLOW_THREADS + return 0; +} + +static PyObject* SbkEnumObject_name(PyObject* self, void*) +{ + SbkEnumObject *enum_self = reinterpret_cast(self); + + if (enum_self->ob_name == NULL) + Py_RETURN_NONE; + + Py_INCREF(enum_self->ob_name); + return enum_self->ob_name; +} + +static PyObject* SbkEnum_tp_new(PyTypeObject *type, PyObject *args, PyObject *) +{ + long itemValue = 0; + if (!PyArg_ParseTuple(args, "|l:__new__", &itemValue)) + return 0; + + SbkEnumObject* self = PyObject_New(SbkEnumObject, type); + if (!self) + return 0; + self->ob_value = itemValue; + PyObject* item = Shiboken::Enum::getEnumItemFromValue(type, itemValue); + if (item) { + self->ob_name = SbkEnumObject_name(item, 0); + Py_XDECREF(item); + } else { + self->ob_name = 0; + } + return reinterpret_cast(self); +} + +/* Notes: + * On Py3k land we use long type when using integer numbers. However, on older + * versions of Python (version 2) we need to convert it to int type, + * respectively. + * + * Thus calling PyInt_FromLong() will result in calling PyLong_FromLong in + * Py3k. + */ +static PyObject* enum_int(PyObject* v) +{ + return PyInt_FromLong(SBK_ENUM(v)->ob_value); +} + +static long getNumberValue(PyObject* v) +{ + PyObject* number = PyNumber_Long(v); + long result = PyLong_AsLong(number); + Py_XDECREF(number); + return result; +} + +static PyObject* enum_and(PyObject* self, PyObject* b) +{ + if (!PyNumber_Check(b)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + long valA = SBK_ENUM(self)->ob_value; + long valB = getNumberValue(b); + return PyInt_FromLong(valA & valB); +} + +static PyObject* enum_or(PyObject* self, PyObject* b) +{ + if (!PyNumber_Check(b)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + long valA = SBK_ENUM(self)->ob_value; + long valB = getNumberValue(b); + return PyInt_FromLong(valA | valB); +} + +static PyObject* enum_xor(PyObject* self, PyObject* b) +{ + if (!PyNumber_Check(b)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + long valA = SBK_ENUM(self)->ob_value; + long valB = getNumberValue(b); + return PyInt_FromLong(valA ^ valB); +} + +static int enum_bool(PyObject* v) +{ + return (SBK_ENUM(v)->ob_value > 0); +} + +static PyObject* enum_add(PyObject* self, PyObject* v) +{ + long valA = SBK_ENUM(self)->ob_value; + long valB = getNumberValue(v); + return PyInt_FromLong(valA + valB); +} + +static PyObject* enum_subtract(PyObject* self, PyObject* v) +{ + long valA = SBK_ENUM(self)->ob_value; + long valB = getNumberValue(v); + return PyInt_FromLong(valA - valB); +} + +static PyObject* enum_multiply(PyObject* self, PyObject* v) +{ + long valA = SBK_ENUM(self)->ob_value; + long valB = getNumberValue(v); + return PyInt_FromLong(valA * valB); +} + +#ifndef IS_PY3K +static PyObject* enum_divide(PyObject* self, PyObject* v) +{ + long valA = SBK_ENUM(self)->ob_value; + long valB = getNumberValue(v); + return PyLong_FromLong(valA / valB); +} +#endif + +static PyObject* enum_richcompare(PyObject* self, PyObject* other, int op) +{ + int result = 0; + if (!PyNumber_Check(other)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + long valA = SBK_ENUM(self)->ob_value; + long valB = getNumberValue(other); + + switch (op) { + case Py_EQ: + result = (valA == valB); + break; + case Py_NE: + result = (valA != valB); + break; + case Py_LE: + result = (valA <= valB); + break; + case Py_GE: + result = (valA >= valB); + break; + case Py_LT: + result = (valA < valB); + break; + case Py_GT: + result = (valA > valB); + break; + default: + PyErr_BadArgument(); + return NULL; + } + if (result) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +static Py_hash_t enum_hash(PyObject* pyObj) +{ + Py_hash_t val = reinterpret_cast(pyObj)->ob_value; + if (val == -1) + val = -2; + return val; +} + +static PyGetSetDef SbkEnumGetSetList[] = { + {const_cast("name"), &SbkEnumObject_name, 0, 0, 0}, + {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, +#ifndef IS_PY3K + /* nb_coerce */ 0, +#endif + /* nb_int */ enum_int, +#ifdef IS_PY3K + /* nb_reserved */ 0, + /* nb_float */ 0, +#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, +#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 +}; + +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 +}; + +void SbkEnumTypeDealloc(PyObject* pyObj) +{ + SbkEnumType* sbkType = reinterpret_cast(pyObj); + + PyObject_GC_UnTrack(pyObj); + Py_TRASHCAN_SAFE_BEGIN(pyObj); + if (sbkType->converter) { + Shiboken::Conversions::deleteConverter(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)); + if (!newType) + return 0; + return reinterpret_cast(newType); +} + +} // extern "C" + +namespace Shiboken { + +class DeclaredEnumTypes +{ +public: + DeclaredEnumTypes(); + ~DeclaredEnumTypes(); + static DeclaredEnumTypes& instance(); + void addEnumType(PyTypeObject* type); + +private: + DeclaredEnumTypes(const DeclaredEnumTypes&); + DeclaredEnumTypes& operator=(const DeclaredEnumTypes&); + std::list m_enumTypes; +}; + +namespace Enum { + +bool check(PyObject* pyObj) +{ + return Py_TYPE(pyObj->ob_type) == &SbkEnumType_Type; +} + +PyObject* getEnumItemFromValue(PyTypeObject* enumType, long itemValue) +{ + PyObject *key, *value; + Py_ssize_t pos = 0; + PyObject* values = PyDict_GetItemString(enumType->tp_dict, const_cast("values")); + + while (PyDict_Next(values, &pos, &key, &value)) { + SbkEnumObject *obj = reinterpret_cast(value); + if (obj->ob_value == itemValue) { + Py_INCREF(obj); + return value; + } + } + return 0; +} + +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; + if (PyType_Ready(enumType) < 0) + return 0; + Shiboken::TypeResolver::createValueTypeResolver(cppName); + if (shortName) + Shiboken::TypeResolver::createValueTypeResolver(shortName); + return enumType; +} + +PyTypeObject* createGlobalEnum(PyObject* module, const char* name, const char* fullName, const char* cppName, PyTypeObject* flagsType) +{ + PyTypeObject* enumType = createEnum(fullName, cppName, name, flagsType); + Shiboken::TypeResolver::createValueTypeResolver("Qt::WindowType"); + Shiboken::TypeResolver::createValueTypeResolver("WindowType"); + if (enumType && PyModule_AddObject(module, name, reinterpret_cast(enumType)) < 0) + return 0; + if (flagsType && PyModule_AddObject(module, flagsType->tp_name, reinterpret_cast(flagsType)) < 0) + return 0; + return enumType; +} + +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; + 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) + return 0; + 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; +} + +bool createScopedEnumItem(PyTypeObject* enumType, SbkObjectType* scope, const char* itemName, long itemValue) +{ + PyObject* enumItem = createEnumItem(enumType, itemName, itemValue); + if (enumItem) { + if (PyDict_SetItemString(scope->super.ht_type.tp_dict, itemName, enumItem) < 0) + return false; + Py_DECREF(enumItem); + return true; + } + return false; +} + +PyObject* newItem(PyTypeObject* enumType, long itemValue, const char* itemName) +{ + bool newValue = true; + SbkEnumObject* enumObj; + if (!itemName) { + enumObj = reinterpret_cast(getEnumItemFromValue(enumType, itemValue)); + if (enumObj) + return reinterpret_cast(enumObj); + + newValue = false; + } + + enumObj = PyObject_New(SbkEnumObject, enumType); + if (!enumObj) + return 0; + + enumObj->ob_name = itemName ? PyBytes_FromString(itemName) : 0; + enumObj->ob_value = itemValue; + + if (newValue) { + PyObject* values = PyDict_GetItemString(enumType->tp_dict, const_cast("values")); + if (!values) { + values = PyDict_New(); + PyDict_SetItemString(enumType->tp_dict, const_cast("values"), values); + Py_DECREF(values); // ^ values still alive, because setitemstring incref it + } + PyDict_SetItemString(values, itemName, reinterpret_cast(enumObj)); + } + + return reinterpret_cast(enumObj); +} + +PyTypeObject* newType(const char* name) +{ + return newTypeWithName(name, ""); +} + +PyTypeObject* newTypeWithName(const char* name, const char* cppName) +{ + 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; + + SbkEnumType* enumType = reinterpret_cast(type); + enumType->cppName = cppName; + enumType->converterPtr = &enumType->converter; + DeclaredEnumTypes::instance().addEnumType(type); + return type; +} + +const char* getCppName(PyTypeObject* enumType) +{ + assert(Py_TYPE(enumType) == &SbkEnumType_Type); + return reinterpret_cast(enumType)->cppName;; +} + +long int getValue(PyObject* enumItem) +{ + assert(Shiboken::Enum::check(enumItem)); + return reinterpret_cast(enumItem)->ob_value; +} + +void setTypeConverter(PyTypeObject* enumType, SbkConverter* converter) +{ + //reinterpret_cast(enumType)->converter = converter; + SBK_CONVERTER(enumType) = converter; +} + +SbkConverter* getTypeConverter(PyTypeObject* enumType) +{ + //return reinterpret_cast(enumType)->converter; + return SBK_CONVERTER(enumType); +} + +} // namespace Enum + +DeclaredEnumTypes& DeclaredEnumTypes::instance() +{ + static DeclaredEnumTypes me; + return me; +} + +DeclaredEnumTypes::DeclaredEnumTypes() +{ +} + +DeclaredEnumTypes::~DeclaredEnumTypes() +{ + std::list::const_iterator it = m_enumTypes.begin(); + for (; it != m_enumTypes.end(); ++it) + delete *it; + m_enumTypes.clear(); +} + +void DeclaredEnumTypes::addEnumType(PyTypeObject* type) +{ + m_enumTypes.push_back(type); +} + +} -- cgit v1.2.3