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/CMakeLists.txt | 77 + sources/shiboken2/libshiboken/autodecref.h | 115 ++ sources/shiboken2/libshiboken/basewrapper.cpp | 1518 ++++++++++++++++++++ sources/shiboken2/libshiboken/basewrapper.h | 451 ++++++ sources/shiboken2/libshiboken/basewrapper_p.h | 287 ++++ sources/shiboken2/libshiboken/bindingmanager.cpp | 344 +++++ sources/shiboken2/libshiboken/bindingmanager.h | 109 ++ sources/shiboken2/libshiboken/conversions.h | 731 ++++++++++ sources/shiboken2/libshiboken/debugfreehook.cpp | 194 +++ sources/shiboken2/libshiboken/debugfreehook.h | 61 + sources/shiboken2/libshiboken/gilstate.cpp | 68 + sources/shiboken2/libshiboken/gilstate.h | 63 + sources/shiboken2/libshiboken/helper.cpp | 142 ++ sources/shiboken2/libshiboken/helper.h | 136 ++ sources/shiboken2/libshiboken/python25compat.h | 104 ++ sources/shiboken2/libshiboken/sbkconverter.cpp | 572 ++++++++ sources/shiboken2/libshiboken/sbkconverter.h | 367 +++++ sources/shiboken2/libshiboken/sbkconverter_p.h | 574 ++++++++ sources/shiboken2/libshiboken/sbkdbg.h | 124 ++ sources/shiboken2/libshiboken/sbkenum.cpp | 623 ++++++++ sources/shiboken2/libshiboken/sbkenum.h | 118 ++ sources/shiboken2/libshiboken/sbkmodule.cpp | 124 ++ sources/shiboken2/libshiboken/sbkmodule.h | 115 ++ sources/shiboken2/libshiboken/sbkpython.h | 74 + sources/shiboken2/libshiboken/sbkstring.cpp | 202 +++ sources/shiboken2/libshiboken/sbkstring.h | 75 + sources/shiboken2/libshiboken/sbkversion.h.in | 50 + sources/shiboken2/libshiboken/shiboken.h | 60 + sources/shiboken2/libshiboken/shibokenbuffer.cpp | 96 ++ sources/shiboken2/libshiboken/shibokenbuffer.h | 85 ++ sources/shiboken2/libshiboken/shibokenmacros.h | 63 + sources/shiboken2/libshiboken/threadstatesaver.cpp | 69 + sources/shiboken2/libshiboken/threadstatesaver.h | 66 + .../tmp-referencetopython/sbkconverter.cpp | 213 +++ .../tmp-referencetopython/sbkconverter.h | 191 +++ sources/shiboken2/libshiboken/typeresolver.cpp | 162 +++ sources/shiboken2/libshiboken/typeresolver.h | 139 ++ 37 files changed, 8562 insertions(+) create mode 100644 sources/shiboken2/libshiboken/CMakeLists.txt create mode 100644 sources/shiboken2/libshiboken/autodecref.h create mode 100644 sources/shiboken2/libshiboken/basewrapper.cpp create mode 100644 sources/shiboken2/libshiboken/basewrapper.h create mode 100644 sources/shiboken2/libshiboken/basewrapper_p.h create mode 100644 sources/shiboken2/libshiboken/bindingmanager.cpp create mode 100644 sources/shiboken2/libshiboken/bindingmanager.h create mode 100644 sources/shiboken2/libshiboken/conversions.h create mode 100644 sources/shiboken2/libshiboken/debugfreehook.cpp create mode 100644 sources/shiboken2/libshiboken/debugfreehook.h create mode 100644 sources/shiboken2/libshiboken/gilstate.cpp create mode 100644 sources/shiboken2/libshiboken/gilstate.h create mode 100644 sources/shiboken2/libshiboken/helper.cpp create mode 100644 sources/shiboken2/libshiboken/helper.h create mode 100644 sources/shiboken2/libshiboken/python25compat.h create mode 100644 sources/shiboken2/libshiboken/sbkconverter.cpp create mode 100644 sources/shiboken2/libshiboken/sbkconverter.h create mode 100644 sources/shiboken2/libshiboken/sbkconverter_p.h create mode 100644 sources/shiboken2/libshiboken/sbkdbg.h create mode 100644 sources/shiboken2/libshiboken/sbkenum.cpp create mode 100644 sources/shiboken2/libshiboken/sbkenum.h create mode 100644 sources/shiboken2/libshiboken/sbkmodule.cpp create mode 100644 sources/shiboken2/libshiboken/sbkmodule.h create mode 100644 sources/shiboken2/libshiboken/sbkpython.h create mode 100644 sources/shiboken2/libshiboken/sbkstring.cpp create mode 100644 sources/shiboken2/libshiboken/sbkstring.h create mode 100644 sources/shiboken2/libshiboken/sbkversion.h.in create mode 100644 sources/shiboken2/libshiboken/shiboken.h create mode 100644 sources/shiboken2/libshiboken/shibokenbuffer.cpp create mode 100644 sources/shiboken2/libshiboken/shibokenbuffer.h create mode 100644 sources/shiboken2/libshiboken/shibokenmacros.h create mode 100644 sources/shiboken2/libshiboken/threadstatesaver.cpp create mode 100644 sources/shiboken2/libshiboken/threadstatesaver.h create mode 100644 sources/shiboken2/libshiboken/tmp-referencetopython/sbkconverter.cpp create mode 100644 sources/shiboken2/libshiboken/tmp-referencetopython/sbkconverter.h create mode 100644 sources/shiboken2/libshiboken/typeresolver.cpp create mode 100644 sources/shiboken2/libshiboken/typeresolver.h (limited to 'sources/shiboken2/libshiboken') diff --git a/sources/shiboken2/libshiboken/CMakeLists.txt b/sources/shiboken2/libshiboken/CMakeLists.txt new file mode 100644 index 000000000..9ec49d5bd --- /dev/null +++ b/sources/shiboken2/libshiboken/CMakeLists.txt @@ -0,0 +1,77 @@ +project(libshiboken) + +option(ENABLE_VERSION_SUFFIX "Used to use current version in suffix to generated files. This is used to allow multiples versions installed simultaneous." FALSE) +if(ENABLE_VERSION_SUFFIX) + set(shiboken2_SUFFIX "-${shiboken_MAJOR_VERSION}.${shiboken_MINOR_VERSION}") +else() + set(shiboken2_SUFFIX "") +endif() + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/sbkversion.h.in" + "${CMAKE_CURRENT_BINARY_DIR}/sbkversion.h" @ONLY) + +#Find installed sparsehash +find_path(SPARSEHASH_INCLUDE_PATH sparseconfig.h PATH_SUFFIXES "/google/sparsehash") +if(SPARSEHASH_INCLUDE_PATH) + message(STATUS "Using system hash found in: ${SPARSEHASH_INCLUDE_PATH}") +else() + set(SPARSEHASH_INCLUDE_PATH ${CMAKE_SOURCE_DIR}/ext/sparsehash) +endif() + +set(libshiboken_MAJOR_VERSION ${shiboken_MAJOR_VERSION}) +set(libshiboken_MINOR_VERSION ${shiboken_MINOR_VERSION}) +set(libshiboken_MICRO_VERSION ${shiboken_MICRO_VERSION}) +set(libshiboken_VERSION "${libshiboken_MAJOR_VERSION}.${libshiboken_MINOR_VERSION}.${libshiboken_MICRO_VERSION}") +set(libshiboken_SOVERSION "${libshiboken_MAJOR_VERSION}.${libshiboken_MINOR_VERSION}") + +set(libshiboken_SRC +basewrapper.cpp +debugfreehook.cpp +gilstate.cpp +helper.cpp +sbkconverter.cpp +sbkenum.cpp +sbkmodule.cpp +sbkstring.cpp +bindingmanager.cpp +threadstatesaver.cpp +typeresolver.cpp +shibokenbuffer.cpp +) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + ${SBK_PYTHON_INCLUDE_DIR} + ${SPARSEHASH_INCLUDE_PATH}) +add_library(libshiboken SHARED ${libshiboken_SRC}) +target_link_libraries(libshiboken ${SBK_PYTHON_LIBRARIES}) +set_target_properties(libshiboken PROPERTIES OUTPUT_NAME "shiboken2${shiboken2_SUFFIX}${PYTHON_EXTENSION_SUFFIX}" + VERSION ${libshiboken_VERSION} + SOVERSION ${libshiboken_SOVERSION} + DEFINE_SYMBOL LIBSHIBOKEN_EXPORTS) + +install(FILES + autodecref.h + basewrapper.h + bindingmanager.h + conversions.h + gilstate.h + helper.h + sbkconverter.h + sbkenum.h + sbkmodule.h + python25compat.h + sbkdbg.h + sbkstring.h + shiboken.h + shibokenmacros.h + threadstatesaver.h + typeresolver.h + shibokenbuffer.h + sbkpython.h + "${CMAKE_CURRENT_BINARY_DIR}/sbkversion.h" + DESTINATION include/shiboken2${shiboken2_SUFFIX}) +install(TARGETS libshiboken EXPORT shiboken2 + LIBRARY DESTINATION "${LIB_INSTALL_DIR}" + ARCHIVE DESTINATION "${LIB_INSTALL_DIR}" + RUNTIME DESTINATION bin) diff --git a/sources/shiboken2/libshiboken/autodecref.h b/sources/shiboken2/libshiboken/autodecref.h new file mode 100644 index 000000000..1fefcc259 --- /dev/null +++ b/sources/shiboken2/libshiboken/autodecref.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef AUTODECREF_H +#define AUTODECREF_H + +#include "sbkpython.h" +#include "basewrapper.h" + +struct SbkObject; +namespace Shiboken +{ + +/** + * AutoDecRef holds a PyObject pointer and decrement its reference counter when destroyed. + */ +struct LIBSHIBOKEN_API AutoDecRef +{ +public: + /** + * AutoDecRef constructor. + * \param pyobj A borrowed reference to a Python object + */ + explicit AutoDecRef(PyObject* pyObj) : m_pyObj(pyObj) {} + /** + * AutoDecRef constructor. + * \param pyobj A borrowed reference to a Python object + */ + explicit AutoDecRef(SbkObject* pyObj) : m_pyObj(reinterpret_cast(pyObj)) {} + + /// Decref the borrowed python reference + ~AutoDecRef() + { + Py_XDECREF(m_pyObj); + } + + inline bool isNull() const { return m_pyObj == 0; } + /// Returns the pointer of the Python object being held. + inline PyObject* object() { return m_pyObj; } + inline operator PyObject*() { return m_pyObj; } + inline operator PyTupleObject*() { return reinterpret_cast(m_pyObj); } + inline operator bool() const { return m_pyObj; } + inline PyObject* operator->() { return m_pyObj; } + + template + T cast() + { + return reinterpret_cast(m_pyObj); + } + + /** + * Decref the current borrowed python reference and take the reference + * borrowed by \p other, so other.isNull() will return true. + */ + void operator=(AutoDecRef& other) + { + Py_XDECREF(m_pyObj); + m_pyObj = other.m_pyObj; + other.m_pyObj = 0; + } + + /** + * Decref the current borrowed python reference and borrow \p other. + */ + void operator=(PyObject* other) + { + Py_XDECREF(m_pyObj); + m_pyObj = other; + } +private: + PyObject* m_pyObj; + AutoDecRef(const AutoDecRef&); + AutoDecRef& operator=(const AutoDecRef&); +}; + +} // namespace Shiboken + +#endif // AUTODECREF_H + diff --git a/sources/shiboken2/libshiboken/basewrapper.cpp b/sources/shiboken2/libshiboken/basewrapper.cpp new file mode 100644 index 000000000..b3f99a8a5 --- /dev/null +++ b/sources/shiboken2/libshiboken/basewrapper.cpp @@ -0,0 +1,1518 @@ +/**************************************************************************** +** +** 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 "basewrapper.h" +#include "basewrapper_p.h" +#include "sbkconverter.h" +#include "sbkenum.h" +#include "autodecref.h" +#include "typeresolver.h" +#include "gilstate.h" +#include +#include +#include +#include +#include +#include +#include "threadstatesaver.h" + +namespace { + void _destroyParentInfo(SbkObject* obj, bool keepReference); +} + +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 PyObject *SbkObjectGetDict(PyObject* pObj, void *) +{ + SbkObject *obj = reinterpret_cast(pObj); + if (!obj->ob_dict) + obj->ob_dict = PyDict_New(); + if (!obj->ob_dict) + return 0; + Py_INCREF(obj->ob_dict); + return obj->ob_dict; +} + +static PyGetSetDef SbkObjectGetSetList[] = { + {const_cast("__dict__"), SbkObjectGetDict, 0, 0, 0}, + {0, 0, 0, 0, 0} // Sentinel +}; + +static int SbkObject_traverse(PyObject* self, visitproc visit, void* arg) +{ + SbkObject* sbkSelf = reinterpret_cast(self); + + //Visit children + Shiboken::ParentInfo* pInfo = sbkSelf->d->parentInfo; + if (pInfo) { + std::set::const_iterator it = pInfo->children.begin(); + for(; it != pInfo->children.end(); ++it) + Py_VISIT(*it); + } + + //Visit refs + Shiboken::RefCountMap* rInfo = sbkSelf->d->referredObjects; + if (rInfo) { + Shiboken::RefCountMap::const_iterator it = rInfo->begin(); + for (; it != rInfo->end(); ++it) { + std::list::const_iterator ref = it->second.begin(); + for(; ref != it->second.end(); ++ref) + Py_VISIT(*ref); + } + } + + if (sbkSelf->ob_dict) + Py_VISIT(sbkSelf->ob_dict); + return 0; +} + +static int SbkObject_clear(PyObject* self) +{ + SbkObject* sbkSelf = reinterpret_cast(self); + + Shiboken::Object::removeParent(sbkSelf); + + if (sbkSelf->d->parentInfo) + _destroyParentInfo(sbkSelf, true); + + Shiboken::Object::clearReferences(sbkSelf); + + if (sbkSelf->ob_dict) + Py_CLEAR(sbkSelf->ob_dict); + 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 void SbkDeallocWrapperCommon(PyObject* pyObj, bool canDelete) +{ + SbkObject* sbkObj = reinterpret_cast(pyObj); + PyTypeObject* pyType = Py_TYPE(pyObj); + + // 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); + + // Ensure that the GC is no longer tracking this object to avoid a + // possible reentrancy problem. Since there are multiple steps involved + // in deallocating a SbkObject it is possible for the garbage collector to + // be invoked and it trying to delete this object while it is still in + // progress from the first time around, resulting in a double delete and a + // crash. + PyObject_GC_UnTrack(pyObj); + + // Check that Python is still initialized as sometimes this is called by a static destructor + // after Python interpeter is shutdown. + if (sbkObj->weakreflist && Py_IsInitialized()) + PyObject_ClearWeakRefs(pyObj); + + // 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) { + Shiboken::DeallocVisitor visitor(sbkObj); + Shiboken::walkThroughClassHierarchy(pyObj->ob_type, &visitor); + } else { + void* cptr = sbkObj->d->cptr[0]; + Shiboken::Object::deallocData(sbkObj, true); + + Shiboken::ThreadStateSaver threadSaver; + if (Py_IsInitialized()) + threadSaver.save(); + sbkType->d->cpp_dtor(cptr); + } + } else { + Shiboken::Object::deallocData(sbkObj, true); + } + + if (needTypeDecref) + Py_DECREF(pyType); +} + +void SbkDeallocWrapper(PyObject* pyObj) +{ + SbkDeallocWrapperCommon(pyObj, true); +} + +void SbkDeallocWrapperWithPrivateDtor(PyObject* self) +{ + SbkDeallocWrapperCommon(self, false); +} + +void SbkObjectTypeDealloc(PyObject* pyObj) +{ + SbkObjectType* sbkType = 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; + } + 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; + } + 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. + PyObject* name; + PyObject* pyBases; + PyObject* dict; + static const char* kwlist[] = { "name", "bases", "dict", 0}; + + 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++) { + PyObject* baseType = PyTuple_GET_ITEM(pyBases, i); + 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); + return 0; + } + } +#endif + + // 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)); + if (!newType) + return 0; + + Shiboken::ObjectType::initPrivateData(newType); + SbkObjectTypePrivate* d = newType->d; + + 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; + } 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; + } + if (bases.size() == 1) + d->original_name = strdup(bases.front()->d->original_name); + else + d->original_name = strdup("object"); + d->user_data = 0; + d->d_func = 0; + d->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); + } + + return reinterpret_cast(newType); +} + +PyObject* SbkObjectTpNew(PyTypeObject* subtype, PyObject*, PyObject*) +{ + SbkObject* self = PyObject_GC_New(SbkObject, 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); + d->cptr = new void*[numBases]; + std::memset(d->cptr, 0, sizeof(void*)*numBases); + d->hasOwnership = 1; + d->containsCppWrapper = 0; + d->validCppObject = 0; + d->parentInfo = 0; + d->referredObjects = 0; + d->cppObjectCreated = 0; + self->ob_dict = 0; + self->weakreflist = 0; + self->d = d; + PyObject_GC_Track(reinterpret_cast(self)); + return reinterpret_cast(self); +} + + +} //extern "C" + + +namespace +{ + +void _destroyParentInfo(SbkObject* obj, bool keepReference) +{ + Shiboken::ParentInfo* pInfo = obj->d->parentInfo; + if (pInfo) { + while(!pInfo->children.empty()) { + SbkObject* first = *pInfo->children.begin(); + // Mark child as invalid + Shiboken::Object::invalidate(first); + Shiboken::Object::removeParent(first, false, keepReference); + } + Shiboken::Object::removeParent(obj, false); + } +} + +} + +namespace Shiboken +{ + +static void decRefPyObjectList(const std::list &pyObj, PyObject* skip = 0); + +static void _walkThroughClassHierarchy(PyTypeObject* currentType, HierarchyVisitor* visitor) +{ + PyObject* bases = 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))) { + continue; + } else { + SbkObjectType* sbkType = reinterpret_cast(type); + if (sbkType->d->is_user_type) + _walkThroughClassHierarchy(type, visitor); + else + visitor->visit(sbkType); + } + if (visitor->wasFinished()) + break; + } +} + +void walkThroughClassHierarchy(PyTypeObject* currentType, HierarchyVisitor* visitor) +{ + _walkThroughClassHierarchy(currentType, visitor); + visitor->done(); +} + + +bool importModule(const char* moduleName, PyTypeObject*** cppApiPtr) +{ + PyObject* sysModules = PyImport_GetModuleDict(); + PyObject* module = PyDict_GetItemString(sysModules, moduleName); + if (!module) { + module = PyImport_ImportModule(moduleName); + if (!module) + return false; + } else { + Py_INCREF(module); + } + + Shiboken::AutoDecRef cppApi(PyObject_GetAttrString(module, "_Cpp_Api")); + Py_DECREF(module); + + if (cppApi.isNull()) + return false; + +#ifdef IS_PY3K + if (PyCapsule_CheckExact(cppApi)) + *cppApiPtr = reinterpret_cast(PyCapsule_GetPointer(cppApi, 0)); +#else + // Python 2.6 doesn't have PyCapsule API, so let's keep usign PyCObject on all Python 2.x + if (PyCObject_Check(cppApi)) + *cppApiPtr = reinterpret_cast(PyCObject_AsVoidPtr(cppApi)); +#endif + return true; +} + +// Wrapper metatype and base type ---------------------------------------------------------- + +void DtorCallerVisitor::visit(SbkObjectType* node) +{ + m_ptrs.push_back(std::make_pair(m_pyObj->d->cptr[m_ptrs.size()], node)); +} + +void DtorCallerVisitor::done() +{ + std::list >::const_iterator it = m_ptrs.begin(); + for (; it != m_ptrs.end(); ++it) { + Shiboken::ThreadStateSaver threadSaver; + threadSaver.save(); + it->second->d->cpp_dtor(it->first); + } +} + +void DeallocVisitor::done() +{ + Shiboken::Object::deallocData(m_pyObj, true); + DtorCallerVisitor::done(); +} + +namespace Module { void init(); } +namespace Conversions { void init(); } + +void init() +{ + static bool shibokenAlreadInitialised = false; + if (shibokenAlreadInitialised) + return; + + Module::init(); + Conversions::init(); + + initTypeResolver(); + PyEval_InitThreads(); + + //Init private data + Shiboken::ObjectType::initPrivateData(&SbkObject_Type); + + if (PyType_Ready(&SbkEnumType_Type) < 0) + Py_FatalError("[libshiboken] Failed to initialise Shiboken.SbkEnumType metatype."); + + if (PyType_Ready(&SbkObjectType_Type) < 0) + Py_FatalError("[libshiboken] Failed to initialise Shiboken.BaseWrapperType metatype."); + + if (PyType_Ready(reinterpret_cast(&SbkObject_Type)) < 0) + Py_FatalError("[libshiboken] Failed to initialise Shiboken.BaseWrapper type."); + + shibokenAlreadInitialised = true; +} + +void setErrorAboutWrongArguments(PyObject* args, const char* funcName, const char** cppOverloads) +{ + std::string msg; + std::string params; + if (args) { + if (PyTuple_Check(args)) { + for (Py_ssize_t i = 0, max = PyTuple_GET_SIZE(args); i < max; ++i) { + if (i) + params += ", "; + PyObject* arg = PyTuple_GET_ITEM(args, i); + params += arg->ob_type->tp_name; + } + } else { + params = args->ob_type->tp_name; + } + } + + if (!cppOverloads) { + msg = "'" + std::string(funcName) + "' called with wrong argument types: " + params; + } else { + msg = "'" + std::string(funcName) + "' called with wrong argument types:\n "; + msg += funcName; + msg += '('; + msg += params; + msg += ")\n"; + msg += "Supported signatures:"; + for (int i = 0; cppOverloads[i]; ++i) { + msg += "\n "; + msg += funcName; + msg += '('; + msg += cppOverloads[i]; + msg += ')'; + } + } + PyErr_SetString(PyExc_TypeError, msg.c_str()); + +} + +class FindBaseTypeVisitor : public HierarchyVisitor +{ + public: + FindBaseTypeVisitor(PyTypeObject* typeToFind) : m_found(false), m_typeToFind(typeToFind) {} + virtual void visit(SbkObjectType* node) + { + if (reinterpret_cast(node) == m_typeToFind) { + m_found = true; + finish(); + } + } + bool found() const { return m_found; } + + private: + bool m_found; + PyTypeObject* m_typeToFind; +}; + +std::list splitPyObject(PyObject* pyObj) +{ + std::list result; + if (PySequence_Check(pyObj)) { + AutoDecRef lst(PySequence_Fast(pyObj, "Invalid keep reference object.")); + if (!lst.isNull()) { + for (Py_ssize_t i = 0, i_max = PySequence_Fast_GET_SIZE(lst.object()); i < i_max; ++i) { + PyObject* item = PySequence_Fast_GET_ITEM(lst.object(), i); + if (Object::checkType(item)) + result.push_back(reinterpret_cast(item)); + } + } + } else { + result.push_back(reinterpret_cast(pyObj)); + } + return result; +} + +static void decRefPyObjectList(const std::list& lst, PyObject *skip) +{ + std::list::const_iterator iter = lst.begin(); + while(iter != lst.end()) { + if (*iter != skip) + Py_DECREF(*iter); + ++iter; + } +} + +namespace ObjectType +{ + +bool checkType(PyTypeObject* type) +{ + return PyType_IsSubtype(type, reinterpret_cast(&SbkObject_Type)); +} + +bool isUserType(PyTypeObject* type) +{ + return checkType(type) && reinterpret_cast(type)->d->is_user_type; +} + +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); + return false; + } + return true; +} + + +bool hasExternalCppConversions(SbkObjectType*) { return false; } // DEPRECATED. +bool isExternalConvertible(SbkObjectType *, PyObject *) { return false; } // DEPRECATED. +void setExternalCppConversionFunction(SbkObjectType*, ExtendedToCppFunc) {} // DEPRECATED. +void setExternalIsConvertibleFunction(SbkObjectType*, ExtendedIsConvertibleFunc) {} // DEPRECATED. +void* callExternalCppConversion(SbkObjectType*, PyObject*) { return 0; } // DEPRECATED. + + +bool hasCast(SbkObjectType* type) +{ + return type->d->mi_specialcast; +} + +void* cast(SbkObjectType* sourceType, SbkObject* obj, PyTypeObject* targetType) +{ + return sourceType->d->mi_specialcast(Object::cppPointer(obj, targetType), reinterpret_cast(targetType)); +} + +void setCastFunction(SbkObjectType* type, SpecialCastFunction func) +{ + type->d->mi_specialcast = func; +} + +void setOriginalName(SbkObjectType* self, const char* name) +{ + if (self->d->original_name) + free(self->d->original_name); + self->d->original_name = strdup(name); +} + +const char* getOriginalName(SbkObjectType* self) +{ + return self->d->original_name; +} + +void setTypeDiscoveryFunctionV2(SbkObjectType* self, TypeDiscoveryFuncV2 func) +{ + self->d->type_discovery = func; +} + +void setTypeDiscoveryFunction(SbkObjectType* self, TypeDiscoveryFunc func) +{ + self->d->type_discovery = (TypeDiscoveryFuncV2)func; +} + +TypeDiscoveryFunc getTypeDiscoveryFunction(SbkObjectType* self) +{ + // This is an illegal cast because the return value is different, + // but nobody ever used this function, so... =] + return (TypeDiscoveryFunc)self->d->type_discovery; +} + +void copyMultimpleheritance(SbkObjectType* self, 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; +} + +void setMultipleIheritanceFunction(SbkObjectType* self, MultipleInheritanceInitFunction function) +{ + self->d->mi_init = function; +} + +MultipleInheritanceInitFunction getMultipleIheritanceFunction(SbkObjectType* self) +{ + return self->d->mi_init; +} + +void setDestructorFunction(SbkObjectType* self, ObjectDestructor func) +{ + self->d->cpp_dtor = func; +} + +void initPrivateData(SbkObjectType* self) +{ + self->d = new SbkObjectTypePrivate; + memset(self->d, 0, sizeof(SbkObjectTypePrivate)); +} + +bool introduceWrapperType(PyObject* enclosingObject, + const char* typeName, const char* originalName, + SbkObjectType* type, 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); + 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); + } + } + + if (PyType_Ready(reinterpret_cast(type)) < 0) + return false; + + if (isInnerClass) + return PyDict_SetItemString(enclosingObject, typeName, reinterpret_cast(type)) == 0; + + //PyModule_AddObject steals type's reference. + Py_INCREF(reinterpret_cast(type)); + return PyModule_AddObject(enclosingObject, typeName, reinterpret_cast(type)) == 0; +} + +void setSubTypeInitHook(SbkObjectType* self, SubTypeInitHook func) +{ + self->d->subtype_init = func; +} + +void* getTypeUserData(SbkObjectType* self) +{ + return self->d->user_data; +} + +void setTypeUserData(SbkObjectType* self, void* userData, DeleteUserDataFunc d_func) +{ + self->d->user_data = userData; + self->d->d_func = d_func; +} + +} // namespace ObjectType + + +namespace Object +{ + +static void recursive_invalidate(SbkObject* self, std::set& seen); + +bool checkType(PyObject* pyObj) +{ + return ObjectType::checkType(pyObj->ob_type); +} + +bool isUserType(PyObject* pyObj) +{ + return ObjectType::isUserType(pyObj->ob_type); +} + +Py_hash_t hash(PyObject* pyObj) +{ + assert(Shiboken::Object::checkType(pyObj)); + return reinterpret_cast(pyObj); +} + +static void setSequenceOwnership(PyObject* pyObj, bool owner) +{ + if (PySequence_Check(pyObj)) { + std::list objs = splitPyObject(pyObj); + std::list::const_iterator it = objs.begin(); + for(; it != objs.end(); ++it) { + if (owner) + getOwnership(*it); + else + releaseOwnership(*it); + } + } else if (Object::checkType(pyObj)) { + if (owner) + getOwnership(reinterpret_cast(pyObj)); + else + releaseOwnership(reinterpret_cast(pyObj)); + } +} + +void setValidCpp(SbkObject* pyObj, bool value) +{ + pyObj->d->validCppObject = value; +} + +void setHasCppWrapper(SbkObject* pyObj, bool value) +{ + pyObj->d->containsCppWrapper = value; +} + +bool hasCppWrapper(SbkObject* pyObj) +{ + return pyObj->d->containsCppWrapper; +} + +bool wasCreatedByPython(SbkObject* pyObj) +{ + return pyObj->d->cppObjectCreated; +} + +void callCppDestructors(SbkObject* pyObj) +{ + SbkObjectType* sbkType = reinterpret_cast(Py_TYPE(pyObj)); + if (sbkType->d->is_multicpp) { + Shiboken::DtorCallerVisitor visitor(pyObj); + Shiboken::walkThroughClassHierarchy(Py_TYPE(pyObj), &visitor); + } else { + Shiboken::ThreadStateSaver threadSaver; + threadSaver.save(); + sbkType->d->cpp_dtor(pyObj->d->cptr[0]); + } + + /* invalidate needs to be called before deleting pointer array because + it needs to delete entries for them from the BindingManager hash table; + also release wrapper explicitly if object contains C++ wrapper because + invalidate doesn't */ + invalidate(pyObj); + if (pyObj->d->validCppObject && pyObj->d->containsCppWrapper) { + BindingManager::instance().releaseWrapper(pyObj); + } + + delete[] pyObj->d->cptr; + pyObj->d->cptr = 0; + pyObj->d->validCppObject = false; +} + +bool hasOwnership(SbkObject* pyObj) +{ + return pyObj->d->hasOwnership; +} + +void getOwnership(SbkObject* self) +{ + // skip if already have the ownership + if (self->d->hasOwnership) + return; + + // skip if this object has parent + if (self->d->parentInfo && self->d->parentInfo->parent) + return; + + // Get back the ownership + self->d->hasOwnership = true; + + if (self->d->containsCppWrapper) + Py_DECREF(reinterpret_cast(self)); // Remove extra ref + else + makeValid(self); // Make the object valid again +} + +void getOwnership(PyObject* pyObj) +{ + if (pyObj) + setSequenceOwnership(pyObj, true); +} + +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)) + return; + + // remove object ownership + self->d->hasOwnership = false; + + // If We have control over object life + if (self->d->containsCppWrapper) + Py_INCREF(reinterpret_cast(self)); // keep the python object alive until the wrapper destructor call + else + invalidate(self); // If I do not know when this object will die We need to invalidate this to avoid use after +} + +void releaseOwnership(PyObject* self) +{ + setSequenceOwnership(self, false); +} + +/* Needed forward declarations */ +static void recursive_invalidate(PyObject* pyobj, std::set& seen); +static void recursive_invalidate(SbkObject* self, std::set& seen); + +void invalidate(PyObject* pyobj) +{ + std::set seen; + recursive_invalidate(pyobj, seen); +} + +void invalidate(SbkObject* self) +{ + std::set seen; + recursive_invalidate(self, seen); +} + +static void recursive_invalidate(PyObject* pyobj, std::set& seen) +{ + std::list objs = splitPyObject(pyobj); + std::list::const_iterator it = objs.begin(); + for (; it != objs.end(); it++) + recursive_invalidate(*it, seen); +} + +static void recursive_invalidate(SbkObject* self, std::set& seen) +{ + // Skip if this object not is a valid object or if it's already been seen + if (!self || reinterpret_cast(self) == Py_None || seen.find(self) != seen.end()) + return; + seen.insert(self); + + if (!self->d->containsCppWrapper) { + self->d->validCppObject = false; // Mark object as invalid only if this is not a wrapper class + BindingManager::instance().releaseWrapper(self); + } + + // If it is a parent invalidate all children. + if (self->d->parentInfo) { + // Create a copy because this list can be changed during the process + ChildrenList copy = self->d->parentInfo->children; + ChildrenList::iterator it = copy.begin(); + + for (; it != copy.end(); ++it) { + // invalidate the child + recursive_invalidate(*it, seen); + + // if the parent not is a wrapper class, then remove children from him, because We do not know when this object will be destroyed + if (!self->d->validCppObject) + removeParent(*it, true, true); + } + } + + // If has ref to other objects invalidate all + if (self->d->referredObjects) { + RefCountMap& refCountMap = *(self->d->referredObjects); + RefCountMap::iterator iter; + for (iter = refCountMap.begin(); iter != refCountMap.end(); ++iter) { + const std::list lst = iter->second; + std::list::const_iterator it = lst.begin(); + while(it != lst.end()) { + recursive_invalidate(*it, seen); + ++it; + } + } + } +} + +void makeValid(SbkObject* self) +{ + // Skip if this object not is a valid object + if (!self || reinterpret_cast(self) == Py_None || self->d->validCppObject) + return; + + // Mark object as invalid only if this is not a wrapper class + self->d->validCppObject = true; + + // If it is a parent make all children valid + if (self->d->parentInfo) { + ChildrenList::iterator it = self->d->parentInfo->children.begin(); + for (; it != self->d->parentInfo->children.end(); ++it) + makeValid(*it); + } + + // If has ref to other objects make all valid again + if (self->d->referredObjects) { + RefCountMap& refCountMap = *(self->d->referredObjects); + RefCountMap::iterator iter; + for (iter = refCountMap.begin(); iter != refCountMap.end(); ++iter) { + const std::list lst = iter->second; + std::list::const_iterator it = lst.begin(); + while(it != lst.end()) { + if (Shiboken::Object::checkType(*it)) + makeValid(reinterpret_cast(*it)); + ++it; + } + } + } +} + +bool hasParentInfo(SbkObject* pyObj) +{ + return pyObj->d->parentInfo; +} + +void* cppPointer(SbkObject* pyObj, PyTypeObject* desiredType) +{ + PyTypeObject* type = Py_TYPE(pyObj); + int idx = 0; + if (reinterpret_cast(type)->d->is_multicpp) + idx = getTypeIndexOnHierarchy(type, desiredType); + if (pyObj->d->cptr) + return pyObj->d->cptr[idx]; + return 0; +} + +std::vector cppPointers(SbkObject* pyObj) +{ + int n = getNumberOfCppBaseClasses(Py_TYPE(pyObj)); + std::vector ptrs(n); + for (int i = 0; i < n; ++i) + ptrs[i] = pyObj->d->cptr[i]; + return ptrs; +} + + +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); + + bool alreadyInitialized = sbkObj->d->cptr[idx]; + if (alreadyInitialized) + PyErr_SetString(PyExc_RuntimeError, "You can't initialize an object twice!"); + else + sbkObj->d->cptr[idx] = cptr; + + sbkObj->d->cppObjectCreated = true; + return !alreadyInitialized; +} + +bool isValid(PyObject* pyObj) +{ + if (!pyObj || pyObj == Py_None + || Py_TYPE(pyObj->ob_type) != &SbkObjectType_Type) { + 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); + return false; + } + + if (!priv->validCppObject) { + PyErr_Format(PyExc_RuntimeError, "Internal C++ object (%s) already deleted.", pyObj->ob_type->tp_name); + return false; + } + + return true; +} + +bool isValid(SbkObject* pyObj, bool throwPyError) +{ + if (!pyObj) + return false; + + 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); + return false; + } + + if (!priv->validCppObject) { + if (throwPyError) + PyErr_Format(PyExc_RuntimeError, "Internal C++ object (%s) already deleted.", Py_TYPE(pyObj)->tp_name); + return false; + } + + return true; +} + +bool isValid(PyObject* pyObj, bool throwPyError) +{ + if (!pyObj || pyObj == Py_None || + !PyType_IsSubtype(pyObj->ob_type, reinterpret_cast(&SbkObject_Type))) { + return true; + } + return isValid(reinterpret_cast(pyObj), throwPyError); +} + +SbkObject *findColocatedChild(SbkObject *wrapper, + const SbkObjectType *instanceType) +{ + // Degenerate case, wrapper is the correct wrapper. + if (reinterpret_cast(Py_TYPE(wrapper)) == reinterpret_cast(instanceType)) + return wrapper; + + if (!(wrapper->d && wrapper->d->cptr)) + return 0; + + ParentInfo* pInfo = wrapper->d->parentInfo; + if (!pInfo) + return 0; + + ChildrenList& children = pInfo->children; + + ChildrenList::iterator childrenEnd = children.end(); + for (ChildrenList::iterator iChild = children.begin(); iChild != childrenEnd; ++iChild) { + if (!((*iChild)->d && (*iChild)->d->cptr)) + continue; + if ((*iChild)->d->cptr[0] == wrapper->d->cptr[0]) { + if (reinterpret_cast(Py_TYPE(*iChild)) == reinterpret_cast(instanceType)) + return const_cast((*iChild)); + else + return findColocatedChild(const_cast(*iChild), instanceType); + } + } + return 0; +} + + +PyObject *newObject(SbkObjectType* instanceType, + void* cptr, + bool hasOwnership, + bool isExactType, + const char* typeName) +{ + // Try to find the exact type of cptr. + if (!isExactType) { + PyTypeObject* exactType = 0; + if (typeName) { + exactType = Shiboken::Conversions::getPythonTypeObject(typeName); + if (exactType) + instanceType = reinterpret_cast(exactType); + } + if (!exactType) + instanceType = BindingManager::instance().resolveType(&cptr, instanceType); + } + + bool shouldCreate = true; + bool shouldRegister = true; + SbkObject* self = 0; + + // Some logic to ensure that colocated child field does not overwrite the parent + if (BindingManager::instance().hasWrapper(cptr)) { + SbkObject* existingWrapper = BindingManager::instance().retrieveWrapper(cptr); + + self = findColocatedChild(existingWrapper, instanceType); + if (self) { + // Wrapper already registered for cptr. + // This should not ideally happen, binding code should know when a wrapper + // already exists and retrieve it instead. + shouldRegister = shouldCreate = false; + } else if (hasOwnership && + (!(Shiboken::Object::hasCppWrapper(existingWrapper) || + Shiboken::Object::hasOwnership(existingWrapper)))) { + // Old wrapper is likely junk, since we have ownership and it doesn't. + BindingManager::instance().releaseWrapper(existingWrapper); + } else { + // Old wrapper may be junk caused by some bug in identifying object deletion + // but it may not be junk when a colocated field is accessed for an + // object which was not created by python (returned from c++ factory function). + // Hence we cannot release the wrapper confidently so we do not register. + shouldRegister = false; + } + } + + if (shouldCreate) { + self = reinterpret_cast(SbkObjectTpNew(reinterpret_cast(instanceType), 0, 0)); + self->d->cptr[0] = cptr; + self->d->hasOwnership = hasOwnership; + self->d->validCppObject = 1; + if (shouldRegister) { + BindingManager::instance().registerWrapper(self, cptr); + } + } else { + Py_IncRef(reinterpret_cast(self)); + } + return reinterpret_cast(self); +} + +void destroy(SbkObject* self) +{ + destroy(self, 0); +} + +void destroy(SbkObject* self, void* cppData) +{ + // Skip if this is called with NULL pointer this can happen in derived classes + if (!self) + return; + + // This can be called in c++ side + Shiboken::GilState gil; + + // Remove all references attached to this object + clearReferences(self); + + // Remove the object from parent control + + // Verify if this object has parent + bool hasParent = (self->d->parentInfo && self->d->parentInfo->parent); + + if (self->d->parentInfo) { + // Check for children information and make all invalid if they exists + _destroyParentInfo(self, true); + // If this object has parent then the pyobject can be invalid now, because we remove the last ref after remove from parent + } + + //if !hasParent this object could still alive + if (!hasParent && self->d->containsCppWrapper && !self->d->hasOwnership) { + // Remove extra ref used by c++ object this will case the pyobject destruction + // This can cause the object death + Py_DECREF(reinterpret_cast(self)); + } + + //Python Object is not destroyed yet + if (cppData && Shiboken::BindingManager::instance().hasWrapper(cppData)) { + // Remove from BindingManager + Shiboken::BindingManager::instance().releaseWrapper(self); + self->d->hasOwnership = false; + + // the cpp object instance was deleted + delete[] self->d->cptr; + self->d->cptr = 0; + } + + // After this point the object can be death do not use the self pointer bellow +} + +void removeParent(SbkObject* child, bool giveOwnershipBack, bool keepReference) +{ + ParentInfo* pInfo = child->d->parentInfo; + if (!pInfo || !pInfo->parent) { + if (pInfo && pInfo->hasWrapperRef) { + pInfo->hasWrapperRef = false; + } + return; + } + + ChildrenList& oldBrothers = pInfo->parent->d->parentInfo->children; + // Verify if this child is part of parent list + ChildrenList::iterator iChild = std::find(oldBrothers.begin(), oldBrothers.end(), child); + if (iChild == oldBrothers.end()) + return; + + oldBrothers.erase(iChild); + + pInfo->parent = 0; + + // This will keep the wrapper reference, will wait for wrapper destruction to remove that + if (keepReference && + child->d->containsCppWrapper) { + //If have already a extra ref remove this one + if (pInfo->hasWrapperRef) + Py_DECREF(child); + else + pInfo->hasWrapperRef = true; + return; + } + + // Transfer ownership back to Python + child->d->hasOwnership = giveOwnershipBack; + + // Remove parent ref + Py_DECREF(child); +} + +void setParent(PyObject* parent, PyObject* child) +{ + if (!child || child == Py_None || child == parent) + return; + + /* + * setParent is recursive when the child is a native Python sequence, i.e. objects not binded by Shiboken + * like tuple and list. + * + * This "limitation" exists to fix the following problem: A class multiple inherits QObject and QString, + * so if you pass this class to someone that takes the ownership, we CAN'T enter in this if, but hey! QString + * follows the sequence protocol. + */ + if (PySequence_Check(child) && !Object::checkType(child)) { + Shiboken::AutoDecRef seq(PySequence_Fast(child, 0)); + for (Py_ssize_t i = 0, max = PySequence_Size(seq); i < max; ++i) + setParent(parent, PySequence_Fast_GET_ITEM(seq.object(), i)); + return; + } + + bool parentIsNull = !parent || parent == Py_None; + SbkObject* parent_ = reinterpret_cast(parent); + SbkObject* child_ = reinterpret_cast(child); + + if (!parentIsNull) { + if (!parent_->d->parentInfo) + parent_->d->parentInfo = new ParentInfo; + + // do not re-add a child + if (child_->d->parentInfo && (child_->d->parentInfo->parent == parent_)) + return; + } + + ParentInfo* pInfo = child_->d->parentInfo; + bool hasAnotherParent = pInfo && pInfo->parent && pInfo->parent != parent_; + + //Avoid destroy child during reparent operation + Py_INCREF(child); + + // check if we need to remove this child from the old parent + if (parentIsNull || hasAnotherParent) + removeParent(child_); + + // Add the child to the new parent + pInfo = child_->d->parentInfo; + if (!parentIsNull) { + if (!pInfo) + pInfo = child_->d->parentInfo = new ParentInfo; + + pInfo->parent = parent_; + parent_->d->parentInfo->children.insert(child_); + + // Add Parent ref + Py_INCREF(child_); + + // Remove ownership + child_->d->hasOwnership = false; + } + + // Remove previous safe ref + Py_DECREF(child); +} + +void deallocData(SbkObject* self, bool cleanup) +{ + // Make cleanup if this is not a wrapper otherwise this will be done on wrapper destructor + if(cleanup) { + removeParent(self); + + if (self->d->parentInfo) + _destroyParentInfo(self, true); + + clearReferences(self); + } + + if (self->d->cptr) { + // Remove from BindingManager + Shiboken::BindingManager::instance().releaseWrapper(self); + delete[] self->d->cptr; + self->d->cptr = 0; + // delete self->d; PYSIDE-205: wrong! + } + delete self->d; // PYSIDE-205: always delete d. + Py_XDECREF(self->ob_dict); + 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); + + ob_type->d->d_func = d_func; + ob_type->d->user_data = userData; +} + +void* getTypeUserData(SbkObject* wrapper) +{ + return reinterpret_cast(Py_TYPE(wrapper))->d->user_data; +} + +void keepReference(SbkObject* self, const char* key, PyObject* referredObject, bool append) +{ + bool isNone = (!referredObject || (referredObject == Py_None)); + + if (!self->d->referredObjects) + self->d->referredObjects = new Shiboken::RefCountMap; + + RefCountMap& refCountMap = *(self->d->referredObjects); + RefCountMap::iterator iter = refCountMap.find(key); + std::list objects; + if (iter != refCountMap.end()) { + objects = (*iter).second; + std::list::const_iterator found = std::find(objects.begin(), objects.end(), referredObject); + + // skip if objects already exists + if (found != objects.end()) + return; + } + + if (append && !isNone) { + refCountMap[key].push_back(referredObject); + Py_INCREF(referredObject); + } else if (!append) { + if (objects.size() > 0) + decRefPyObjectList(objects, isNone ? 0 : referredObject); + if (isNone) { + if (iter != refCountMap.end()) + refCountMap.erase(iter); + } else { + objects.clear(); + objects.push_back(referredObject); + refCountMap[key] = objects; + Py_INCREF(referredObject); + } + } +} + +void removeReference(SbkObject* self, const char* key, PyObject* referredObject) +{ + if (!referredObject || (referredObject == Py_None)) + return; + + if (!self->d->referredObjects) + return; + + RefCountMap& refCountMap = *(self->d->referredObjects); + RefCountMap::iterator iter = refCountMap.find(key); + if (iter != refCountMap.end()) { + decRefPyObjectList(iter->second); + refCountMap.erase(iter); + } +} + +void clearReferences(SbkObject* self) +{ + if (!self->d->referredObjects) + return; + + RefCountMap& refCountMap = *(self->d->referredObjects); + RefCountMap::iterator iter; + for (iter = refCountMap.begin(); iter != refCountMap.end(); ++iter) + decRefPyObjectList(iter->second); + self->d->referredObjects->clear(); +} + +std::string info(SbkObject* self) +{ + std::ostringstream s; + std::list bases; + + if (self->d && self->d->cptr) { + if (ObjectType::isUserType(Py_TYPE(self))) + bases = getCppBaseClasses(Py_TYPE(self)); + else + bases.push_back(reinterpret_cast(Py_TYPE(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 << "\n"; + } + else { + s << "C++ address....... <>\n"; + } + + s << "hasOwnership...... " << bool(self->d->hasOwnership) << "\n" + "containsCppWrapper " << self->d->containsCppWrapper << "\n" + "validCppObject.... " << self->d->validCppObject << "\n" + "wasCreatedByPython " << self->d->cppObjectCreated << "\n"; + + + if (self->d->parentInfo && self->d->parentInfo->parent) { + s << "parent............ "; + Shiboken::AutoDecRef parent(PyObject_Str(reinterpret_cast(self->d->parentInfo->parent))); + s << String::toCString(parent) << "\n"; + } + + if (self->d->parentInfo && self->d->parentInfo->children.size()) { + s << "children.......... "; + ChildrenList& children = self->d->parentInfo->children; + for (ChildrenList::const_iterator it = children.begin(); it != children.end(); ++it) { + Shiboken::AutoDecRef child(PyObject_Str(reinterpret_cast(*it))); + s << String::toCString(child) << ' '; + } + s << '\n'; + } + + if (self->d->referredObjects && self->d->referredObjects->size()) { + Shiboken::RefCountMap& map = *self->d->referredObjects; + s << "referred objects.. "; + Shiboken::RefCountMap::const_iterator it = map.begin(); + for (; it != map.end(); ++it) { + if (it != map.begin()) + s << " "; + s << '"' << it->first << "\" => "; + std::list::const_iterator j = it->second.begin(); + for (; j != it->second.end(); ++j) { + Shiboken::AutoDecRef obj(PyObject_Str(*j)); + s << String::toCString(obj) << ' '; + } + s << ' '; + } + s << '\n'; + } + return s.str(); +} + +} // namespace Object + +} // namespace Shiboken diff --git a/sources/shiboken2/libshiboken/basewrapper.h b/sources/shiboken2/libshiboken/basewrapper.h new file mode 100644 index 000000000..002337f3c --- /dev/null +++ b/sources/shiboken2/libshiboken/basewrapper.h @@ -0,0 +1,451 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef BASEWRAPPER_H +#define BASEWRAPPER_H + +#include "sbkpython.h" +#include "shibokenmacros.h" + +#include +#include + +extern "C" +{ + +struct SbkConverter; +struct SbkObjectPrivate; + +/// Base Python object for all the wrapped C++ classes. +struct LIBSHIBOKEN_API SbkObject +{ + PyObject_HEAD + /// Instance dictionary. + PyObject* ob_dict; + /// List of weak references + PyObject* weakreflist; + SbkObjectPrivate* d; +}; + + +/// Dealloc the python object \p pyObj and the C++ object represented by it. +LIBSHIBOKEN_API void SbkDeallocWrapper(PyObject* pyObj); +LIBSHIBOKEN_API void SbkDeallocWrapperWithPrivateDtor(PyObject* self); + +struct SbkObjectType; + +/// Function signature for the multiple inheritance information initializers that should be provided by classes with multiple inheritance. +typedef int* (*MultipleInheritanceInitFunction)(const void*); + +/** + * Special cast function is used to correctly cast an object when it's + * part of a multiple inheritance hierarchy. + * The implementation of this function is auto generated by the generator and you don't need to care about it. + */ +typedef void* (*SpecialCastFunction)(void*, SbkObjectType*); +typedef SbkObjectType* (*TypeDiscoveryFunc)(void*, SbkObjectType*); +typedef void* (*TypeDiscoveryFuncV2)(void*, SbkObjectType*); + +typedef void* (*ExtendedToCppFunc)(PyObject*); // DEPRECATED. +typedef bool (*ExtendedIsConvertibleFunc)(PyObject*); // DEPRECATED. + +// Used in userdata dealloc function +typedef void (*DeleteUserDataFunc)(void*); + +typedef void (*ObjectDestructor)(void*); + +typedef void (*SubTypeInitHook)(SbkObjectType*, PyObject*, PyObject*); + +extern LIBSHIBOKEN_API PyTypeObject SbkObjectType_Type; +extern LIBSHIBOKEN_API SbkObjectType SbkObject_Type; + + +struct SbkObjectTypePrivate; +/// PyTypeObject extended with C++ multiple inheritance information. +struct LIBSHIBOKEN_API SbkObjectType +{ + PyHeapTypeObject super; + SbkObjectTypePrivate* d; +}; + +LIBSHIBOKEN_API PyObject* SbkObjectTpNew(PyTypeObject* subtype, PyObject*, PyObject*); + +} // extern "C" + +namespace Shiboken +{ + +/** +* Init shiboken library. +*/ +LIBSHIBOKEN_API void init(); + + +/// Delete the class T allocated on \p cptr. +template +void callCppDestructor(void* cptr) +{ + delete reinterpret_cast(cptr); +} + +/** + * Shiboken::importModule is DEPRECATED. Use Shiboken::Module::import() instead. + */ +SBK_DEPRECATED(LIBSHIBOKEN_API bool importModule(const char* moduleName, PyTypeObject*** cppApiPtr)); +LIBSHIBOKEN_API void setErrorAboutWrongArguments(PyObject* args, const char* funcName, const char** cppOverloads); + +namespace ObjectType { + +/** +* Returns true if the object is an instance of a type created by the Shiboken generator. +*/ +LIBSHIBOKEN_API bool checkType(PyTypeObject* pyObj); + +/** +* Returns true if this object is an instance of an user defined type derived from an Shiboken type. +*/ +LIBSHIBOKEN_API bool isUserType(PyTypeObject* pyObj); + +/** +* Returns true if the constructor of \p ctorType can be called for a instance of type \p myType. +* \note This function set a python error when returning false. +*/ +LIBSHIBOKEN_API bool canCallConstructor(PyTypeObject* myType, PyTypeObject* ctorType); + + +LIBSHIBOKEN_API bool hasExternalCppConversions(SbkObjectType*); // DEPRECATED. +LIBSHIBOKEN_API bool isExternalConvertible(SbkObjectType*, PyObject*); // DEPRECATED. +LIBSHIBOKEN_API void setExternalCppConversionFunction(SbkObjectType*, ExtendedToCppFunc); // DEPRECATED. +LIBSHIBOKEN_API void setExternalIsConvertibleFunction(SbkObjectType*, ExtendedIsConvertibleFunc); // DEPRECATED. +LIBSHIBOKEN_API void* callExternalCppConversion(SbkObjectType*, PyObject*); // DEPRECATED. + + +/** + * Tells if the \p type represents an object of a class with multiple inheritance in C++. + * When this occurs, the C++ pointer held by the Python wrapper will need to be cast when + * passed as a parameter that expects a type of its ancestry. + * \returns true if a call to ObjectType::cast() is needed to obtain the correct + * C++ pointer for Python objects of type \p type. + */ +LIBSHIBOKEN_API bool hasCast(SbkObjectType* type); +/** + * Cast the C++ pointer held by a Python object \p obj of type \p sourceType, + * to a C++ pointer of a C++ class indicated by type \p targetType. + * \returns The cast C++ pointer. + */ +LIBSHIBOKEN_API void* cast(SbkObjectType* sourceType, SbkObject* obj, PyTypeObject* targetType); +/// Set the C++ cast function for \p type. +LIBSHIBOKEN_API void setCastFunction(SbkObjectType* type, SpecialCastFunction func); + +LIBSHIBOKEN_API void setOriginalName(SbkObjectType* self, const char* name); +LIBSHIBOKEN_API const char* getOriginalName(SbkObjectType* self); + +LIBSHIBOKEN_API void setTypeDiscoveryFunctionV2(SbkObjectType* self, TypeDiscoveryFuncV2 func); +LIBSHIBOKEN_API SBK_DEPRECATED(void setTypeDiscoveryFunction(SbkObjectType* self, TypeDiscoveryFunc func)); +LIBSHIBOKEN_API SBK_DEPRECATED(TypeDiscoveryFunc getTypeDiscoveryFunction(SbkObjectType* self)); + +LIBSHIBOKEN_API void copyMultimpleheritance(SbkObjectType* self, SbkObjectType* other); +LIBSHIBOKEN_API void setMultipleIheritanceFunction(SbkObjectType* self, MultipleInheritanceInitFunction func); +LIBSHIBOKEN_API MultipleInheritanceInitFunction getMultipleIheritanceFunction(SbkObjectType* self); + +LIBSHIBOKEN_API void setDestructorFunction(SbkObjectType* self, ObjectDestructor func); + +LIBSHIBOKEN_API void initPrivateData(SbkObjectType* self); + +/** + * Initializes a Shiboken wrapper type and adds it to the module, + * or to the enclosing class if the type is an inner class. + * This function also calls initPrivateData and setDestructorFunction. + * \param enclosingObject The module or enclosing class to where the new \p type will be added. + * \param typeName Name by which the type will be known in Python. + * \param originalName Original C++ name of the type. + * \param type The new type to be initialized and added to the module. + * \param cppObjDtor Memory deallocation function for the C++ object held by \p type. + * Should not be used if the underlying C++ class has a private destructor. + * \param baseType Base type from whom the new \p type inherits. + * \param baseTypes Other base types from whom the new \p type inherits. + * \param isInnerClass Tells if the new \p type is an inner class (the default is that it isn't). + * If false then the \p enclosingObject is a module, otherwise it is another + * 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, ObjectDestructor cppObjDtor = 0, + SbkObjectType* baseType = 0, PyObject* baseTypes = 0, + bool isInnerClass = false); + +/** + * Set the subtype init hook for a type. + * + * This hook will be invoked every time the user creates a sub-type inherited from a Shiboken based type. + * The hook gets 3 params, they are: The new type being created, args and kwds. The last two are the very + * same got from tp_new. + */ +LIBSHIBOKEN_API void setSubTypeInitHook(SbkObjectType* self, SubTypeInitHook func); + +/** + * Get the user data previously set by Shiboken::Object::setTypeUserData + */ +LIBSHIBOKEN_API void* getTypeUserData(SbkObjectType* self); +LIBSHIBOKEN_API void setTypeUserData(SbkObjectType* self, void* userData, DeleteUserDataFunc d_func); + +} + +namespace Object { + +/** + * Returns a string with information about the internal state of the instance object, useful for debug purposes. + */ +LIBSHIBOKEN_API std::string info(SbkObject* self); + +/** +* Returns true if the object is an instance of a type created by the Shiboken generator. +*/ +LIBSHIBOKEN_API bool checkType(PyObject* pyObj); + +/** + * Returns true if this object type is an instance of an user defined type derived from an Shiboken type. + * \see Shiboken::ObjectType::isUserType + */ +LIBSHIBOKEN_API bool isUserType(PyObject* pyObj); + +/** + * Generic function used to make ObjectType hashable, the C++ pointer is used as hash value. + */ +LIBSHIBOKEN_API Py_hash_t hash(PyObject* pyObj); + +/** + * Find a child of given wrapper having same address having the specified type. + */ +LIBSHIBOKEN_API SbkObject *findColocatedChild(SbkObject *wrapper, + const SbkObjectType *instanceType); + +/** + * Bind a C++ object to Python. + * \param instanceType equivalent Python type for the C++ object. + * \param hasOwnership if true, Python will try to delete the underlying C++ object when there's no more refs. + * \param isExactType if false, Shiboken will use some heuristics to detect the correct Python type of this C++ + * object, in any case you must provide \p instanceType, it'll be used as search starting point + * and as fallback. + * \param typeName If non-null, this will be used as helper to find the correct Python type for this object. + */ +LIBSHIBOKEN_API PyObject* newObject(SbkObjectType* instanceType, + void* cptr, + bool hasOwnership = true, + bool isExactType = false, + const char* typeName = 0); + +/** + * Changes the valid flag of a PyObject, invalid objects will raise an exception when someone tries to access it. + */ +LIBSHIBOKEN_API void setValidCpp(SbkObject* pyObj, bool value); +/** + * Tells shiboken the Python object \p pyObj has a C++ wrapper used to intercept virtual method calls. + */ +LIBSHIBOKEN_API void setHasCppWrapper(SbkObject* pyObj, bool value); +/** + * Return true if the Python object \p pyObj has a C++ wrapper used to intercept virtual method calls. + */ +LIBSHIBOKEN_API bool hasCppWrapper(SbkObject* pyObj); + +/** + * Return true if the Python object was created by Python, false otherwise. + * \note This function was added to libshiboken only to be used by shiboken.wasCreatedByPython() + */ +LIBSHIBOKEN_API bool wasCreatedByPython(SbkObject* pyObj); + +/** + * Call the C++ object destructor and invalidates the Python object. + * \note This function was added to libshiboken only to be used by shiboken.delete() + */ +LIBSHIBOKEN_API void callCppDestructors(SbkObject* pyObj); + +/** + * Return true if the Python is responsible for deleting the underlying C++ object. + */ +LIBSHIBOKEN_API bool hasOwnership(SbkObject* pyObj); + +/** + * Sets python as responsible to delete the underlying C++ object. + * \note You this overload only when the PyObject can be a sequence and you want to + * call this function for every item in the sequence. + * \see getOwnership(SbkObject*) + */ +LIBSHIBOKEN_API void getOwnership(PyObject* pyObj); + +/** + * Sets python as responsible to delete the underlying C++ object. + */ +LIBSHIBOKEN_API void getOwnership(SbkObject* pyObj); + +/** + * Release the ownership, so Python will not delete the underlying C++ object. + * \note You this overload only when the PyObject can be a sequence and you want to + * call this function for every item in the sequence. + * \see releaseOwnership(SbkObject*) + */ +LIBSHIBOKEN_API void releaseOwnership(PyObject* pyObj); +/** + * Release the ownership, so Python will not delete the underlying C++ object. + */ +LIBSHIBOKEN_API void releaseOwnership(SbkObject* pyObj); + +/** + * Returns true if the pyObj holds information about their parents. + */ +LIBSHIBOKEN_API bool hasParentInfo(SbkObject* pyObj); + +/** + * Get the C++ pointer of type \p desiredType from a Python object. + */ +LIBSHIBOKEN_API void* cppPointer(SbkObject* pyObj, PyTypeObject* desiredType); + +/** + * Return a list with all C++ pointers held from a Python object. + * \note This function was added to libshiboken only to be used by shiboken.getCppPointer() + */ +LIBSHIBOKEN_API std::vector cppPointers(SbkObject* pyObj); + +/** + * Set the C++ pointer of type \p desiredType of a Python object. + */ +LIBSHIBOKEN_API bool setCppPointer(SbkObject* sbkObj, PyTypeObject* desiredType, void* cptr); + +/** + * Returns false and sets a Python RuntimeError if the Python wrapper is not marked as valid. + */ +LIBSHIBOKEN_API bool isValid(PyObject* pyObj); + +/** + * Returns false if the Python wrapper is not marked as valid. + * \param pyObj the object. + * \param throwPyError sets a Python RuntimeError when the object isn't valid. + */ +LIBSHIBOKEN_API bool isValid(SbkObject* pyObj, bool throwPyError = true); + +/** + * Returns false if the Python wrapper is not marked as valid. + * \param pyObj the object. + * \param throwPyError sets a Python RuntimeError when the object isn't valid. + */ +LIBSHIBOKEN_API bool isValid(PyObject* pyObj, bool throwPyError); + +/** +* Set the parent of \p child to \p parent. +* When an object dies, all their children, grandchildren, etc, are tagged as invalid. +* \param parent the parent object, if null, the child will have no parents. +* \param child the child. +*/ +LIBSHIBOKEN_API void setParent(PyObject* parent, PyObject* child); + +/** +* Remove this child from their parent, if any. +* \param child the child. +*/ +LIBSHIBOKEN_API void removeParent(SbkObject* child, bool giveOwnershipBack = true, bool keepReferenc = false); + +/** +* \internal This is an internal function called by tp_dealloc, it's exported just for technical reasons. +* \note Do not call this function inside your bindings. +*/ +LIBSHIBOKEN_API void destroyParentInfo(SbkObject* obj, bool removeFromParent = true); + +/** + * Mark the object as invalid + */ +LIBSHIBOKEN_API void invalidate(SbkObject* self); + +/** + * Help function can be used to invalidate a sequence of object + **/ +LIBSHIBOKEN_API void invalidate(PyObject* pyobj); + +/** + * Make the object valid again + */ +LIBSHIBOKEN_API void makeValid(SbkObject* self); + +/// \deprecated Use destroy(SbkObject*, void*) +SBK_DEPRECATED(LIBSHIBOKEN_API void destroy(SbkObject* self)); + +/** + * Destroy any data in Shiboken structure and c++ pointer if the pyboject has the ownership + */ +LIBSHIBOKEN_API void destroy(SbkObject* self, void* cppData); + +/** + * Set user data on type of \p wrapper. + * \param wrapper instance object, the user data will be set on his type + * \param userData the user data + * \param d_func a function used to delete the user data + */ +LIBSHIBOKEN_API void setTypeUserData(SbkObject* wrapper, void* userData, DeleteUserDataFunc d_func); +/** + * Get the user data previously set by Shiboken::Object::setTypeUserData + */ +LIBSHIBOKEN_API void* getTypeUserData(SbkObject* wrapper); + +/** + * Increments the reference count of the referred Python object. + * A previous Python object in the same position identified by the 'key' parameter + * will have its reference counter decremented automatically when replaced. + * All the kept references should be decremented when the Python wrapper indicated by + * 'self' dies. + * No checking is done for any of the passed arguments, since it is meant to be used + * by generated code it is supposed that the generator is correct. + * \param self the wrapper instance that keeps references to other objects. + * \param key a key that identifies the C++ method signature and argument where the referred Object came from. + * \param referredObject the object whose reference is used by the self object. + */ +LIBSHIBOKEN_API void keepReference(SbkObject* self, const char* key, PyObject* referredObject, bool append = false); + +/** + * Removes any reference previously added by keepReference function + * \param self the wrapper instance that keeps references to other objects. + * \param key a key that identifies the C++ method signature and argument from where the referred Object came. + * \param referredObject the object whose reference is used by the self object. + */ +LIBSHIBOKEN_API void removeReference(SbkObject* self, const char* key, PyObject* referredObject); + +} // namespace Object + +} // namespace Shiboken + +#endif // BASEWRAPPER_H diff --git a/sources/shiboken2/libshiboken/basewrapper_p.h b/sources/shiboken2/libshiboken/basewrapper_p.h new file mode 100644 index 000000000..129322246 --- /dev/null +++ b/sources/shiboken2/libshiboken/basewrapper_p.h @@ -0,0 +1,287 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef BASEWRAPPER_P_H +#define BASEWRAPPER_P_H + +#include "sbkpython.h" +#include "basewrapper.h" + +#include +#include +#include +#include + +struct SbkObject; +struct SbkObjectType; +struct SbkConverter; + +namespace Shiboken +{ +/** + * This mapping associates a method and argument of an wrapper object with the wrapper of + * said argument when it needs the binding to help manage its reference count. + */ +typedef std::map > RefCountMap; + +/// Linked list of SbkBaseWrapper pointers +typedef std::set ChildrenList; + +/// Structure used to store information about object parent and children. +struct ParentInfo +{ + /// Default ctor. + ParentInfo() : parent(0), hasWrapperRef(false) {} + /// Pointer to parent object. + SbkObject* parent; + /// List of object children. + ChildrenList children; + /// has internal ref + bool hasWrapperRef; +}; + +} // namespace Shiboken + +extern "C" +{ + +/** + * \internal + * Private data for SbkBaseWrapper + */ +struct SbkObjectPrivate +{ + /// Pointer to the C++ class. + void** cptr; + /// True when Python is responsible for freeing the used memory. + unsigned int hasOwnership : 1; + /// This is true when the C++ class of the wrapped object has a virtual destructor AND was created by Python. + unsigned int containsCppWrapper : 1; + /// Marked as false when the object is lost to C++ and the binding can not know if it was deleted or not. + unsigned int validCppObject : 1; + /// Marked as true when the object constructor was called + unsigned int cppObjectCreated : 1; + /// Information about the object parents and children, may be null. + Shiboken::ParentInfo* parentInfo; + /// Manage reference count of objects that are referred to but not owned from. + Shiboken::RefCountMap* referredObjects; + + ~SbkObjectPrivate() + { + delete parentInfo; + parentInfo = 0; + delete referredObjects; + referredObjects = 0; + } +}; + +// TODO-CONVERTERS: to be deprecated/removed +/// The type behaviour was not defined yet +#define BEHAVIOUR_UNDEFINED 0 +/// The type is a value type +#define BEHAVIOUR_VALUETYPE 1 +/// The type is an object type +#define BEHAVIOUR_OBJECTTYPE 2 + +struct SbkObjectTypePrivate +{ + SbkConverter* converter; + int* mi_offsets; + MultipleInheritanceInitFunction mi_init; + + /// Special cast function, null if this class doesn't have multiple inheritance. + SpecialCastFunction mi_specialcast; + TypeDiscoveryFuncV2 type_discovery; + /// Pointer to a function responsible for deletion of the C++ instance calling the proper destructor. + ObjectDestructor cpp_dtor; + /// True if this type holds two or more C++ instances, e.g.: a Python class which inherits from two C++ classes. + int is_multicpp : 1; + /// True if this type was defined by the user. + int is_user_type : 1; + /// Tells is the type is a value type or an object-type, see BEHAVIOUR_* constants. + // TODO-CONVERTERS: to be deprecated/removed + int type_behaviour : 2; + /// C++ name + char* original_name; + /// Type user data + void* user_data; + DeleteUserDataFunc d_func; + void (*subtype_init)(SbkObjectType*, PyObject*, PyObject*); +}; + + +} // extern "C" + +namespace Shiboken +{ +/** + * Utility function used to transform a PyObject that implements sequence protocol into a std::list. + **/ +std::list splitPyObject(PyObject* pyObj); + +/** +* Visitor class used by walkOnClassHierarchy function. +*/ +class HierarchyVisitor +{ +public: + HierarchyVisitor() : m_wasFinished(false) {} + virtual ~HierarchyVisitor() {} + virtual void visit(SbkObjectType* node) = 0; + virtual void done() {} + void finish() { m_wasFinished = true; }; + bool wasFinished() const { return m_wasFinished; } +private: + bool m_wasFinished; +}; + +class BaseCountVisitor : public HierarchyVisitor +{ +public: + BaseCountVisitor() : m_count(0) {} + + void visit(SbkObjectType*) + { + m_count++; + } + + int count() const { return m_count; } +private: + int m_count; +}; + +class BaseAccumulatorVisitor : public HierarchyVisitor +{ +public: + BaseAccumulatorVisitor() {} + + void visit(SbkObjectType* node) + { + m_bases.push_back(node); + } + + std::list bases() const { return m_bases; } +private: + std::list m_bases; +}; + +class GetIndexVisitor : public HierarchyVisitor +{ +public: + GetIndexVisitor(PyTypeObject* desiredType) : m_index(-1), m_desiredType(desiredType) {} + virtual void visit(SbkObjectType* node) + { + m_index++; + if (PyType_IsSubtype(reinterpret_cast(node), m_desiredType)) + finish(); + } + int index() const { return m_index; } + +private: + int m_index; + PyTypeObject* m_desiredType; +}; + +/// Call the destructor of each C++ object held by a Python object +class DtorCallerVisitor : public HierarchyVisitor +{ +public: + DtorCallerVisitor(SbkObject* pyObj) : m_pyObj(pyObj) {} + void visit(SbkObjectType* node); + void done(); +protected: + std::list > m_ptrs; + SbkObject* m_pyObj; +}; + +/// Dealloc of each C++ object held by a Python object, this implies a call to the C++ object destructor +class DeallocVisitor : public DtorCallerVisitor +{ +public: + DeallocVisitor(SbkObject* pyObj) : DtorCallerVisitor(pyObj) {} + void done(); +}; + +/// \internal Internal function used to walk on classes inheritance trees. +/** +* Walk on class hierarchy using a DFS algorithm. +* For each pure Shiboken type found, HiearchyVisitor::visit is called and the algorithm consider +* all children of this type as visited. +*/ +void walkThroughClassHierarchy(PyTypeObject* currentType, HierarchyVisitor* visitor); + +inline int getTypeIndexOnHierarchy(PyTypeObject* baseType, PyTypeObject* desiredType) +{ + GetIndexVisitor visitor(desiredType); + walkThroughClassHierarchy(baseType, &visitor); + return visitor.index(); +} + +inline int getNumberOfCppBaseClasses(PyTypeObject* baseType) +{ + BaseCountVisitor visitor; + walkThroughClassHierarchy(baseType, &visitor); + return visitor.count(); +} + +inline std::list getCppBaseClasses(PyTypeObject* baseType) +{ + BaseAccumulatorVisitor visitor; + walkThroughClassHierarchy(baseType, &visitor); + return visitor.bases(); +} + +namespace Object +{ +/** +* Decrements the reference counters of every object referred by self. +* \param self the wrapper instance that keeps references to other objects. +*/ +void clearReferences(SbkObject* self); + +/** + * Destroy internal data + **/ +void deallocData(SbkObject* self, bool doCleanup); + +} // namespace Object + +} // namespace Shiboken + +#endif diff --git a/sources/shiboken2/libshiboken/bindingmanager.cpp b/sources/shiboken2/libshiboken/bindingmanager.cpp new file mode 100644 index 000000000..d7e122cd7 --- /dev/null +++ b/sources/shiboken2/libshiboken/bindingmanager.cpp @@ -0,0 +1,344 @@ +/**************************************************************************** +** +** 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 "basewrapper.h" +#include "basewrapper_p.h" +#include "bindingmanager.h" +#include "google/dense_hash_map" +#include "sbkdbg.h" +#include "gilstate.h" +#include "sbkstring.h" +#include "debugfreehook.h" + +#include +#include + +namespace Shiboken +{ + +typedef google::dense_hash_map WrapperMap; + +class Graph +{ +public: + typedef std::list NodeList; + typedef google::dense_hash_map Edges; + + Edges m_edges; + + Graph() + { + m_edges.set_empty_key(0); + } + + void addEdge(SbkObjectType* from, SbkObjectType* to) + { + m_edges[from].push_back(to); + } + +#ifndef NDEBUG + void dumpDotGraph() + { + std::ofstream file("/tmp/shiboken_graph.dot"); + + file << "digraph D {\n"; + + Edges::const_iterator i = m_edges.begin(); + for (; i != m_edges.end(); ++i) { + 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"; + } + file << "}\n"; + } +#endif + + SbkObjectType* identifyType(void** cptr, SbkObjectType* type, SbkObjectType* baseType) const + { + Edges::const_iterator edgesIt = m_edges.find(type); + if (edgesIt != m_edges.end()) { + const NodeList& adjNodes = m_edges.find(type)->second; + NodeList::const_iterator i = adjNodes.begin(); + for (; i != adjNodes.end(); ++i) { + SbkObjectType* newType = identifyType(cptr, *i, baseType); + if (newType) + return newType; + } + } + void* typeFound = ((type->d && type->d->type_discovery) ? type->d->type_discovery(*cptr, baseType) : 0); + 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 + // a possible variation of the C++ instance pointer (*cptr). + if (typeFound != type) + *cptr = typeFound; + return type; + } else { + return 0; + } + } +}; + + +#ifndef NDEBUG +static void showWrapperMap(const WrapperMap& wrapperMap) +{ + if (Py_VerboseFlag > 0) { + fprintf(stderr, "-------------------------------\n"); + fprintf(stderr, "WrapperMap: %p (size: %d)\n", &wrapperMap, (int) wrapperMap.size()); + WrapperMap::const_iterator iter; + for (iter = wrapperMap.begin(); iter != wrapperMap.end(); ++iter) { + 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, + int(reinterpret_cast(sbkObj)->ob_refcnt)); + } + fprintf(stderr, "-------------------------------\n"); + } +} +#endif + +struct BindingManager::BindingManagerPrivate { + WrapperMap wrapperMapper; + Graph classHierarchy; + bool destroying; + + BindingManagerPrivate() : destroying(false) {} + bool releaseWrapper(void* cptr, SbkObject* wrapper); + void assignWrapper(SbkObject* wrapper, const void* cptr); + +}; + +bool BindingManager::BindingManagerPrivate::releaseWrapper(void* cptr, SbkObject* wrapper) +{ + // The wrapper argument is checked to ensure that the correct wrapper is released. + // Returns true if the correct wrapper is found and released. + // If wrapper argument is NULL, no such check is performed. + WrapperMap::iterator iter = wrapperMapper.find(cptr); + if (iter != wrapperMapper.end() && (wrapper == 0 || iter->second == wrapper)) { + wrapperMapper.erase(iter); + return true; + } + return false; +} + +void BindingManager::BindingManagerPrivate::assignWrapper(SbkObject* wrapper, const void* cptr) +{ + assert(cptr); + WrapperMap::iterator iter = wrapperMapper.find(cptr); + if (iter == wrapperMapper.end()) + wrapperMapper.insert(std::make_pair(cptr, wrapper)); +} + +BindingManager::BindingManager() +{ + m_d = new BindingManager::BindingManagerPrivate; + m_d->wrapperMapper.set_empty_key((WrapperMap::key_type)0); + m_d->wrapperMapper.set_deleted_key((WrapperMap::key_type)1); + +#ifdef SHIBOKEN_INSTALL_FREE_DEBUG_HOOK + debugInstallFreeHook(); +#endif +} + +BindingManager::~BindingManager() +{ +#ifdef SHIBOKEN_INSTALL_FREE_DEBUG_HOOK + debugRemoveFreeHook(); +#endif +#ifndef NDEBUG + showWrapperMap(m_d->wrapperMapper); +#endif + /* Cleanup hanging references. We just invalidate them as when + * the BindingManager is being destroyed the interpreter is alredy + * shutting down. */ + if (Py_IsInitialized()) { // ensure the interpreter is still valid + while (!m_d->wrapperMapper.empty()) { + Object::destroy(m_d->wrapperMapper.begin()->second, const_cast(m_d->wrapperMapper.begin()->first)); + } + assert(m_d->wrapperMapper.size() == 0); + } + delete m_d; +} + +BindingManager& BindingManager::instance() { + static BindingManager singleton; + return singleton; +} + +bool BindingManager::hasWrapper(const void* cptr) +{ + return m_d->wrapperMapper.find(cptr) != m_d->wrapperMapper.end(); +} + +void BindingManager::registerWrapper(SbkObject* pyObj, void* cptr) +{ + SbkObjectType* instanceType = reinterpret_cast(Py_TYPE(pyObj)); + SbkObjectTypePrivate* d = instanceType->d; + + if (!d) + return; + + if (d->mi_init && !d->mi_offsets) + d->mi_offsets = d->mi_init(cptr); + m_d->assignWrapper(pyObj, cptr); + if (d->mi_offsets) { + int* offset = d->mi_offsets; + while (*offset != -1) { + if (*offset > 0) + m_d->assignWrapper(pyObj, reinterpret_cast((std::size_t) cptr + (*offset))); + offset++; + } + } +} + +void BindingManager::releaseWrapper(SbkObject* sbkObj) +{ + SbkObjectType* sbkType = reinterpret_cast(Py_TYPE(sbkObj)); + SbkObjectTypePrivate* d = sbkType->d; + int numBases = ((d && d->is_multicpp) ? getNumberOfCppBaseClasses(Py_TYPE(sbkObj)) : 1); + + void** cptrs = reinterpret_cast(sbkObj)->d->cptr; + for (int i = 0; i < numBases; ++i) { + unsigned char *cptr = reinterpret_cast(cptrs[i]); + m_d->releaseWrapper(cptr, sbkObj); + if (d && d->mi_offsets) { + int* offset = d->mi_offsets; + while (*offset != -1) { + if (*offset > 0) + m_d->releaseWrapper(reinterpret_cast((std::size_t) cptr + (*offset)), sbkObj); + offset++; + } + } + } + sbkObj->d->validCppObject = false; +} + +SbkObject* BindingManager::retrieveWrapper(const void* cptr) +{ + WrapperMap::iterator iter = m_d->wrapperMapper.find(cptr); + if (iter == m_d->wrapperMapper.end()) + return 0; + return iter->second; +} + +PyObject* BindingManager::getOverride(const void* cptr, const char* methodName) +{ + SbkObject* wrapper = retrieveWrapper(cptr); + // The refcount can be 0 if the object is dieing and someone called + // a virtual method from the destructor + if (!wrapper || reinterpret_cast(wrapper)->ob_refcnt == 0) + return 0; + + if (wrapper->ob_dict) { + PyObject* method = PyDict_GetItemString(wrapper->ob_dict, methodName); + if (method) { + Py_INCREF(reinterpret_cast(method)); + return method; + } + } + + PyObject* pyMethodName = Shiboken::String::fromCString(methodName); + PyObject *method = PyObject_GetAttr(reinterpret_cast(wrapper), pyMethodName); + + if (method && PyMethod_Check(method) + && reinterpret_cast(method)->im_self == reinterpret_cast(wrapper)) { + PyObject* defaultMethod; + PyObject* mro = 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) { + Py_DECREF(pyMethodName); + return method; + } + } + } + } + + Py_XDECREF(method); + Py_DECREF(pyMethodName); + return 0; +} + +void BindingManager::addClassInheritance(SbkObjectType* parent, SbkObjectType* child) +{ + m_d->classHierarchy.addEdge(parent, child); +} + +SbkObjectType* BindingManager::resolveType(void* cptr, SbkObjectType* type) +{ + return resolveType(&cptr, type); +} + +SbkObjectType* BindingManager::resolveType(void** cptr, SbkObjectType* type) +{ + SbkObjectType* identifiedType = m_d->classHierarchy.identifyType(cptr, type, type); + return identifiedType ? identifiedType : type; +} + +std::set BindingManager::getAllPyObjects() +{ + std::set pyObjects; + const WrapperMap& wrappersMap = m_d->wrapperMapper; + WrapperMap::const_iterator it = wrappersMap.begin(); + for (; it != wrappersMap.end(); ++it) + pyObjects.insert(reinterpret_cast(it->second)); + + return pyObjects; +} + +void BindingManager::visitAllPyObjects(ObjectVisitor visitor, void* data) +{ + WrapperMap copy = m_d->wrapperMapper; + for (WrapperMap::iterator it = copy.begin(); it != copy.end(); ++it) { + if (hasWrapper(it->first)) + visitor(it->second, data); + } +} + +} // namespace Shiboken + diff --git a/sources/shiboken2/libshiboken/bindingmanager.h b/sources/shiboken2/libshiboken/bindingmanager.h new file mode 100644 index 000000000..80c5add2f --- /dev/null +++ b/sources/shiboken2/libshiboken/bindingmanager.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef BINDINGMANAGER_H +#define BINDINGMANAGER_H + +#include "sbkpython.h" +#include +#include "shibokenmacros.h" + +struct SbkObject; +struct SbkObjectType; + +namespace Shiboken +{ + +typedef void (*ObjectVisitor)(SbkObject*, void*); + +class LIBSHIBOKEN_API BindingManager +{ +public: + static BindingManager& instance(); + + bool hasWrapper(const void *cptr); + + void registerWrapper(SbkObject* pyObj, void* cptr); + void releaseWrapper(SbkObject* wrapper); + + SbkObject* retrieveWrapper(const void* cptr); + PyObject* getOverride(const void* cptr, const char* methodName); + + void addClassInheritance(SbkObjectType* parent, SbkObjectType* child); + /** + * \deprecated Use \fn resolveType(void**, SbkObjectType*), this version is broken when used with multiple inheritance + * because the \p cptr pointer of the discovered type may be different of the given \p cptr in case + * of multiple inheritance + */ + SBK_DEPRECATED(SbkObjectType* resolveType(void* cptr, SbkObjectType* type)); + /** + * Try to find the correct type of *cptr knowing that it's at least of type \p type. + * In case of multiple inheritance this function may change the contents of cptr. + * \param cptr a pointer to a pointer to the instance of type \p type + * \param type type of *cptr + * \warning This function is slow, use it only as last resort. + */ + SbkObjectType* resolveType(void** cptr, SbkObjectType* type); + + std::set getAllPyObjects(); + + /** + * Calls the function \p visitor for each object registered on binding manager. + * \note As various C++ pointers can point to the same PyObject due to multiple inheritance + * a PyObject can be called more than one time for each PyObject. + * \param visitor function called for each object. + * \param data user data passed as second argument to the visitor function. + */ + void visitAllPyObjects(ObjectVisitor visitor, void* data); + +private: + ~BindingManager(); + // disable copy + BindingManager(); + BindingManager(const BindingManager&); + BindingManager& operator=(const BindingManager&); + + struct BindingManagerPrivate; + BindingManagerPrivate* m_d; +}; + +} // namespace Shiboken + +#endif // BINDINGMANAGER_H + diff --git a/sources/shiboken2/libshiboken/conversions.h b/sources/shiboken2/libshiboken/conversions.h new file mode 100644 index 000000000..f0af2be8e --- /dev/null +++ b/sources/shiboken2/libshiboken/conversions.h @@ -0,0 +1,731 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef CONVERSIONS_H +#define CONVERSIONS_H + +#include "sbkpython.h" +#include +#include + +#include "sbkstring.h" +#include "sbkenum.h" +#include "basewrapper.h" +#include "bindingmanager.h" +#include "sbkdbg.h" + +// When the user adds a function with an argument unknown for the typesystem, the generator writes type checks as +// TYPENAME_Check, so this macro allows users to add PyObject arguments to their added functions. +#define PyObject_Check(X) true +#define SbkChar_Check(X) (SbkNumber_Check(X) || Shiboken::String::checkChar(X)) +#include "autodecref.h" + +namespace Shiboken +{ +/** +* This function template is used to get the PyTypeObject of a C++ type T. +* All implementations should be provided by template specializations generated by the generator when +* T isn't a C++ primitive type. +* \see SpecialCastFunction +*/ +template +PyTypeObject* SbkType() +{ + return 0; +} + +template<> inline PyTypeObject* SbkType() { return &PyInt_Type; } +template<> inline PyTypeObject* SbkType() { return &PyLong_Type; } +template<> inline PyTypeObject* SbkType() { return &PyInt_Type; } +template<> inline PyTypeObject* SbkType() { return &PyInt_Type; } +template<> inline PyTypeObject* SbkType() { return &PyLong_Type; } +template<> inline PyTypeObject* SbkType() { return &PyLong_Type; } +template<> inline PyTypeObject* SbkType() { return &PyLong_Type; } +template<> inline PyTypeObject* SbkType() { return &PyLong_Type; } +template<> inline PyTypeObject* SbkType() { return &PyBool_Type; } +template<> inline PyTypeObject* SbkType() { return &PyFloat_Type; } +template<> inline PyTypeObject* SbkType() { return &PyFloat_Type; } +template<> inline PyTypeObject* SbkType() { return &PyInt_Type; } +template<> inline PyTypeObject* SbkType() { return &PyInt_Type; } +template<> inline PyTypeObject* SbkType() { return &PyInt_Type; } + +/** + * Convenience template to create wrappers using the proper Python type for a given C++ class instance. + */ +template +inline PyObject* createWrapper(const T* cppobj, bool hasOwnership = false, bool isExactType = false) +{ + const char* typeName = 0; + if (!isExactType) + typeName = typeid(*const_cast(cppobj)).name(); + return Object::newObject(reinterpret_cast(SbkType()), + const_cast(cppobj), hasOwnership, isExactType, typeName); +} + +// Base Conversions ---------------------------------------------------------- +// The basic converter must be empty to avoid object types being converted by value. +template struct Converter {}; + +// Pointer conversion specialization for value types. +template +struct Converter +{ + static inline bool checkType(PyObject* pyObj) + { + return Converter::checkType(pyObj); + } + + static inline bool isConvertible(PyObject* pyObj) + { + return pyObj == Py_None || PyObject_TypeCheck(pyObj, SbkType()); + } + + static PyObject* toPython(const T* cppobj) + { + if (!cppobj) + Py_RETURN_NONE; + PyObject* pyobj = reinterpret_cast(BindingManager::instance().retrieveWrapper(cppobj)); + if (pyobj) + Py_INCREF(pyobj); + else + pyobj = createWrapper(cppobj); + return pyobj; + } + + static T* toCpp(PyObject* pyobj) + { + if (PyObject_TypeCheck(pyobj, SbkType())) + return reinterpret_cast(Object::cppPointer(reinterpret_cast(pyobj), SbkType())); + else if (Converter::isConvertible(pyobj)) + return new T(Converter::toCpp(pyobj)); + else if (pyobj == Py_None) + return 0; + + assert(false); + return 0; + } +}; +template struct Converter : Converter {}; + +// Specialization for reference conversions. +template +struct Converter +{ + static inline bool checkType(PyObject* pyObj) { return Converter::checkType(pyObj); } + static inline bool isConvertible(PyObject* pyObj) { return Converter::isConvertible(pyObj); } + static inline PyObject* toPython(const T& cppobj) { return Converter::toPython(&cppobj); } + static inline T& toCpp(PyObject* pyobj) { return *Converter::toCpp(pyobj); } +}; + +// Void pointer conversions. +template<> +struct Converter +{ + static inline bool checkType(PyObject *) { return false; } + static inline bool isConvertible(PyObject *) { return true; } + static PyObject* toPython(void* cppobj) + { + if (!cppobj) + Py_RETURN_NONE; + PyObject *result = reinterpret_cast(cppobj); + Py_INCREF(result); + return result; + } + static void* toCpp(PyObject* pyobj) { return pyobj; } +}; + +// Base converter meant to be inherited by converters for classes that could be +// passed by value. +// Example: "struct Converter : ValueTypeConverter" +template +struct ValueTypeConverter +{ + static inline bool checkType(PyObject* pyObj) { return PyObject_TypeCheck(pyObj, SbkType()); } + + // The basic version of this method also tries to use the extended 'isConvertible' method. + static inline bool isConvertible(PyObject* pyobj) + { + if (PyObject_TypeCheck(pyobj, SbkType())) + return true; + SbkObjectType* shiboType = reinterpret_cast(SbkType()); + return ObjectType::isExternalConvertible(shiboType, pyobj); + } + static inline PyObject* toPython(void* cppobj) { return toPython(*reinterpret_cast(cppobj)); } + static inline PyObject* toPython(const T& cppobj) + { + PyObject* obj = createWrapper(new T(cppobj), true, true); +// SbkBaseWrapper_setContainsCppWrapper(obj, SbkTypeInfo::isCppWrapper); + return obj; + } + // Classes with implicit conversions are expected to reimplement 'toCpp' to build T from + // its various implicit constructors. Even classes without implicit conversions could + // get some of those via other modules defining conversion operator for them, thus + // the basic Converter for value types checks for extended conversion and tries to + // use them if it is the case. + static inline T toCpp(PyObject* pyobj) + { + if (!PyObject_TypeCheck(pyobj, SbkType())) { + SbkObjectType* shiboType = reinterpret_cast(SbkType()); + if (ObjectType::hasExternalCppConversions(shiboType) && isConvertible(pyobj)) { + T* cptr = reinterpret_cast(ObjectType::callExternalCppConversion(shiboType, pyobj)); + const T result = *cptr; + delete cptr; + return result; + } + assert(false); + } + return *reinterpret_cast(Object::cppPointer(reinterpret_cast(pyobj), SbkType())); + } +}; + +// Base converter meant to be inherited by converters for abstract classes and object types +// (i.e. classes with private copy constructors and = operators). +// Example: "struct Converter : ObjectTypeConverter" +template +struct ObjectTypeConverter +{ + static inline bool checkType(PyObject* pyObj) { return pyObj == Py_None || PyObject_TypeCheck(pyObj, SbkType()); } + /// Py_None objects are the only objects convertible to an object type (in the form of a NULL pointer). + static inline bool isConvertible(PyObject* pyObj) { return pyObj == Py_None || PyObject_TypeCheck(pyObj, SbkType()); } + /// Convenience overload that calls "toPython(const T*)" method. + static inline PyObject* toPython(void* cppobj) { return toPython(reinterpret_cast(cppobj)); } + /// Returns a new Python wrapper for the C++ object or an existing one with its reference counter incremented. + static PyObject* toPython(const T* cppobj) + { + if (!cppobj) + Py_RETURN_NONE; + PyObject* pyobj = reinterpret_cast(BindingManager::instance().retrieveWrapper(cppobj)); + if (pyobj) + Py_INCREF(pyobj); + else + pyobj = createWrapper(cppobj); + return pyobj; + } + /// Returns the wrapped C++ pointer casted properly, or a NULL pointer if the argument is a Py_None. + static T* toCpp(PyObject* pyobj) + { + if (pyobj == Py_None) + return 0; + SbkObject *sbkObj = reinterpret_cast(pyobj); + SbkObjectType* shiboType = reinterpret_cast(pyobj->ob_type); + if (ObjectType::hasCast(shiboType)) + return reinterpret_cast(ObjectType::cast(shiboType, sbkObj, SbkType())); + return reinterpret_cast(Object::cppPointer(sbkObj, SbkType())); + } +}; + +template +struct ObjectTypeReferenceConverter : ObjectTypeConverter +{ + static inline bool checkType(PyObject* pyObj) { return PyObject_TypeCheck(pyObj, SbkType()); } + static inline bool isConvertible(PyObject* pyObj) { return PyObject_TypeCheck(pyObj, SbkType()); } + static inline PyObject* toPython(const T& cppobj) { return Converter::toPython(&cppobj); } + static inline T& toCpp(PyObject* pyobj) + { + T* t = Converter::toCpp(pyobj); + assert(t); + return *t; + } +}; + +// PyObject* specialization to avoid converting what doesn't need to be converted. +template<> +struct Converter : ObjectTypeConverter +{ + static inline PyObject* toCpp(PyObject* pyobj) { return pyobj; } +}; + +// Primitive Conversions ------------------------------------------------------ +template <> +struct Converter +{ + static inline bool checkType(PyObject* pyobj) { return PyBool_Check(pyobj); } + static inline bool isConvertible(PyObject* pyobj) { return PyInt_Check(pyobj); } + static inline PyObject* toPython(void* cppobj) { return toPython(*reinterpret_cast(cppobj)); } + static inline PyObject* toPython(bool cppobj) { return PyBool_FromLong(cppobj); } + static inline bool toCpp(PyObject* pyobj) { return PyInt_AS_LONG(pyobj); } +}; + +/** + * Helper template for checking if a value overflows when casted to type T + */ +template::is_signed > +struct OverFlowChecker; + +template +struct OverFlowChecker +{ + static bool check(const PY_LONG_LONG& value) + { + return value < std::numeric_limits::min() || value > std::numeric_limits::max(); + } +}; + +template +struct OverFlowChecker +{ + static bool check(const PY_LONG_LONG& value) + { + return value < 0 || static_cast(value) > std::numeric_limits::max(); + } +}; + +template<> +struct OverFlowChecker +{ + static bool check(const PY_LONG_LONG &) + { + return false; + } +}; + +template<> +struct OverFlowChecker +{ + static bool check(const double &) + { + return false; + } +}; + +template<> +struct OverFlowChecker +{ + static bool check(const double& value) + { + return value < std::numeric_limits::min() || value > std::numeric_limits::max(); + } +}; + +template +struct Converter_PyInt +{ + static inline bool checkType(PyObject* pyobj) { return PyInt_Check(pyobj); } + static inline bool isConvertible(PyObject* pyobj) { return SbkNumber_Check(pyobj); } + static inline PyObject* toPython(void* cppobj) { return toPython(*reinterpret_cast(cppobj)); } + static inline PyObject* toPython(const PyIntEquiv& cppobj) { return PyInt_FromLong((long) cppobj); } + static PyIntEquiv toCpp(PyObject* pyobj) + { + if (PyFloat_Check(pyobj)) { + double d_result = PyFloat_AS_DOUBLE(pyobj); + // If cast to long directly it could overflow silently + if (OverFlowChecker::check(d_result)) + PyErr_SetObject(PyExc_OverflowError, 0); + return static_cast(d_result); + } else { + PY_LONG_LONG result = PyLong_AsLongLong(pyobj); + if (OverFlowChecker::check(result)) + PyErr_SetObject(PyExc_OverflowError, 0); + return static_cast(result); + } + } +}; + +template +struct Converter_PyULongInt : Converter_PyInt +{ + static inline PyObject* toPython(void* cppobj) { return toPython(*reinterpret_cast(cppobj)); } + static inline PyObject* toPython(const T& cppobj) { return PyLong_FromUnsignedLong(cppobj); } +}; + +/// Specialization to convert char and unsigned char, it accepts Python numbers and strings with just one character. +template +struct CharConverter +{ + static inline bool checkType(PyObject* pyobj) { return SbkChar_Check(pyobj); } + static inline bool isConvertible(PyObject* pyobj) { return SbkChar_Check(pyobj); } + static inline PyObject* toPython(void* cppobj) { return toPython(*reinterpret_cast(cppobj)); } + static inline PyObject* toPython(const CharType& cppobj) { return PyInt_FromLong(cppobj); } + static CharType toCpp(PyObject* pyobj) + { + if (PyBytes_Check(pyobj)) { + assert(PyBytes_GET_SIZE(pyobj) == 1); // This check is made on SbkChar_Check + return PyBytes_AS_STRING(pyobj)[0]; + } else if (PyInt_Check(pyobj)) { + PY_LONG_LONG result = PyInt_AsUnsignedLongLongMask(pyobj); + if (OverFlowChecker::check(result)) + PyErr_SetObject(PyExc_OverflowError, 0); + return result; + } else if (Shiboken::String::check(pyobj)) { + return Shiboken::String::toCString(pyobj)[0]; + } else { + return 0; + } + } +}; + +template <> struct Converter : Converter_PyULongInt {}; +template <> struct Converter : Converter_PyULongInt {}; +template <> struct Converter : CharConverter +{ + // Should we really return a string? + using CharConverter::toPython; + using CharConverter::isConvertible; + using CharConverter::toCpp; + + + static inline bool isConvertible(PyObject* pyobj) { + return SbkChar_Check(pyobj); + } + + static inline PyObject* toPython(const char& cppObj) { + return Shiboken::String::fromFormat("%c", cppObj); + } + + static char toCpp(PyObject* pyobj) + { + if (PyBytes_Check(pyobj)) { + assert(PyBytes_GET_SIZE(pyobj) == 1); // This check is made on SbkChar_Check + return PyBytes_AS_STRING(pyobj)[0]; + } else if (PyInt_Check(pyobj)) { + PY_LONG_LONG result = PyInt_AsUnsignedLongLongMask(pyobj); + if (OverFlowChecker::check(result)) + PyErr_SetObject(PyExc_OverflowError, 0); + return char(result); + } else if (Shiboken::String::check(pyobj)) { + return Shiboken::String::toCString(pyobj)[0]; + } else { + return 0; + } + } +}; +template <> struct Converter : CharConverter {}; +template <> struct Converter : CharConverter {}; +template <> struct Converter : Converter_PyInt {}; +template <> struct Converter : Converter_PyInt {}; +template <> struct Converter : Converter_PyInt {}; +template <> struct Converter : Converter_PyInt {}; + +template <> +struct Converter +{ + static inline PyObject* toPython(void* cppobj) { return toPython(*reinterpret_cast(cppobj)); } + static inline PyObject* toPython(PY_LONG_LONG cppobj) { return PyLong_FromLongLong(cppobj); } + static inline PY_LONG_LONG toCpp(PyObject* pyobj) { return (PY_LONG_LONG) PyLong_AsLongLong(pyobj); } +}; + +template <> +struct Converter +{ + static inline PyObject* toPython(void* cppobj) + { + return toPython(*reinterpret_cast(cppobj)); + } + static inline PyObject* toPython(unsigned PY_LONG_LONG cppobj) + { + return PyLong_FromUnsignedLongLong(cppobj); + } + static inline unsigned PY_LONG_LONG toCpp(PyObject* pyobj) + { +#if PY_MAJOR_VERSION >= 3 + if (!PyLong_Check(pyobj)) { + PyErr_SetString(PyExc_TypeError, "Invalid type for unsigned long long conversion"); + return 0; + } + + return PyLong_AsUnsignedLongLong(pyobj); +#else + if (PyInt_Check(pyobj)) { + long result = (unsigned PY_LONG_LONG) PyInt_AsLong(pyobj); + if (result < 0) { + PyErr_SetObject(PyExc_OverflowError, 0); + return 0; + } else + return (unsigned PY_LONG_LONG) result; + } else if (PyLong_Check(pyobj)) { + return (unsigned PY_LONG_LONG) PyLong_AsUnsignedLongLong(pyobj); + } else { + PyErr_SetString(PyExc_TypeError, "Invalid type for unsigned long long conversion"); + return 0; + } +#endif // Python 2 + } +}; + +template +struct Converter_PyFloat +{ + static inline bool checkType(PyObject* obj) { return PyFloat_Check(obj); } + static inline bool isConvertible(PyObject* obj) { return SbkNumber_Check(obj); } + static inline PyObject* toPython(void* cppobj) { return toPython(*reinterpret_cast(cppobj)); } + static inline PyObject* toPython(PyFloatEquiv cppobj) { return PyFloat_FromDouble((double) cppobj); } + static inline PyFloatEquiv toCpp(PyObject* pyobj) + { + if (PyInt_Check(pyobj) || PyLong_Check(pyobj)) + return (PyFloatEquiv) PyLong_AsLong(pyobj); + return (PyFloatEquiv) PyFloat_AsDouble(pyobj); + } +}; + +template <> struct Converter : Converter_PyFloat {}; +template <> struct Converter : Converter_PyFloat {}; + +// PyEnum Conversions --------------------------------------------------------- +template +struct EnumConverter +{ + static inline bool checkType(PyObject* pyObj) { return PyObject_TypeCheck(pyObj, SbkType()); } + static inline bool isConvertible(PyObject* pyObj) { return PyObject_TypeCheck(pyObj, SbkType()); } + static inline PyObject* toPython(void* cppobj) { return toPython(*reinterpret_cast(cppobj)); } + static inline PyObject* toPython(CppEnum cppenum) + { + return Shiboken::Enum::newItem(Shiboken::SbkType(), (long) cppenum); + } + static inline CppEnum toCpp(PyObject* pyObj) + { + return (CppEnum) Shiboken::Enum::getValue(pyObj);; + } +}; + +// C Sting Types -------------------------------------------------------------- +template +struct Converter_CString +{ + // Note: 0 is also a const char* in C++, so None is accepted in checkType + static inline bool checkType(PyObject* pyObj) { + return Shiboken::String::check(pyObj); + } + static inline bool isConvertible(PyObject* pyObj) { + return Shiboken::String::isConvertible(pyObj); + } + static inline PyObject* toPython(void* cppobj) { return toPython(reinterpret_cast(cppobj)); } + static inline PyObject* toPython(CString cppobj) + { + if (!cppobj) + Py_RETURN_NONE; + return Shiboken::String::fromCString(cppobj); + } + static inline CString toCpp(PyObject* pyobj) { + if (pyobj == Py_None) + return 0; + return Shiboken::String::toCString(pyobj); + } +}; + +template <> struct Converter : Converter_CString {}; + +template <> struct Converter : Converter_CString +{ + static inline PyObject* toPython(void* cppobj) { return toPython(*reinterpret_cast(cppobj)); } + static inline PyObject* toPython(std::string cppObj) + { + return Shiboken::String::fromCString(cppObj.c_str()); + } + + static inline std::string toCpp(PyObject* pyobj) + { + if (pyobj == Py_None) + return 0; + return std::string(Shiboken::String::toCString(pyobj)); + } +}; + +// C++ containers ------------------------------------------------------------- +// The following container converters are meant to be used for pairs, lists and maps +// that are similar to the STL containers of the same name. + +// For example to create a converter for a std::list the following code is enough: +// template struct Converter > : StdListConverter > {}; + +// And this for a std::map: +// template +// struct Converter > : StdMapConverter > {}; + +template +struct StdListConverter +{ + static inline bool checkType(PyObject* pyObj) + { + return isConvertible(pyObj); + } + + static inline bool isConvertible(PyObject* pyObj) + { + if (PyObject_TypeCheck(pyObj, SbkType())) + return true; + // Sequence conversion are made ONLY for python sequences, not for + // binded types implementing sequence protocol, otherwise this will + // cause a mess like QBitArray being accepted by someone expecting a + // QStringList. + if ((SbkType() && Object::checkType(pyObj)) || !PySequence_Check(pyObj)) + return false; + for (int i = 0, max = PySequence_Length(pyObj); i < max; ++i) { + AutoDecRef item(PySequence_GetItem(pyObj, i)); + if (!Converter::isConvertible(item)) + return false; + } + return true; + } + static PyObject* toPython(void* cppObj) { return toPython(*reinterpret_cast(cppObj)); } + static PyObject* toPython(const StdList& cppobj) + { + PyObject* result = PyList_New((int) cppobj.size()); + typename StdList::const_iterator it = cppobj.begin(); + for (int idx = 0; it != cppobj.end(); ++it, ++idx) { + typename StdList::value_type vh(*it); + PyList_SET_ITEM(result, idx, Converter::toPython(vh)); + } + return result; + } + static StdList toCpp(PyObject* pyobj) + { + if (PyObject_TypeCheck(pyobj, SbkType())) + return *reinterpret_cast(Object::cppPointer(reinterpret_cast(pyobj), SbkType())); + + StdList result; + for (int i = 0; i < PySequence_Size(pyobj); i++) { + AutoDecRef pyItem(PySequence_GetItem(pyobj, i)); + result.push_back(Converter::toCpp(pyItem)); + } + return result; + } +}; + +template +struct StdPairConverter +{ + static inline bool checkType(PyObject* pyObj) + { + return isConvertible(pyObj); + } + + static inline bool isConvertible(PyObject* pyObj) + { + if (PyObject_TypeCheck(pyObj, SbkType())) + return true; + if ((SbkType() && Object::checkType(pyObj)) || !PySequence_Check(pyObj) || PySequence_Length(pyObj) != 2) + return false; + + AutoDecRef item1(PySequence_GetItem(pyObj, 0)); + AutoDecRef item2(PySequence_GetItem(pyObj, 1)); + + if (!Converter::isConvertible(item1) + && !Converter::isConvertible(item2)) { + return false; + } + return true; + } + static PyObject* toPython(void* cppObj) { return toPython(*reinterpret_cast(cppObj)); } + static PyObject* toPython(const StdPair& cppobj) + { + typename StdPair::first_type first(cppobj.first); + typename StdPair::second_type second(cppobj.second); + PyObject* tuple = PyTuple_New(2); + PyTuple_SET_ITEM(tuple, 0, Converter::toPython(first)); + PyTuple_SET_ITEM(tuple, 1, Converter::toPython(second)); + return tuple; + } + static StdPair toCpp(PyObject* pyobj) + { + StdPair result; + AutoDecRef pyFirst(PySequence_GetItem(pyobj, 0)); + AutoDecRef pySecond(PySequence_GetItem(pyobj, 1)); + result.first = Converter::toCpp(pyFirst); + result.second = Converter::toCpp(pySecond); + return result; + } +}; + +template +struct StdMapConverter +{ + static inline bool checkType(PyObject* pyObj) + { + return isConvertible(pyObj); + } + + static inline bool isConvertible(PyObject* pyObj) + { + if (PyObject_TypeCheck(pyObj, SbkType())) + return true; + if ((SbkType() && Object::checkType(pyObj)) || !PyDict_Check(pyObj)) + return false; + + PyObject* key; + PyObject* value; + Py_ssize_t pos = 0; + + while (PyDict_Next(pyObj, &pos, &key, &value)) { + if (!Converter::isConvertible(key) + || !Converter::isConvertible(value)) { + return false; + } + } + return true; + } + + static PyObject* toPython(void* cppObj) { return toPython(*reinterpret_cast(cppObj)); } + static PyObject* toPython(const StdMap& cppobj) + { + PyObject* result = PyDict_New(); + typename StdMap::const_iterator it = cppobj.begin(); + + for (; it != cppobj.end(); ++it) { + PyDict_SetItem(result, + Converter::toPython(it->first), + Converter::toPython(it->second)); + } + + return result; + } + static StdMap toCpp(PyObject* pyobj) + { + StdMap result; + + PyObject* key; + PyObject* value; + Py_ssize_t pos = 0; + + while (PyDict_Next(pyobj, &pos, &key, &value)) { + result.insert(typename StdMap::value_type( + Converter::toCpp(key), + Converter::toCpp(value))); + } + return result; + } +}; + + +// class used to translate python objects to another type +template struct PythonConverter {}; + +} // namespace Shiboken + +#endif // CONVERSIONS_H + diff --git a/sources/shiboken2/libshiboken/debugfreehook.cpp b/sources/shiboken2/libshiboken/debugfreehook.cpp new file mode 100644 index 000000000..f5757a70f --- /dev/null +++ b/sources/shiboken2/libshiboken/debugfreehook.cpp @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** 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 "debugfreehook.h" +#include "bindingmanager.h" +#include "gilstate.h" + +#if defined(_WIN32) && defined(_DEBUG) +#include +#include +#endif + +#ifdef __GLIBC__ +#include +#endif + +#ifdef __APPLE__ +#include +#include +#include +#endif + +#ifdef SHIBOKEN_INSTALL_FREE_DEBUG_HOOK +extern "C" { + +static int testPointerBeingFreed(void *ptr) +{ + // It is an error for a deleted pointer address to still be registered + // in the BindingManager + if (Shiboken::BindingManager::instance().hasWrapper(ptr)) { + Shiboken::GilState state; + + SbkObject *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(ptr); + + fprintf(stderr, "SbkObject still in binding map when deleted: "); + PyObject_Print(reinterpret_cast(wrapper), stderr, 0); + fprintf(stderr, "\n"); + +#ifdef _WIN32 + DebugBreak(); +#else + assert(0); +#endif + return FALSE; + } + + return TRUE; +} + +#if defined(_WIN32) && defined(_DEBUG) +static _CRT_ALLOC_HOOK lastCrtAllocHook; +static int DebugAllocHook(int nAllocType, void *pvData, + size_t nSize, int nBlockUse, long lRequest, + const unsigned char * szFileName, int nLine) +{ + // It is an error for a deleted pointer address to still be registered + // in the BindingManager + if ( nAllocType == _HOOK_FREE) { + if ( !testPointerBeingFreed(pvData) ) { + return 0; + } + } + + if ( lastCrtAllocHook != NULL ) { + return lastCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, + szFileName, nLine); + } + + return 1; +} +#endif // _WIN32 && _DEBUG + +#ifdef __GLIBC__ +static void (*lastFreeHook)(void* ptr, const void* caller); +static void DebugFreeHook(void* ptr, const void* caller) +{ + testPointerBeingFreed(ptr); + + if ( lastFreeHook != NULL ) + lastFreeHook(ptr, caller); +} +#endif // __GLIBC__ + +#ifdef __APPLE__ +static malloc_zone_t lastMallocZone; +static void DebugFreeHook(malloc_zone_t *zone, void *ptr) +{ + testPointerBeingFreed(ptr); + + if ( lastMallocZone.free != NULL ) + lastMallocZone.free(zone, ptr); +} +static void DebugFreeDefiniteSizeHook(malloc_zone_t *zone, void *ptr, size_t size) +{ + testPointerBeingFreed(ptr); + + if ( lastMallocZone.free_definite_size != NULL ) + lastMallocZone.free_definite_size(zone, ptr, size); +} +#endif __APPLE__ + +void debugInstallFreeHook(void) +{ +#if defined(_WIN32) && defined(_DEBUG) + lastCrtAllocHook = _CrtSetAllocHook(DebugAllocHook); +#endif + +#ifdef __GLIBC__ + // __free_hook is not thread safe so it marked as deprecated. Use here + // is hopefully safe and should catch errors in a single threaded program + // and only miss some in a multithreaded program + lastFreeHook = __free_hook; + __free_hook = DebugFreeHook; +#endif + +#ifdef __APPLE__ + malloc_zone_t* zone = malloc_default_zone(); + assert(zone != NULL); + //remove the write protection from the zone struct + if (zone->version >= 8) { + vm_protect(mach_task_self(), (uintptr_t)zone, sizeof(*zone), 0, VM_PROT_READ | VM_PROT_WRITE); + } + lastMallocZone = *zone; + zone->free = DebugFreeHook; + zone->free_definite_size = DebugFreeDefiniteSizeHook; + if (zone->version >= 8) { + vm_protect(mach_task_self(), (uintptr_t)zone, sizeof(*zone), 0, VM_PROT_READ); + } +#endif +} + +void debugRemoveFreeHook(void) +{ +#if defined(_WIN32) && defined(_DEBUG) + _CrtSetAllocHook(lastCrtAllocHook); +#endif + +#ifdef __GLIBC__ + __free_hook = lastFreeHook; +#endif + +#ifdef __APPLE__ + malloc_zone_t* zone = malloc_default_zone(); + assert(zone != NULL); + //remove the write protection from the zone struct + if (zone->version >= 8) { + vm_protect(mach_task_self(), (uintptr_t)zone, sizeof(*zone), 0, VM_PROT_READ | VM_PROT_WRITE); + } + zone->free = lastMallocZone.free; + zone->free_definite_size = lastMallocZone.free_definite_size; + if (zone->version >= 8) { + vm_protect(mach_task_self(), (uintptr_t)zone, sizeof(*zone), 0, VM_PROT_READ); + } +#endif +} + +} // extern "C" +#endif // SHIBOKEN_INSTALL_DEBUG_FREE_HOOK diff --git a/sources/shiboken2/libshiboken/debugfreehook.h b/sources/shiboken2/libshiboken/debugfreehook.h new file mode 100644 index 000000000..743d56e4c --- /dev/null +++ b/sources/shiboken2/libshiboken/debugfreehook.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef DEBUGFREEHOOK_H +#define DEBUGFREEHOOK_H + +// These functions enable C library runtime hooks to try to catch cases where +// C++ object addresses remain in hash table of valid wrappers when the address +// is passed to free. The hooks are probably not thread safe and thus +// should only be enabled in single threaded environments + +// To enable the hook, uncomment the following define. +//#define SHIBOKEN_INSTALL_FREE_DEBUG_HOOK + +#ifdef SHIBOKEN_INSTALL_FREE_DEBUG_HOOK +extern "C" { + +void debugInstallFreeHook(void); +void debugRemoveFreeHook(void); + +} // extern "C" + +#endif // SHIBOKEN_INSTALL_FREE_DEBUG_HOOK + +#endif // DEBUGFREEHOOK_H diff --git a/sources/shiboken2/libshiboken/gilstate.cpp b/sources/shiboken2/libshiboken/gilstate.cpp new file mode 100644 index 000000000..e7406fdd6 --- /dev/null +++ b/sources/shiboken2/libshiboken/gilstate.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** 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 "gilstate.h" + +namespace Shiboken +{ + +GilState::GilState() + : m_locked(false) +{ + if (Py_IsInitialized()) { + m_gstate = PyGILState_Ensure(); + m_locked = true; + } +} + +GilState::~GilState() +{ + release(); +} + +void GilState::release() +{ + if (m_locked && Py_IsInitialized()) { + PyGILState_Release(m_gstate); + m_locked = false; + } +} + +} // namespace Shiboken + diff --git a/sources/shiboken2/libshiboken/gilstate.h b/sources/shiboken2/libshiboken/gilstate.h new file mode 100644 index 000000000..e0f18a814 --- /dev/null +++ b/sources/shiboken2/libshiboken/gilstate.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef GILSTATE_H +#define GILSTATE_H + +#include +#include "sbkpython.h" + +namespace Shiboken +{ + +class LIBSHIBOKEN_API GilState +{ +public: + GilState(); + ~GilState(); + void release(); +private: + PyGILState_STATE m_gstate; + bool m_locked; +}; + +} // namespace Shiboken + +#endif // GILSTATE_H + diff --git a/sources/shiboken2/libshiboken/helper.cpp b/sources/shiboken2/libshiboken/helper.cpp new file mode 100644 index 000000000..9709d0776 --- /dev/null +++ b/sources/shiboken2/libshiboken/helper.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** 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 "helper.h" +#include + +namespace Shiboken +{ + +bool sequenceToArgcArgv(PyObject* argList, int* argc, char*** argv, const char* defaultAppName) +{ + if (!PySequence_Check(argList)) + return false; + + if (!defaultAppName) + defaultAppName = "PySideApplication"; + + // Check all items + Shiboken::AutoDecRef args(PySequence_Fast(argList, 0)); + int numArgs = PySequence_Fast_GET_SIZE(argList); + for (int i = 0; i < numArgs; ++i) { + PyObject* item = PySequence_Fast_GET_ITEM(args.object(), i); + if (!PyBytes_Check(item) && !PyUnicode_Check(item)) + return false; + } + + bool hasEmptyArgList = numArgs == 0; + if (hasEmptyArgList) + numArgs = 1; + + *argc = numArgs; + *argv = new char*[*argc]; + + if (hasEmptyArgList) { + // Try to get the script name + PyObject* globals = PyEval_GetGlobals(); + PyObject* appName = PyDict_GetItemString(globals, "__file__"); + (*argv)[0] = strdup(appName ? Shiboken::String::toCString(appName) : defaultAppName); + } else { + for (int i = 0; i < numArgs; ++i) { + PyObject* item = PySequence_Fast_GET_ITEM(args.object(), i); + char* string = 0; + if (Shiboken::String::check(item)) { + string = strdup(Shiboken::String::toCString(item)); + } + (*argv)[i] = string; + } + } + + return true; +} + +int* sequenceToIntArray(PyObject* obj, bool zeroTerminated) +{ + AutoDecRef seq(PySequence_Fast(obj, "Sequence of ints expected")); + if (seq.isNull()) + return 0; + + Py_ssize_t size = PySequence_Fast_GET_SIZE(seq.object()); + int* array = new int[size + (zeroTerminated ? 1 : 0)]; + + for (int i = 0; i < size; i++) { + PyObject* item = PySequence_Fast_GET_ITEM(seq.object(), i); + if (!PyInt_Check(item)) { + PyErr_SetString(PyExc_TypeError, "Sequence of ints expected"); + delete[] array; + return 0; + } else { + array[i] = PyInt_AsLong(item); + } + } + + if (zeroTerminated) + array[size] = 0; + + return array; +} + + +int warning(PyObject* category, int stacklevel, const char* format, ...) +{ + va_list args; + va_start(args, format); +#if _WIN32 + va_list args2 = args; +#else + va_list args2; + va_copy(args2, args); +#endif + + // check the necessary memory + int size = vsnprintf(NULL, 0, format, args) + 1; + char* message = new char[size]; + int result = 0; + if (message) { + // format the message + vsnprintf(message, size, format, args2); + result = PyErr_WarnEx(category, message, stacklevel); + delete [] message; + } + va_end(args2); + va_end(args); + return result; +} + +} // namespace Shiboken diff --git a/sources/shiboken2/libshiboken/helper.h b/sources/shiboken2/libshiboken/helper.h new file mode 100644 index 000000000..f2061b667 --- /dev/null +++ b/sources/shiboken2/libshiboken/helper.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef HELPER_H +#define HELPER_H + +#include "sbkpython.h" +#include "shibokenmacros.h" +#include "conversions.h" +#include "autodecref.h" + +#define SBK_UNUSED(x) (void)x; + +namespace Shiboken +{ + +template +inline PyObject* makeTuple(const A& a) +{ + return PyTuple_Pack(1, AutoDecRef(Converter::toPython(a)).object()); +} + +template +inline PyObject* makeTuple(const A& a, const B& b) +{ + return PyTuple_Pack(2, AutoDecRef(Converter::toPython(a)).object(), + AutoDecRef(Converter::toPython(b)).object()); +} + +template +inline PyObject* makeTuple(const A& a, const B& b, const C& c) +{ + return PyTuple_Pack(3, AutoDecRef(Converter::toPython(a)).object(), + AutoDecRef(Converter::toPython(b)).object(), + AutoDecRef(Converter::toPython(c)).object()); +} + +template +inline PyObject* makeTuple(const A& a, const B& b, const C& c, const D& d) +{ + return PyTuple_Pack(4, AutoDecRef(Converter::toPython(a)).object(), + AutoDecRef(Converter::toPython(b)).object(), + AutoDecRef(Converter::toPython(c)).object(), + AutoDecRef(Converter::toPython(d)).object()); +} + +template +inline PyObject* makeTuple(const A& a, const B& b, const C& c, const D& d, const E& e) +{ + return PyTuple_Pack(5, AutoDecRef(Converter::toPython(a)).object(), + AutoDecRef(Converter::toPython(b)).object(), + AutoDecRef(Converter::toPython(c)).object(), + AutoDecRef(Converter::toPython(d)).object(), + AutoDecRef(Converter::toPython(e)).object()); +} + +/** +* It transforms a python sequence into two C variables, argc and argv. +* This function tries to find the application (script) name and put it into argv[0], if +* the application name can't be guessed, defaultAppName will be used. +* +* No memory is allocated is an error occur. +* +* \note argc must be a valid address. +* \note The argv array is allocated using new operator and each item is allocated using malloc. +* \returns True on sucess, false otherwise. +*/ +LIBSHIBOKEN_API bool sequenceToArgcArgv(PyObject* argList, int* argc, char*** argv, const char* defaultAppName = 0); + +/** + * Convert a python sequence into a heap-allocated array of ints. + * + * \returns The newly allocated array or NULL in case of error or empty sequence. Check with PyErr_Occurred + * if it was successfull. + */ +LIBSHIBOKEN_API int* sequenceToIntArray(PyObject* obj, bool zeroTerminated = false); + +/** + * Creates and automatically deallocates C++ arrays. + */ +template +class AutoArrayPointer +{ + public: + AutoArrayPointer(int size) { data = new T[size]; } + T& operator[](int pos) { return data[pos]; } + operator T*() const { return data; } + ~AutoArrayPointer() { delete[] data; } + private: + T* data; +}; + +/** + * An utility function used to call PyErr_WarnEx with a formatted message. + */ +LIBSHIBOKEN_API int warning(PyObject* category, int stacklevel, const char* format, ...); + +} // namespace Shiboken + +#endif // HELPER_H diff --git a/sources/shiboken2/libshiboken/python25compat.h b/sources/shiboken2/libshiboken/python25compat.h new file mode 100644 index 000000000..71c88d86a --- /dev/null +++ b/sources/shiboken2/libshiboken/python25compat.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef PYTHON25COMPAT_H +#define PYTHON25COMPAT_H +#include +#include + +/* + *The #defines below were taken from Cython-generated code to allow shiboken to be used with python2.5. + * Maybe not all of these defines are useful to us, time will tell which ones are really needed or not. + */ + +#if PY_VERSION_HEX < 0x02060000 +#define Py_REFCNT(ob) (((PyObject*)(ob))->ob_refcnt) +#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) +#define Py_SIZE(ob) (((PyVarObject*)(ob))->ob_size) +#define PyVarObject_HEAD_INIT(type, size) \ + PyObject_HEAD_INIT(type) size, +#define PyType_Modified(t) + +typedef struct { + void *buf; + PyObject *obj; + Py_ssize_t len; + Py_ssize_t itemsize; + int readonly; + int ndim; + char *format; + Py_ssize_t *shape; + Py_ssize_t *strides; + Py_ssize_t *suboffsets; + void *internal; +} Py_buffer; + +#define PyBUF_SIMPLE 0 +#define PyBUF_WRITABLE 0x0001 +#define PyBUF_LOCK 0x0002 +#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 PyBytes_Check PyString_Check +#define PyBytes_FromString PyString_FromString +#define PyBytes_FromFormat PyString_FromFormat +#define PyBytes_FromStringAndSize PyString_FromStringAndSize +#define PyBytes_GET_SIZE PyString_GET_SIZE +#define PyBytes_AS_STRING PyString_AS_STRING +#define PyBytes_AsString PyString_AsString +#define PyBytes_Concat PyString_Concat +#define PyBytes_Size PyString_Size + +inline PyObject* PyUnicode_FromString(const char* s) +{ + std::size_t len = std::strlen(s); + return PyUnicode_DecodeUTF8(s, len, 0); +} + +#define PyLong_FromSize_t _PyLong_FromSize_t +#define PyLong_AsSsize_t _PyLong_AsSsize_t + +#endif + +#endif diff --git a/sources/shiboken2/libshiboken/sbkconverter.cpp b/sources/shiboken2/libshiboken/sbkconverter.cpp new file mode 100644 index 000000000..e7e9995b4 --- /dev/null +++ b/sources/shiboken2/libshiboken/sbkconverter.cpp @@ -0,0 +1,572 @@ +/**************************************************************************** +** +** 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 "sbkconverter.h" +#include "sbkconverter_p.h" +#include "basewrapper_p.h" +#include "google/dense_hash_map" +#include "autodecref.h" +#include "sbkdbg.h" +#include "helper.h" + +static SbkConverter** PrimitiveTypeConverters; + +typedef google::dense_hash_map ConvertersMap; +static ConvertersMap converters; + +namespace Shiboken { +namespace Conversions { + +void init() +{ + static SbkConverter* primitiveTypeConverters[] = { + Primitive::createConverter(), + Primitive::createConverter(), + Primitive::createConverter(), + Primitive::createConverter(), + Primitive::createConverter(), + Primitive::createConverter(), + Primitive::createConverter(), + Primitive::createConverter(), + Primitive::createConverter(), + Primitive::createConverter(), + Primitive::createConverter(), + Primitive::createConverter(), + Primitive::createConverter(), + Primitive::createConverter(), + Primitive::createConverter(), + Primitive::createConverter(), + Primitive::createConverter() + }; + PrimitiveTypeConverters = primitiveTypeConverters; + + assert(converters.empty()); + converters.set_empty_key(""); + converters.set_deleted_key("?"); + converters["PY_LONG_LONG"] = primitiveTypeConverters[SBK_PY_LONG_LONG_IDX]; + converters["bool"] = primitiveTypeConverters[SBK_BOOL_IDX_1]; + converters["char"] = primitiveTypeConverters[SBK_CHAR_IDX]; + converters["const char *"] = primitiveTypeConverters[SBK_CONSTCHARPTR_IDX]; + converters["double"] = primitiveTypeConverters[SBK_DOUBLE_IDX]; + converters["float"] = primitiveTypeConverters[SBK_FLOAT_IDX]; + converters["int"] = primitiveTypeConverters[SBK_INT_IDX]; + converters["long"] = primitiveTypeConverters[SBK_LONG_IDX]; + converters["short"] = primitiveTypeConverters[SBK_SHORT_IDX]; + converters["signed char"] = primitiveTypeConverters[SBK_SIGNEDCHAR_IDX]; + converters["std::string"] = primitiveTypeConverters[SBK_STD_STRING_IDX]; + converters["unsigned PY_LONG_LONG"] = primitiveTypeConverters[SBK_UNSIGNEDPY_LONG_LONG_IDX]; + converters["unsigned char"] = primitiveTypeConverters[SBK_UNSIGNEDCHAR_IDX]; + converters["unsigned int"] = primitiveTypeConverters[SBK_UNSIGNEDINT_IDX]; + converters["unsigned long"] = primitiveTypeConverters[SBK_UNSIGNEDLONG_IDX]; + converters["unsigned short"] = primitiveTypeConverters[SBK_UNSIGNEDSHORT_IDX]; + converters["void*"] = primitiveTypeConverters[SBK_VOIDPTR_IDX]; +} + +static SbkConverter* createConverterObject(PyTypeObject* type, + PythonToCppFunc toCppPointerConvFunc, + IsConvertibleToCppFunc toCppPointerCheckFunc, + CppToPythonFunc pointerToPythonFunc, + CppToPythonFunc copyToPythonFunc) +{ + SbkConverter* converter = new SbkConverter; + converter->pythonType = type; + + converter->pointerToPython = pointerToPythonFunc; + converter->copyToPython = copyToPythonFunc; + + if (toCppPointerCheckFunc && toCppPointerConvFunc) + converter->toCppPointerConversion = std::make_pair(toCppPointerCheckFunc, toCppPointerConvFunc); + converter->toCppConversions.clear(); + + return converter; +} + +SbkConverter* createConverter(SbkObjectType* type, + PythonToCppFunc toCppPointerConvFunc, + IsConvertibleToCppFunc toCppPointerCheckFunc, + CppToPythonFunc pointerToPythonFunc, + CppToPythonFunc copyToPythonFunc) +{ + SbkConverter *converter = + createConverterObject(reinterpret_cast(type), + toCppPointerConvFunc, toCppPointerCheckFunc, + pointerToPythonFunc, copyToPythonFunc); + type->d->converter = converter; + return converter; +} + +SbkConverter* createConverter(PyTypeObject* type, CppToPythonFunc toPythonFunc) +{ + return createConverterObject(type, 0, 0, 0, toPythonFunc); +} + +void deleteConverter(SbkConverter* converter) +{ + if (converter) { + converter->toCppConversions.clear(); + delete converter; + } +} + +void setCppPointerToPythonFunction(SbkConverter* converter, CppToPythonFunc pointerToPythonFunc) +{ + converter->pointerToPython = pointerToPythonFunc; +} + +void setPythonToCppPointerFunctions(SbkConverter* converter, + PythonToCppFunc toCppPointerConvFunc, + IsConvertibleToCppFunc toCppPointerCheckFunc) +{ + converter->toCppPointerConversion = std::make_pair(toCppPointerCheckFunc, toCppPointerConvFunc); +} + +void addPythonToCppValueConversion(SbkConverter* converter, + PythonToCppFunc pythonToCppFunc, + IsConvertibleToCppFunc isConvertibleToCppFunc) +{ + converter->toCppConversions.push_back(std::make_pair(isConvertibleToCppFunc, pythonToCppFunc)); +} +void addPythonToCppValueConversion(SbkObjectType* type, + PythonToCppFunc pythonToCppFunc, + IsConvertibleToCppFunc isConvertibleToCppFunc) +{ + addPythonToCppValueConversion(type->d->converter, pythonToCppFunc, isConvertibleToCppFunc); +} + +PyObject* pointerToPython(const SbkObjectType *type, const void *cppIn) +{ + return pointerToPython(type->d->converter, cppIn); +} + +PyObject* pointerToPython(const SbkConverter *converter, const void *cppIn) +{ + assert(converter); + if (!cppIn) + Py_RETURN_NONE; + if (!converter->pointerToPython) { + warning(PyExc_RuntimeWarning, 0, "pointerToPython(): SbkConverter::pointerToPython is null for \"%s\".", + converter->pythonType->tp_name); + Py_RETURN_NONE; + } + return converter->pointerToPython(cppIn); +} + +PyObject* referenceToPython(const SbkObjectType *type, const void *cppIn) +{ + return referenceToPython(type->d->converter, cppIn); +} + +PyObject* referenceToPython(const SbkConverter *converter, const void *cppIn) +{ + assert(cppIn); + + PyObject *pyOut = reinterpret_cast(BindingManager::instance().retrieveWrapper(cppIn)); + if (pyOut) { + Py_INCREF(pyOut); + return pyOut; + } + if (!converter->pointerToPython) { + warning(PyExc_RuntimeWarning, 0, "referenceToPython(): SbkConverter::pointerToPython is null for \"%s\".", + converter->pythonType->tp_name); + Py_RETURN_NONE; + } + return converter->pointerToPython(cppIn); +} + +static inline PyObject* CopyCppToPython(const SbkConverter *converter, const void *cppIn) +{ + if (!cppIn) + Py_RETURN_NONE; + if (!converter->copyToPython) { + warning(PyExc_RuntimeWarning, 0, "CopyCppToPython(): SbkConverter::copyToPython is null for \"%s\".", + converter->pythonType->tp_name); + Py_RETURN_NONE; + } + return converter->copyToPython(cppIn); +} +PyObject* copyToPython(const SbkObjectType *type, const void *cppIn) +{ + return CopyCppToPython(type->d->converter, cppIn); +} +PyObject* copyToPython(const SbkConverter *converter, const void *cppIn) +{ + return CopyCppToPython(converter, cppIn); +} + +PythonToCppFunc isPythonToCppPointerConvertible(const SbkObjectType *type, PyObject *pyIn) +{ + assert(pyIn); + return type->d->converter->toCppPointerConversion.first(pyIn); +} + +static inline PythonToCppFunc IsPythonToCppConvertible(const SbkConverter *converter, PyObject *pyIn) +{ + assert(pyIn); + const ToCppConversionList& convs = converter->toCppConversions; + for (ToCppConversionList::const_iterator conv = convs.begin(), end = convs.end(); conv != end; ++conv) { + PythonToCppFunc toCppFunc = 0; + if ((toCppFunc = (*conv).first(pyIn))) + return toCppFunc; + } + return 0; +} +PythonToCppFunc isPythonToCppValueConvertible(const SbkObjectType *type, PyObject *pyIn) +{ + return IsPythonToCppConvertible(type->d->converter, pyIn); +} +PythonToCppFunc isPythonToCppConvertible(const SbkConverter *converter, PyObject *pyIn) +{ + return IsPythonToCppConvertible(converter, pyIn); +} + +PythonToCppFunc isPythonToCppReferenceConvertible(const SbkObjectType *type, PyObject *pyIn) +{ + if (pyIn != Py_None) { + PythonToCppFunc toCpp = isPythonToCppPointerConvertible(type, pyIn); + if (toCpp) + return toCpp; + } + return isPythonToCppValueConvertible(type, pyIn); +} + +void nonePythonToCppNullPtr(PyObject*, void* cppOut) +{ + assert(cppOut); + *((void**)cppOut) = 0; +} + +void* cppPointer(PyTypeObject* desiredType, SbkObject* pyIn) +{ + assert(pyIn); + if (!ObjectType::checkType(desiredType)) + return pyIn; + SbkObjectType *inType = reinterpret_cast(Py_TYPE(pyIn)); + if (ObjectType::hasCast(inType)) + return ObjectType::cast(inType, pyIn, desiredType); + return Object::cppPointer(pyIn, desiredType); +} + +void pythonToCppPointer(SbkObjectType* type, PyObject* pyIn, void* cppOut) +{ + assert(type); + assert(pyIn); + assert(cppOut); + *reinterpret_cast(cppOut) = pyIn == Py_None + ? 0 + : cppPointer(reinterpret_cast(type), reinterpret_cast(pyIn)); +} + +void pythonToCppPointer(const SbkConverter *converter, PyObject *pyIn, void *cppOut) +{ + assert(converter); + assert(pyIn); + assert(cppOut); + *reinterpret_cast(cppOut) = pyIn == Py_None + ? 0 + : cppPointer(reinterpret_cast(converter->pythonType), reinterpret_cast(pyIn)); +} + +static void _pythonToCppCopy(const SbkConverter *converter, PyObject *pyIn, void *cppOut) +{ + assert(converter); + assert(pyIn); + assert(cppOut); + PythonToCppFunc toCpp = IsPythonToCppConvertible(converter, pyIn); + if (toCpp) + toCpp(pyIn, cppOut); +} + +void pythonToCppCopy(const SbkObjectType *type, PyObject *pyIn, void *cppOut) +{ + assert(type); + _pythonToCppCopy(type->d->converter, pyIn, cppOut); +} + +void pythonToCppCopy(const SbkConverter *converter, PyObject *pyIn, void *cppOut) +{ + _pythonToCppCopy(converter, pyIn, cppOut); +} + +bool isImplicitConversion(const 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) + return false; + + // Object Types doesn't have any kind of value conversion, + // only C++ pointer retrieval. + if (type->d->converter->toCppConversions.empty()) + return false; + + // The first conversion of the non-pointer conversion list is + // a Value Type's copy to C++ function, which is not an implicit + // conversion. + // Otherwise it must be one of the implicit conversions. + // 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(); + return toCppFunc != (*conv).second; +} + +void registerConverterName(SbkConverter* converter , const char* typeName) +{ + ConvertersMap::iterator iter = converters.find(typeName); + if (iter == converters.end()) + converters.insert(std::make_pair(typeName, converter)); +} + +SbkConverter* getConverter(const char* typeName) +{ + ConvertersMap::const_iterator it = converters.find(typeName); + if (it != converters.end()) + return it->second; + if (Py_VerboseFlag > 0) + SbkDbg() << "Can't find type resolver for type '" << typeName << "'."; + return 0; +} + +SbkConverter* primitiveTypeConverter(int index) +{ + return PrimitiveTypeConverters[index]; +} + +bool checkSequenceTypes(PyTypeObject* type, PyObject* pyIn) +{ + assert(type); + assert(pyIn); + if (!PySequence_Check(pyIn)) + return false; + const Py_ssize_t size = PySequence_Size(pyIn); + for (Py_ssize_t i = 0; i < size; ++i) { + if (!PyObject_TypeCheck(AutoDecRef(PySequence_GetItem(pyIn, i)), type)) + return false; + } + return true; +} +bool convertibleSequenceTypes(const SbkConverter *converter, PyObject *pyIn) +{ + assert(converter); + assert(pyIn); + if (!PySequence_Check(pyIn)) + return false; + const Py_ssize_t size = PySequence_Size(pyIn); + for (Py_ssize_t i = 0; i < size; ++i) { + if (!isPythonToCppConvertible(converter, AutoDecRef(PySequence_GetItem(pyIn, i)))) + return false; + } + return true; +} +bool convertibleSequenceTypes(const SbkObjectType *type, PyObject *pyIn) +{ + assert(type); + return convertibleSequenceTypes(type->d->converter, pyIn); +} + +bool checkPairTypes(PyTypeObject* firstType, PyTypeObject* secondType, PyObject* pyIn) +{ + assert(firstType); + assert(secondType); + assert(pyIn); + if (!PySequence_Check(pyIn)) + return false; + if (PySequence_Size(pyIn) != 2) + return false; + if (!PyObject_TypeCheck(AutoDecRef(PySequence_GetItem(pyIn, 0)), firstType)) + return false; + if (!PyObject_TypeCheck(AutoDecRef(PySequence_GetItem(pyIn, 1)), secondType)) + return false; + return true; +} +bool convertiblePairTypes(const SbkConverter *firstConverter, bool firstCheckExact, + const SbkConverter *secondConverter, bool secondCheckExact, + PyObject *pyIn) +{ + assert(firstConverter); + assert(secondConverter); + assert(pyIn); + if (!PySequence_Check(pyIn)) + return false; + if (PySequence_Size(pyIn) != 2) + return false; + AutoDecRef firstItem(PySequence_GetItem(pyIn, 0)); + if (firstCheckExact) { + if (!PyObject_TypeCheck(firstItem, firstConverter->pythonType)) + return false; + } else if (!isPythonToCppConvertible(firstConverter, firstItem)) { + return false; + } + AutoDecRef secondItem(PySequence_GetItem(pyIn, 1)); + if (secondCheckExact) { + if (!PyObject_TypeCheck(secondItem, secondConverter->pythonType)) + return false; + } else if (!isPythonToCppConvertible(secondConverter, secondItem)) { + return false; + } + + return true; +} + +bool checkDictTypes(PyTypeObject* keyType, PyTypeObject* valueType, PyObject* pyIn) +{ + assert(keyType); + assert(valueType); + assert(pyIn); + if (!PyDict_Check(pyIn)) + return false; + + PyObject* key; + PyObject* value; + Py_ssize_t pos = 0; + while (PyDict_Next(pyIn, &pos, &key, &value)) { + if (!PyObject_TypeCheck(key, keyType)) + return false; + if (!PyObject_TypeCheck(value, valueType)) + return false; + } + return true; +} + +bool convertibleDictTypes(const SbkConverter * keyConverter, bool keyCheckExact, const SbkConverter *valueConverter, + bool valueCheckExact, PyObject *pyIn) +{ + assert(keyConverter); + assert(valueConverter); + assert(pyIn); + if (!PyDict_Check(pyIn)) + return false; + PyObject* key; + PyObject* value; + Py_ssize_t pos = 0; + while (PyDict_Next(pyIn, &pos, &key, &value)) { + if (keyCheckExact) { + if (!PyObject_TypeCheck(key, keyConverter->pythonType)) + return false; + } else if (!isPythonToCppConvertible(keyConverter, key)) { + return false; + } + if (valueCheckExact) { + if (!PyObject_TypeCheck(value, valueConverter->pythonType)) + return false; + } else if (!isPythonToCppConvertible(valueConverter, value)) { + return false; + } + } + return true; +} + +PyTypeObject* getPythonTypeObject(const SbkConverter *converter) +{ + if (converter) + return converter->pythonType; + return 0; +} + +PyTypeObject* getPythonTypeObject(const char* typeName) +{ + return getPythonTypeObject(getConverter(typeName)); +} + +bool pythonTypeIsValueType(const SbkConverter *converter) +{ + assert(converter); + return converter->pointerToPython && converter->copyToPython; +} + +bool pythonTypeIsObjectType(const SbkConverter *converter) +{ + return converter->pointerToPython && !converter->copyToPython; +} + +bool pythonTypeIsWrapperType(const SbkConverter *converter) +{ + return converter->pointerToPython; +} + +SpecificConverter::SpecificConverter(const char* typeName) + : m_type(InvalidConversion) +{ + m_converter = getConverter(typeName); + if (!m_converter) + return; + const Py_ssize_t len = strlen(typeName); + char lastChar = typeName[len -1]; + if (lastChar == '&') { + m_type = ReferenceConversion; + } else if (lastChar == '*' || pythonTypeIsObjectType(m_converter)) { + m_type = PointerConversion; + } else { + m_type = CopyConversion; + } +} + +PyObject* SpecificConverter::toPython(const void* cppIn) +{ + switch (m_type) { + case CopyConversion: + return copyToPython(m_converter, cppIn); + case PointerConversion: + return pointerToPython(m_converter, *((const void**)cppIn)); + case ReferenceConversion: + return referenceToPython(m_converter, cppIn); + default: + PyErr_SetString(PyExc_RuntimeError, "tried to use invalid converter in 'C++ to Python' conversion"); + } + return 0; +} + +void SpecificConverter::toCpp(PyObject* pyIn, void* cppOut) +{ + switch (m_type) { + case CopyConversion: + pythonToCppCopy(m_converter, pyIn, cppOut); + break; + case PointerConversion: + pythonToCppPointer(m_converter, pyIn, cppOut); + break; + case ReferenceConversion: + pythonToCppPointer(m_converter, pyIn, &cppOut); + break; + default: + PyErr_SetString(PyExc_RuntimeError, "tried to use invalid converter in 'Python to C++' conversion"); + } +} + +} } // namespace Shiboken::Conversions diff --git a/sources/shiboken2/libshiboken/sbkconverter.h b/sources/shiboken2/libshiboken/sbkconverter.h new file mode 100644 index 000000000..7489b930d --- /dev/null +++ b/sources/shiboken2/libshiboken/sbkconverter.h @@ -0,0 +1,367 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef SBK_CONVERTER_H +#define SBK_CONVERTER_H + +#include "sbkpython.h" +#include "shibokenmacros.h" + +#include +#include + +struct SbkObject; +struct SbkObjectType; + +/** + * This is a convenience macro identical to Python's PyObject_TypeCheck, + * except that the arguments have swapped places, for the great convenience + * of generator. + */ +#define SbkObject_TypeCheck(tp, ob) \ + (Py_TYPE(ob) == (tp) || PyType_IsSubtype(Py_TYPE(ob), (tp))) + +extern "C" +{ + +/** + * SbkConverter is used to perform type conversions from C++ + * to Python and vice-versa;.and it is also used for type checking. + * SbkConverter is a private structure that must be accessed + * using the functions provided by the converter API. + */ +struct SbkConverter; + +/** + * Given a void pointer to a C++ object, this function must return + * the proper Python object. It may be either an existing wrapper + * for the C++ object, or a newly create one. Or even the Python + * equivalent of the C++ value passed in the argument. + * + * C++ -> Python + */ +typedef PyObject* (*CppToPythonFunc)(const void*); + +/** + * This function converts a Python object to a C++ value, it may be + * a pointer, value, class, container or primitive type, passed via + * a void pointer, that will be cast properly inside the function. + * This function is usually returned by an IsConvertibleToCppFunc + * function, or obtained knowing the type of the Python object input, + * thus it will not check the Python object type, and will expect + * the void pointer to be pointing to a proper variable. + * + * Python -> C++ + */ +typedef void (*PythonToCppFunc)(PyObject*,void*); + +/** + * Checks if the Python object passed in the argument is convertible to a + * C++ type defined inside the function, it returns the converter function + * that will transform a Python argument into a C++ value. + * It returns NULL if the Python object is not convertible to the C++ type + * that the function represents. + * + * Python -> C++ ? + */ +typedef PythonToCppFunc (*IsConvertibleToCppFunc)(PyObject*); + +} // extern "C" + +namespace Shiboken { +namespace Conversions { + + +class LIBSHIBOKEN_API SpecificConverter +{ +public: + enum Type + { + InvalidConversion, + CopyConversion, + PointerConversion, + ReferenceConversion + }; + + explicit SpecificConverter(const char* typeName); + + inline SbkConverter* converter() { return m_converter; } + inline operator SbkConverter*() const { return m_converter; } + + inline bool isValid() { return m_type != InvalidConversion; } + inline operator bool() const { return m_type != InvalidConversion; } + + inline Type conversionType() { return m_type; } + + PyObject* toPython(const void* cppIn); + void toCpp(PyObject* pyIn, void* cppOut); +private: + SbkConverter* m_converter; + Type m_type; +}; + + +/** + * Creates a converter for a wrapper type. + * \param type A Shiboken.ObjectType that will receive the new converter. + * \param toCppPointerConvFunc Function to retrieve the C++ pointer held by a Python wrapper. + * \param toCppPointerCheckFunc Check and return the retriever function of the C++ pointer held by a Python wrapper. + * \param pointerToPythonFunc Function to convert a C++ object to a Python \p type wrapper, keeping their identity. + * \param copyToPythonFunc Function to convert a C++ object to a Python \p type, copying the object. + * \returns The new converter referred by the wrapper \p type. + */ +LIBSHIBOKEN_API SbkConverter* createConverter(SbkObjectType* type, + PythonToCppFunc toCppPointerConvFunc, + IsConvertibleToCppFunc toCppPointerCheckFunc, + CppToPythonFunc pointerToPythonFunc, + CppToPythonFunc copyToPythonFunc = 0); + +/** + * Creates a converter for a non wrapper type (primitive or container type). + * \param type Python type representing to the new converter. + * \param toPythonFunc Function to convert a C++ object to a Python \p type. + * \returns A new type converter. + */ +LIBSHIBOKEN_API SbkConverter* createConverter(PyTypeObject* type, CppToPythonFunc toPythonFunc); + +LIBSHIBOKEN_API void deleteConverter(SbkConverter* converter); + +/// Sets the Python object to C++ pointer conversion function. +LIBSHIBOKEN_API void setCppPointerToPythonFunction(SbkConverter* converter, CppToPythonFunc pointerToPythonFunc); + +/// Sets the C++ pointer to Python object conversion functions. +LIBSHIBOKEN_API void setPythonToCppPointerFunctions(SbkConverter* converter, + PythonToCppFunc toCppPointerConvFunc, + IsConvertibleToCppFunc toCppPointerCheckFunc); + +/** + * Adds a new conversion of a Python object to a C++ value. + * This is used in copy and implicit conversions. + */ +LIBSHIBOKEN_API void addPythonToCppValueConversion(SbkConverter* converter, + PythonToCppFunc pythonToCppFunc, + IsConvertibleToCppFunc isConvertibleToCppFunc); +LIBSHIBOKEN_API void addPythonToCppValueConversion(SbkObjectType* type, + PythonToCppFunc pythonToCppFunc, + IsConvertibleToCppFunc isConvertibleToCppFunc); + +// C++ -> Python --------------------------------------------------------------------------- + +/** + * Retrieves the Python wrapper object for the given \p cppIn C++ pointer object. + * This function is used only for Value and Object Types. + * Example usage: + * TYPE* var; + * PyObject* pyVar = pointerToPython(SBKTYPE, &var); + */ +LIBSHIBOKEN_API PyObject* pointerToPython(const SbkObjectType *type, const void *cppIn); +LIBSHIBOKEN_API PyObject* pointerToPython(const SbkConverter *converter, const void *cppIn); + +/** + * For the given \p cppIn C++ reference it returns the Python wrapper object, + * always for Object Types, and when they already exist for reference types; + * for when the latter doesn't have an existing wrapper type, the C++ object + * is copied to Python. + * Example usage: + * TYPE& var = SOMETHING; + * PyObject* pyVar = referenceToPython(SBKTYPE, &var); + */ +LIBSHIBOKEN_API PyObject* referenceToPython(const SbkObjectType *type, const void *cppIn); +LIBSHIBOKEN_API PyObject* referenceToPython(const SbkConverter *converter, const void *cppIn); + +/** + * Retrieves the Python wrapper object for the given C++ value pointed by \p cppIn. + * This function is used only for Value Types. + * Example usage: + * TYPE var; + * PyObject* pyVar = copyToPython(SBKTYPE, &var); + */ +LIBSHIBOKEN_API PyObject* copyToPython(const SbkObjectType *type, const void *cppIn); +LIBSHIBOKEN_API PyObject* copyToPython(const SbkConverter *converter, const void *cppIn); + +// Python -> C++ --------------------------------------------------------------------------- + +/** + * 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); + +/** + * Returns a Python to C++ conversion function if the Python object is convertible to a C++ value. + * The resulting converter function will create a copy of the Python object in C++, or implicitly + * 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); + +/** + * Returns a Python to C++ conversion function if the Python object is convertible to a C++ reference. + * The resulting converter function will return the underlying C++ object held by the Python wrapper, + * 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); + +/// This is the same as isPythonToCppValueConvertible function. +LIBSHIBOKEN_API PythonToCppFunc isPythonToCppConvertible(const SbkConverter *converter, PyObject *pyIn); + +/** + * Returns the C++ pointer for the \p pyIn object cast to the type passed via \p desiredType. + * It differs from Shiboken::Object::cppPointer because it casts the pointer to a proper + * memory offset depending on the desired type. + */ +LIBSHIBOKEN_API void* cppPointer(PyTypeObject* desiredType, SbkObject* pyIn); + +/// Converts a Python object \p pyIn to C++ and stores the result in the C++ pointer passed in \p cppOut. +LIBSHIBOKEN_API void pythonToCppPointer(SbkObjectType* type, PyObject* pyIn, void* cppOut); +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(const SbkConverter *converter, PyObject *pyIn, void *cppOut); + +/** + * Helper function returned by generated convertible checking functions + * that returns a C++ NULL when the input Python object is None. + */ +LIBSHIBOKEN_API void nonePythonToCppNullPtr(PyObject*, void* cppOut); + +/** + * Returns true if the \p toCpp function passed is an implicit conversion of Python \p type. + * 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); + +/// Registers a converter with a type name that may be used to retrieve the converter. +LIBSHIBOKEN_API void registerConverterName(SbkConverter* converter, const char* typeName); + +/// Returns the converter for a given type name, or NULL if it wasn't registered before. +LIBSHIBOKEN_API SbkConverter* getConverter(const char* typeName); + +/// Returns the converter for a primitive type. +LIBSHIBOKEN_API SbkConverter* primitiveTypeConverter(int index); + +/// Returns true if a Python sequence is comprised of objects of the given \p type. +LIBSHIBOKEN_API bool checkSequenceTypes(PyTypeObject* type, PyObject* pyIn); + +/// Returns true if a Python sequence is comprised of objects of a type convertible to the one represented by the given \p converter. +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); + +/// Returns true if a Python sequence can be converted to a C++ pair. +LIBSHIBOKEN_API bool checkPairTypes(PyTypeObject* firstType, PyTypeObject* secondType, PyObject* pyIn); + +/// Returns true if a Python sequence can be converted to a C++ pair. +LIBSHIBOKEN_API bool convertiblePairTypes(const SbkConverter *firstConverter, bool firstCheckExact, + const SbkConverter *secondConverter, bool secondCheckExact, + PyObject *pyIn); + +/// Returns true if a Python dictionary can be converted to a C++ hash or map. +LIBSHIBOKEN_API bool checkDictTypes(PyTypeObject* keyType, PyTypeObject* valueType, PyObject* pyIn); + +/// Returns true if a Python dictionary can be converted to a C++ hash or map. +LIBSHIBOKEN_API bool convertibleDictTypes(const SbkConverter *keyConverter, bool keyCheckExact, + const SbkConverter *valueConverter, bool valueCheckExact, + PyObject *pyIn); + +/// Returns the Python type object associated with the given \p converter. +LIBSHIBOKEN_API PyTypeObject* getPythonTypeObject(const SbkConverter *converter); + +/// Returns the Python type object for the given \p typeName. +LIBSHIBOKEN_API PyTypeObject* getPythonTypeObject(const char* typeName); + +/// Returns true if the Python type associated with the converter is a value type. +LIBSHIBOKEN_API bool pythonTypeIsValueType(const SbkConverter *converter); + +/// Returns true if the Python type associated with the converter is an object type. +LIBSHIBOKEN_API bool pythonTypeIsObjectType(const SbkConverter *converter); + +/// Returns true if the Python type associated with the converter is a wrapper type. +LIBSHIBOKEN_API bool pythonTypeIsWrapperType(const SbkConverter *converter); + +#define SBK_PY_LONG_LONG_IDX 0 +// Qt5: name collision in QtCore after QBool is replaced by bool +#define SBK_BOOL_IDX_1 1 +#define SBK_CHAR_IDX 2 +#define SBK_CONSTCHARPTR_IDX 3 +#define SBK_DOUBLE_IDX 4 +#define SBK_FLOAT_IDX 5 +#define SBK_INT_IDX 6 +#define SBK_SIGNEDINT_IDX 6 +#define SBK_LONG_IDX 7 +#define SBK_SHORT_IDX 8 +#define SBK_SIGNEDCHAR_IDX 9 +#define SBK_STD_STRING_IDX 10 +#define SBK_UNSIGNEDPY_LONG_LONG_IDX 11 +#define SBK_UNSIGNEDCHAR_IDX 12 +#define SBK_UNSIGNEDINT_IDX 13 +#define SBK_UNSIGNEDLONG_IDX 14 +#define SBK_UNSIGNEDSHORT_IDX 15 +#define SBK_VOIDPTR_IDX 16 + +template SbkConverter* PrimitiveTypeConverter() { return 0; } +template<> inline SbkConverter* PrimitiveTypeConverter() { return primitiveTypeConverter(SBK_PY_LONG_LONG_IDX); } +template<> inline SbkConverter* PrimitiveTypeConverter() { return primitiveTypeConverter(SBK_BOOL_IDX_1); } +template<> inline SbkConverter* PrimitiveTypeConverter() { return primitiveTypeConverter(SBK_CHAR_IDX); } +template<> inline SbkConverter* PrimitiveTypeConverter() { return primitiveTypeConverter(SBK_CONSTCHARPTR_IDX); } +template<> inline SbkConverter* PrimitiveTypeConverter() { return primitiveTypeConverter(SBK_DOUBLE_IDX); } +template<> inline SbkConverter* PrimitiveTypeConverter() { return primitiveTypeConverter(SBK_FLOAT_IDX); } +template<> inline SbkConverter* PrimitiveTypeConverter() { return primitiveTypeConverter(SBK_INT_IDX); } +template<> inline SbkConverter* PrimitiveTypeConverter() { return primitiveTypeConverter(SBK_LONG_IDX); } +template<> inline SbkConverter* PrimitiveTypeConverter() { return primitiveTypeConverter(SBK_SHORT_IDX); } +template<> inline SbkConverter* PrimitiveTypeConverter() { return primitiveTypeConverter(SBK_SIGNEDCHAR_IDX); } +template<> inline SbkConverter* PrimitiveTypeConverter() { return primitiveTypeConverter(SBK_STD_STRING_IDX); } +template<> inline SbkConverter* PrimitiveTypeConverter() { return primitiveTypeConverter(SBK_UNSIGNEDPY_LONG_LONG_IDX); } +template<> inline SbkConverter* PrimitiveTypeConverter() { return primitiveTypeConverter(SBK_UNSIGNEDCHAR_IDX); } +template<> inline SbkConverter* PrimitiveTypeConverter() { return primitiveTypeConverter(SBK_UNSIGNEDINT_IDX); } +template<> inline SbkConverter* PrimitiveTypeConverter() { return primitiveTypeConverter(SBK_UNSIGNEDLONG_IDX); } +template<> inline SbkConverter* PrimitiveTypeConverter() { return primitiveTypeConverter(SBK_UNSIGNEDSHORT_IDX); } +template<> inline SbkConverter* PrimitiveTypeConverter() { return primitiveTypeConverter(SBK_VOIDPTR_IDX); } + +} } // namespace Shiboken::Conversions + +struct _SbkGenericType { PyHeapTypeObject super; SbkConverter** converter; }; +#define SBK_CONVERTER(pyType) (*reinterpret_cast<_SbkGenericType*>(pyType)->converter) + + +#endif // SBK_CONVERTER_H diff --git a/sources/shiboken2/libshiboken/sbkconverter_p.h b/sources/shiboken2/libshiboken/sbkconverter_p.h new file mode 100644 index 000000000..b38561780 --- /dev/null +++ b/sources/shiboken2/libshiboken/sbkconverter_p.h @@ -0,0 +1,574 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef SBK_CONVERTER_P_H +#define SBK_CONVERTER_P_H + +#include "sbkpython.h" +#include "sbkconverter.h" +#include "sbkstring.h" +#include +#include +#include +#include +#include + +#include "sbkdbg.h" + +extern "C" +{ + +typedef std::pair ToCppConversion; +typedef std::list ToCppConversionList; + +/** + * \internal + * Private structure of SbkConverter. + */ +struct SbkConverter +{ + /** + * Python type associated with this converter. If the type is a Shiboken + * wrapper, then it must be a SbkObjectType; otherwise it will be the + * Python type to which the C++ value will be converted (note that the + * C++ type could be produced from various Python types). + */ + PyTypeObject* pythonType; + /** + * This function converts a C++ object to a Python object of the type + * indicated in pythonType. The identity of the C++ object is kept, + * because it looks for an already existing Python wrapper associated + * with the C++ instance. + * It is used to convert C++ pointers and references to Python objects. + */ + CppToPythonFunc pointerToPython; + /** + * This function converts a C++ object to a Python object of the type + * indicated in pythonType. The identity of the object is not kept, + * because a new instance of the C++ object is created. + * It is used to convert objects passed by value, or reference, if said + * reference can't be traced to an object that already has a Python + * wrapper assigned for it. + */ + CppToPythonFunc copyToPython; + /** + * This is a special case of a Python to C++ conversion. It returns + * the underlying C++ pointer of a Python wrapper passed as parameter + * or NULL if the Python object is a None value. + * It comes separated from the other toCppConversions because if you + * have a Python object representing a Value Type the type checking + * for both ValueType and ValueType* would be the same, thus the first + * check would be true and the second would never be reached. + */ + ToCppConversion toCppPointerConversion; + /** + * This is a list of type checking functions that return the + * proper Python to C++ conversion function, for the given Python + * object. + * For Object Types, that never have implicit conversions, this + * list is always empty. + */ + ToCppConversionList toCppConversions; +}; + +} // extern "C" + +template +struct OverFlowCheckerBase { + static void formatOverFlowMessage(const MaxLimitType& value, + const std::string *valueAsString = 0) + { + std::ostringstream str; + str << "libshiboken: Overflow: Value "; + if (valueAsString != 0 && !valueAsString->empty()) + str << *valueAsString; + else + str << value; + str << " exceeds limits of type " + << " [" << (isSigned ? "signed" : "unsigned") + << "] \"" << typeid(T).name() + << "\" (" << sizeof(T) << "bytes)."; + const std::string message = str.str(); + PyErr_WarnEx(PyExc_RuntimeWarning, message.c_str(), 0); + } + + // Checks if an overflow occurred inside Python code. + // Precondition: use after calls like PyLong_AsLongLong or PyLong_AsUnsignedLongLong. + // Postcondition: if error ocurred, sets the string reference to the string representation of + // the passed value. + static bool checkForInternalPyOverflow(PyObject *pyIn, std::string &valueAsString) + { + if (PyErr_Occurred()) { + PyErr_Print(); + PyObject *stringRepresentation = PyObject_Str(pyIn); + const char *cString = Shiboken::String::toCString(stringRepresentation); + valueAsString.assign(cString); + Py_DECREF(stringRepresentation); + return true; + } + return false; + } +}; + +// Helper template for checking if a value overflows when cast to type T. +// The MaxLimitType size is usually >= than type T size, so that it can still represent values that +// can't be stored in T (unless the types are of course the same). +// TLDR: MaxLimitType is either long long or unsigned long long. +template::is_signed > +struct OverFlowChecker; + +template +struct OverFlowChecker : + public OverFlowCheckerBase { + static bool check(const MaxLimitType& value, PyObject *pyIn) + { + std::string valueAsString; + const bool isOverflow = + OverFlowChecker::checkForInternalPyOverflow(pyIn, valueAsString) + || value < std::numeric_limits::min() + || value > std::numeric_limits::max(); + if (isOverflow) + OverFlowChecker::formatOverFlowMessage(value, &valueAsString); + return isOverflow; + } +}; + +template +struct OverFlowChecker + : public OverFlowCheckerBase { + static bool check(const MaxLimitType& value, PyObject *pyIn) + { + std::string valueAsString; + const bool isOverflow = + OverFlowChecker::checkForInternalPyOverflow(pyIn, valueAsString) + || value < 0 + || static_cast(value) > std::numeric_limits::max(); + if (isOverflow) + OverFlowChecker::formatOverFlowMessage(value, &valueAsString); + return isOverflow; + } +}; +template<> +struct OverFlowChecker : + public OverFlowCheckerBase { + static bool check(const PY_LONG_LONG &value, PyObject *pyIn) { + std::string valueAsString; + const bool isOverflow = checkForInternalPyOverflow(pyIn, valueAsString); + if (isOverflow) + OverFlowChecker::formatOverFlowMessage(value, &valueAsString); + return isOverflow; + + } +}; +template<> +struct OverFlowChecker { + static bool check(const double &, PyObject *) { return false; } +}; +template<> +struct OverFlowChecker : + public OverFlowCheckerBase { + static bool check(const double& value, PyObject *) + { + const bool result = value < std::numeric_limits::min() + || value > std::numeric_limits::max(); + if (result) + formatOverFlowMessage(value); + return result; + } +}; + +// Basic primitive type converters --------------------------------------------------------- +template PyTypeObject* SbkType() { return 0; } +template<> inline PyTypeObject* SbkType() { return &PyLong_Type; } +template<> inline PyTypeObject* SbkType() { return &PyBool_Type; } +template<> inline PyTypeObject* SbkType() { return &PyInt_Type; } +template<> inline PyTypeObject* SbkType() { return &PyString_Type; } +template<> inline PyTypeObject* SbkType() { return &PyFloat_Type; } +template<> inline PyTypeObject* SbkType() { return &PyFloat_Type; } +template<> inline PyTypeObject* SbkType() { return &PyInt_Type; } +template<> inline PyTypeObject* SbkType() { return &PyLong_Type; } +template<> inline PyTypeObject* SbkType() { return &PyInt_Type; } +template<> inline PyTypeObject* SbkType() { return &PyInt_Type; } +template<> inline PyTypeObject* SbkType() { return &PyLong_Type; } +template<> inline PyTypeObject* SbkType() { return &PyInt_Type; } +template<> inline PyTypeObject* SbkType() { return &PyLong_Type; } +template<> inline PyTypeObject* SbkType() { return &PyLong_Type; } +template<> inline PyTypeObject* SbkType() { return &PyInt_Type; } + +template struct Primitive {}; + +template +struct OnePrimitive +{ + static PyObject* toPython(const void*) { return 0; } + static PythonToCppFunc isConvertible(PyObject*) { return 0; } + static void toCpp(PyObject*, void*) {} + static SbkConverter* createConverter() + { + SbkConverter* converter = Shiboken::Conversions::createConverter(SbkType(), Primitive::toPython); + Shiboken::Conversions::addPythonToCppValueConversion(converter, Primitive::toCpp, Primitive::isConvertible); + return converter; + } +}; +template +struct TwoPrimitive : OnePrimitive +{ + static PythonToCppFunc isOtherConvertible(PyObject*) { return 0; } + static void otherToCpp(PyObject*, void*) {} + static SbkConverter* createConverter() + { + SbkConverter* converter = OnePrimitive::createConverter(); + Shiboken::Conversions::addPythonToCppValueConversion(converter, Primitive::otherToCpp, Primitive::isOtherConvertible); + return converter; + } +}; + +// Integers -------------------------------------------------------------------------------- + +template +struct IntPrimitive : TwoPrimitive +{ + static PyObject* toPython(const void* cppIn) + { + return PyInt_FromLong(*reinterpret_cast(cppIn)); + } + static void toCpp(PyObject* pyIn, void* cppOut) + { + double result = PyFloat_AS_DOUBLE(pyIn); + // If cast to long directly it could overflow silently. + if (OverFlowChecker::check(result, pyIn)) + PyErr_SetObject(PyExc_OverflowError, 0); + *reinterpret_cast(cppOut) = static_cast(result); + } + static PythonToCppFunc isConvertible(PyObject* pyIn) + { + if (PyFloat_Check(pyIn)) + return toCpp; + return 0; + } + static void otherToCpp(PyObject* pyIn, void* cppOut) + { + PY_LONG_LONG result = PyLong_AsLongLong(pyIn); + if (OverFlowChecker::check(result, pyIn)) + PyErr_SetObject(PyExc_OverflowError, 0); + *reinterpret_cast(cppOut) = static_cast(result); + } + static PythonToCppFunc isOtherConvertible(PyObject* pyIn) + { + if (SbkNumber_Check(pyIn)) + return otherToCpp; + return 0; + } +}; +template <> struct Primitive : IntPrimitive {}; +template <> struct Primitive : IntPrimitive {}; +template <> struct Primitive : IntPrimitive {}; +template <> struct Primitive : IntPrimitive {}; + +// Unsigned Long Integers ------------------------------------------------------------------ + +template +struct UnsignedLongPrimitive : IntPrimitive +{ + static PyObject* toPython(const void* cppIn) + { + return PyLong_FromUnsignedLong(*reinterpret_cast(cppIn)); + } +}; +template <> struct Primitive : UnsignedLongPrimitive {}; +template <> struct Primitive : UnsignedLongPrimitive {}; + +// Big integers ---------------------------------------------------------------------------- + +template <> +struct Primitive : OnePrimitive +{ + static PyObject* toPython(const void* cppIn) + { + return PyLong_FromLongLong(*((PY_LONG_LONG*)cppIn)); + } + static void toCpp(PyObject* pyIn, void* cppOut) + { + PY_LONG_LONG result = PyLong_AsLongLong(pyIn); + if (OverFlowChecker::check(result, pyIn)) + PyErr_SetObject(PyExc_OverflowError, 0); + *reinterpret_cast(cppOut) = result; + } + static PythonToCppFunc isConvertible(PyObject* pyIn) + { + if (SbkNumber_Check(pyIn)) + return toCpp; + return 0; + } +}; + +template <> +struct Primitive : OnePrimitive +{ + static PyObject* toPython(const void* cppIn) + { + return PyLong_FromUnsignedLongLong(*((unsigned PY_LONG_LONG*)cppIn)); + } + static void toCpp(PyObject* pyIn, void* cppOut) + { +#if PY_MAJOR_VERSION >= 3 + if (PyLong_Check(pyIn)) { + unsigned PY_LONG_LONG result = PyLong_AsUnsignedLongLong(pyIn); + if (OverFlowChecker::check(result, pyIn)) + PyErr_SetObject(PyExc_OverflowError, 0); + *reinterpret_cast(cppOut) = result; + } + else { + PyErr_SetString(PyExc_TypeError, "Invalid type for unsigned long long conversion"); + } +#else + if (PyInt_Check(pyIn)) { + long result = PyInt_AsLong(pyIn); + if (OverFlowChecker::check(result, pyIn)) + PyErr_SetObject(PyExc_OverflowError, 0); + *reinterpret_cast(cppOut) = + static_cast(result); + } else if (PyLong_Check(pyIn)) { + unsigned PY_LONG_LONG result = PyLong_AsUnsignedLongLong(pyIn); + if (OverFlowChecker::check(result, pyIn)) + PyErr_SetObject(PyExc_OverflowError, 0); + *reinterpret_cast(cppOut) = result; + } else { + PyErr_SetString(PyExc_TypeError, "Invalid type for unsigned long long conversion"); + } +#endif // Python 2 + } + static PythonToCppFunc isConvertible(PyObject* pyIn) + { + if (SbkNumber_Check(pyIn)) + return toCpp; + return 0; + } +}; + +// Floating point -------------------------------------------------------------------------- + +template +struct FloatPrimitive : TwoPrimitive +{ + static PyObject* toPython(const void* cppIn) + { + return PyFloat_FromDouble(*reinterpret_cast(cppIn)); + } + static void toCpp(PyObject* pyIn, void* cppOut) + { + *reinterpret_cast(cppOut) = FLOAT(PyLong_AsLong(pyIn)); + } + static PythonToCppFunc isConvertible(PyObject* pyIn) + { + if (PyInt_Check(pyIn) || PyLong_Check(pyIn)) + return toCpp; + return 0; + } + static void otherToCpp(PyObject* pyIn, void* cppOut) + { + *reinterpret_cast(cppOut) = FLOAT(PyFloat_AsDouble(pyIn)); + } + static PythonToCppFunc isOtherConvertible(PyObject* pyIn) + { + if (SbkNumber_Check(pyIn)) + return otherToCpp; + return 0; + } +}; +template <> struct Primitive : FloatPrimitive {}; +template <> struct Primitive : FloatPrimitive {}; + +// Boolean --------------------------------------------------------------------------------- + +template <> +struct Primitive : OnePrimitive +{ + static PyObject* toPython(const void* cppIn) + { + return PyBool_FromLong(*reinterpret_cast(cppIn)); + } + static PythonToCppFunc isConvertible(PyObject* pyIn) + { + if (SbkNumber_Check(pyIn)) + return toCpp; + return 0; + } + static void toCpp(PyObject* pyIn, void* cppOut) + { + *reinterpret_cast(cppOut) = PyInt_AS_LONG(pyIn) != 0; + } +}; + +// Characters ------------------------------------------------------------------------------ + +template +struct CharPrimitive : IntPrimitive +{ + static void toCpp(PyObject* pyIn, void* cppOut) + { + *reinterpret_cast(cppOut) = CHAR(Shiboken::String::toCString(pyIn)[0]); + } + static PythonToCppFunc isConvertible(PyObject* pyIn) + { + if (Shiboken::String::checkChar(pyIn)) + return toCpp; + return 0; + } + static void otherToCpp(PyObject* pyIn, void* cppOut) + { + PY_LONG_LONG result = PyLong_AsLongLong(pyIn); + if (OverFlowChecker::check(result, pyIn)) + PyErr_SetObject(PyExc_OverflowError, 0); + *reinterpret_cast(cppOut) = CHAR(result); + } + static PythonToCppFunc isOtherConvertible(PyObject* pyIn) + { + if (SbkNumber_Check(pyIn)) + return otherToCpp; + return 0; + } + static SbkConverter* createConverter() + { + SbkConverter* converter = IntPrimitive::createConverter(); + Shiboken::Conversions::addPythonToCppValueConversion(converter, CharPrimitive::otherToCpp, CharPrimitive::isOtherConvertible); + return converter; + } + +}; +template <> struct Primitive : CharPrimitive {}; +template <> struct Primitive : CharPrimitive {}; +template <> struct Primitive : CharPrimitive { + using CharPrimitive::toPython; + static PyObject* toPython(const void* cppIn) { + return Shiboken::String::fromCString((const char*)cppIn, 1); + } +}; + + + +// Strings --------------------------------------------------------------------------------- + +template <> +struct Primitive : TwoPrimitive +{ + static PyObject* toPython(const void* cppIn) + { + if (!cppIn) + Py_RETURN_NONE; + return Shiboken::String::fromCString((const char*)cppIn); + } + static void toCpp(PyObject *, void *cppOut) + { + *((const char**)cppOut) = 0; + } + static PythonToCppFunc isConvertible(PyObject* pyIn) + { + if (pyIn == Py_None) + return toCpp; + return 0; + } + static void otherToCpp(PyObject* pyIn, void* cppOut) + { + *((const char**)cppOut) = (const char*) Shiboken::String::toCString(pyIn); + } + static PythonToCppFunc isOtherConvertible(PyObject* pyIn) + { + if (Shiboken::String::check(pyIn)) + return otherToCpp; + return 0; + } +}; + +template <> +struct Primitive : TwoPrimitive +{ + static PyObject* toPython(const void* cppIn) + { + return Shiboken::String::fromCString(((std::string*)cppIn)->c_str()); + } + static void toCpp(PyObject *, void *cppOut) + { + *((std::string*)cppOut) = std::string(); + } + static PythonToCppFunc isConvertible(PyObject* pyIn) + { + if (pyIn == Py_None) + return toCpp; + return 0; + } + static void otherToCpp(PyObject* pyIn, void* cppOut) + { + *((std::string*)cppOut) = Shiboken::String::toCString(pyIn); + } + static PythonToCppFunc isOtherConvertible(PyObject* pyIn) + { + if (Shiboken::String::check(pyIn)) + return otherToCpp; + return 0; + } +}; + +// Void pointer ---------------------------------------------------------------------------- + +template <> +struct Primitive : OnePrimitive +{ + static PyObject* toPython(const void* cppIn) + { + SbkDbg() << cppIn; + if (!cppIn) + Py_RETURN_NONE; + PyObject *result = reinterpret_cast(const_cast(cppIn)); + Py_INCREF(result); + return result; + } + static void toCpp(PyObject* pyIn, void* cppOut) + { + SbkDbg() << pyIn; + *reinterpret_cast(cppOut) = pyIn; + } + static PythonToCppFunc isConvertible(PyObject *) + { + return toCpp; + } +}; + +#endif // SBK_CONVERTER_P_H diff --git a/sources/shiboken2/libshiboken/sbkdbg.h b/sources/shiboken2/libshiboken/sbkdbg.h new file mode 100644 index 000000000..937153adf --- /dev/null +++ b/sources/shiboken2/libshiboken/sbkdbg.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef SBKDBG_H +#define SBKDBG_H + +#include "sbkpython.h" +#include "basewrapper.h" +#include + +#ifndef NOCOLOR + #define COLOR_END "\033[0m" + #define COLOR_WHITE "\033[1;37m" + #define COLOR_YELLOW "\033[1;33m" + #define COLOR_GREEN "\033[0;32m" + #define COLOR_RED "\033[0;31m" +#else + #define COLOR_END "" + #define COLOR_WHITE "" + #define COLOR_YELLOW "" + #define COLOR_GREEN "" + #define COLOR_RED "" +#endif + +#ifndef NDEBUG + +class BaseLogger +{ +public: + BaseLogger(std::ostream& output, const char* function, const char* context) + : m_stream(output), m_function(function), m_context(context) {} + ~BaseLogger() + { + m_stream << std::endl; + } + std::ostream& operator()() { return m_stream; }; + template + std::ostream& operator<<(const T& t) + { + m_stream << '['; + if (m_context[0]) + m_stream << COLOR_GREEN << m_context << COLOR_END << "|"; + return m_stream << COLOR_WHITE << m_function << COLOR_END << "] " << t; + } +private: + std::ostream& m_stream; + const char* m_function; + const char* m_context; +}; + +inline std::ostream& operator<<(std::ostream& out, PyObject* obj) +{ + PyObject* repr = Shiboken::Object::isValid(obj, false) ? PyObject_Repr(obj) : 0; + if (repr) { +#ifdef IS_PY3K + PyObject* str = PyUnicode_AsUTF8String(repr); + Py_DECREF(repr); + repr = str; +#endif + out << PyBytes_AS_STRING(repr); + Py_DECREF(repr); + } else { + out << reinterpret_cast(obj); + } + return out; +} + +class _SbkDbg : public BaseLogger +{ +public: + _SbkDbg(const char* function, const char* context = "") : BaseLogger(std::cout, function, context) {} +}; + +#ifdef __GNUG__ +#define SbkDbg(X) _SbkDbg(__PRETTY_FUNCTION__, X"") +#else +#define SbkDbg(X) _SbkDbg(__FUNCTION__, X"") +#endif + +#else + +struct SbkDbg { + template + SbkDbg& operator<<(const T&) { return *this; } +}; + +#endif +#endif // LOGGER_H 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); +} + +} diff --git a/sources/shiboken2/libshiboken/sbkenum.h b/sources/shiboken2/libshiboken/sbkenum.h new file mode 100644 index 000000000..4b572dbcc --- /dev/null +++ b/sources/shiboken2/libshiboken/sbkenum.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef SBKENUM_H +#define SBKENUM_H + +#include "sbkpython.h" +#include "shibokenmacros.h" + +extern "C" +{ + +extern LIBSHIBOKEN_API PyTypeObject SbkEnumType_Type; +struct SbkObjectType; +struct SbkConverter; + +} // extern "C" + +namespace Shiboken +{ + +inline bool isShibokenEnum(PyObject* pyObj) +{ + return Py_TYPE(pyObj->ob_type) == &SbkEnumType_Type; +} + +namespace Enum +{ + LIBSHIBOKEN_API bool check(PyObject* obj); + /** + * Creates a new enum type (and its flags type, if any is given) + * and registers it to Python and adds it to \p module. + * \param module Module to where the new enum type will be added. + * \param name Name of the enum. + * \param fullName Name of the enum that includes all scope information (e.g.: "module.Enum"). + * \param cppName Full qualified C++ name of the enum. + * \param flagsType Optional Python type for the flags associated with the enum. + * \return The new enum type or NULL if it fails. + */ + LIBSHIBOKEN_API PyTypeObject* createGlobalEnum(PyObject* module, + const char* name, + const char* fullName, + const char* cppName, + PyTypeObject* flagsType = 0); + /// This function does the same as createGlobalEnum, but adds the enum to a Shiboken type or namespace. + LIBSHIBOKEN_API PyTypeObject* createScopedEnum(SbkObjectType* scope, + const char* name, + const char* fullName, + const char* cppName, + PyTypeObject* flagsType = 0); + + /** + * Creates a new enum item for a given enum type and adds it to \p module. + * \param enumType Enum type to where the new enum item will be added. + * \param module Module to where the enum type of the new enum item belongs. + * \param itemName Name of the enum item. + * \param itemValue Numerical value of the enum item. + * \return true if everything goes fine, false if it fails. + */ + LIBSHIBOKEN_API bool createGlobalEnumItem(PyTypeObject* enumType, PyObject* module, const char* itemName, long itemValue); + /// This function does the same as createGlobalEnumItem, but adds the enum to a Shiboken type or namespace. + LIBSHIBOKEN_API bool createScopedEnumItem(PyTypeObject* enumType, SbkObjectType* scope, const char* itemName, long itemValue); + + 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 const char* getCppName(PyTypeObject* type); + + LIBSHIBOKEN_API long getValue(PyObject* enumItem); + LIBSHIBOKEN_API PyObject* getEnumItemFromValue(PyTypeObject* enumType, long itemValue); + + /// Sets the enum's type converter. + LIBSHIBOKEN_API void setTypeConverter(PyTypeObject* enumType, SbkConverter* converter); + /// Returns the converter assigned to the enum \p type. + LIBSHIBOKEN_API SbkConverter* getTypeConverter(PyTypeObject* enumType); +} + +} // namespace Shiboken + +#endif // SKB_PYENUM_H diff --git a/sources/shiboken2/libshiboken/sbkmodule.cpp b/sources/shiboken2/libshiboken/sbkmodule.cpp new file mode 100644 index 000000000..084e23efa --- /dev/null +++ b/sources/shiboken2/libshiboken/sbkmodule.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** 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 "sbkmodule.h" +#include "basewrapper.h" +#include "bindingmanager.h" + +// TODO: for performance reasons this should be a sparse_hash_map, +// because there'll be very few modules as keys. The sparse_hash_map +// is missing from the code added in ../ext/sparsehash/google directory. +#include "google/dense_hash_map" + +/// This hash maps module objects to arrays of Python types. +typedef google::dense_hash_map ModuleTypesMap; + +/// This hash maps module objects to arrays of converters. +typedef google::dense_hash_map ModuleConvertersMap; + +/// All types produced in imported modules are mapped here. +static ModuleTypesMap moduleTypes; +static ModuleConvertersMap moduleConverters; + +namespace Shiboken +{ +namespace Module +{ + +void init() +{ + // Initializes type registry for modules. + moduleTypes.set_empty_key((ModuleTypesMap::key_type)0); + moduleTypes.set_deleted_key((ModuleTypesMap::key_type)1); + moduleConverters.set_empty_key((ModuleConvertersMap::key_type)0); + moduleConverters.set_deleted_key((ModuleConvertersMap::key_type)1); +} + +PyObject* import(const char* moduleName) +{ + PyObject* sysModules = PyImport_GetModuleDict(); + PyObject* module = PyDict_GetItemString(sysModules, moduleName); + if (module) + Py_INCREF(module); + else + module = PyImport_ImportModule(moduleName); + + if (!module) + PyErr_Format(PyExc_ImportError,"could not import module '%s'", moduleName); + + return module; +} + +PyObject* create(const char* moduleName, void* moduleData) +{ + Shiboken::init(); +#ifndef IS_PY3K + return Py_InitModule(moduleName, reinterpret_cast(moduleData)); +#else + return PyModule_Create(reinterpret_cast(moduleData)); +#endif +} + +void registerTypes(PyObject* module, PyTypeObject** types) +{ + ModuleTypesMap::iterator iter = moduleTypes.find(module); + if (iter == moduleTypes.end()) + moduleTypes.insert(std::make_pair(module, types)); +} + +PyTypeObject** getTypes(PyObject* module) +{ + ModuleTypesMap::iterator iter = moduleTypes.find(module); + return (iter == moduleTypes.end()) ? 0 : iter->second; +} + +void registerTypeConverters(PyObject* module, SbkConverter** converters) +{ + ModuleConvertersMap::iterator iter = moduleConverters.find(module); + if (iter == moduleConverters.end()) + moduleConverters.insert(std::make_pair(module, converters)); +} + +SbkConverter** getTypeConverters(PyObject* module) +{ + ModuleConvertersMap::iterator iter = moduleConverters.find(module); + return (iter == moduleConverters.end()) ? 0 : iter->second; +} + +} } // namespace Shiboken::Module diff --git a/sources/shiboken2/libshiboken/sbkmodule.h b/sources/shiboken2/libshiboken/sbkmodule.h new file mode 100644 index 000000000..e1ec7a860 --- /dev/null +++ b/sources/shiboken2/libshiboken/sbkmodule.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef SBK_MODULE_H +#define SBK_MODULE_H + +#include "sbkpython.h" +#include "shibokenmacros.h" + +#if PY_MAJOR_VERSION >= 3 + #define SBK_MODULE_INIT_ERROR 0 + #define SBK_MODULE_INIT_FUNCTION_BEGIN(ModuleName) \ + extern "C" SBK_EXPORT_MODULE PyObject* PyInit_##ModuleName() { + + #define SBK_MODULE_INIT_FUNCTION_END \ + return module; } +#else + #define SBK_MODULE_INIT_ERROR + #define SBK_MODULE_INIT_FUNCTION_BEGIN(ModuleName) \ + extern "C" SBK_EXPORT_MODULE void init##ModuleName() { + + #define SBK_MODULE_INIT_FUNCTION_END \ + } +#endif + +extern "C" +{ +struct SbkConverter; +} + +namespace Shiboken { +namespace Module { + +/** + * Imports and returns the module named \p moduleName, or a NULL pointer in case of failure. + * If the module is already imported, it increments its reference count before returning it. + * \returns the module specified in \p moduleName or NULL if an error occurs. + */ +LIBSHIBOKEN_API PyObject* import(const char* moduleName); + +/** + * Creates a new Python module named \p moduleName using the information passed in \p moduleData. + * In fact, \p moduleData expects a "PyMethodDef*" object, but that's for Python 2. A void* + * was preferred to make this work with future Python 3 support. + * \returns a newly created module. + */ +LIBSHIBOKEN_API PyObject* create(const char* moduleName, void* moduleData); + +/** + * Registers the list of types created by \p module. + * \param module Module where the types were created. + * \param types Array of PyTypeObject* objects representing the types created on \p module. + */ +LIBSHIBOKEN_API void registerTypes(PyObject* module, PyTypeObject** types); + +/** + * Retrieves the array of types. + * \param module Module where the types were created. + * \returns A pointer to the PyTypeObject* array of types. + */ +LIBSHIBOKEN_API PyTypeObject** getTypes(PyObject* module); + +/** + * Registers the list of converters created by \p module for non-wrapper types. + * \param module Module where the converters were created. + * \param converters Array of SbkConverter* objects representing the converters created on \p module. + */ +LIBSHIBOKEN_API void registerTypeConverters(PyObject* module, SbkConverter** converters); + +/** + * Retrieves the array of converters. + * \param module Module where the converters were created. + * \returns A pointer to the SbkConverter* array of converters. + */ +LIBSHIBOKEN_API SbkConverter** getTypeConverters(PyObject* module); + +} } // namespace Shiboken::Module + +#endif // SBK_MODULE_H diff --git a/sources/shiboken2/libshiboken/sbkpython.h b/sources/shiboken2/libshiboken/sbkpython.h new file mode 100644 index 000000000..208618d50 --- /dev/null +++ b/sources/shiboken2/libshiboken/sbkpython.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef SBKPYTHON_H +#define SBKPYTHON_H + +#include "Python.h" +#include "python25compat.h" + +#if PY_MAJOR_VERSION >= 3 + #define IS_PY3K + + #define PyInt_Type PyLong_Type + #define PyInt_Check PyLong_Check + #define PyInt_AS_LONG PyLong_AS_LONG + #define PyInt_AsUnsignedLongLongMask PyLong_AsLongLong + #define PyInt_FromLong PyLong_FromLong + #define PyInt_AsLong PyLong_AsLong + #define SbkNumber_Check PyNumber_Check + #define Py_TPFLAGS_CHECKTYPES 0 + + #define SBK_NB_BOOL(x) (x).nb_bool + #define SBK_PyMethod_New PyMethod_New + #define PyInt_AsSsize_t(x) PyLong_AsSsize_t(x) + #define PyString_Type PyUnicode_Type + +#else + // Note: if there wasn't for the old-style classes, only a PyNumber_Check would suffice. + #define SbkNumber_Check(X) \ + (PyNumber_Check(X) && (!PyInstance_Check(X) || PyObject_HasAttrString(X, "__trunc__"))) + #define SBK_NB_BOOL(x) (x).nb_nonzero + #define SBK_STR_NAME "str" + #define SBK_PyMethod_New(X, Y) PyMethod_New(X, Y, reinterpret_cast(Py_TYPE(Y))) + + #define Py_hash_t long +#endif + +#endif diff --git a/sources/shiboken2/libshiboken/sbkstring.cpp b/sources/shiboken2/libshiboken/sbkstring.cpp new file mode 100644 index 000000000..598bccc46 --- /dev/null +++ b/sources/shiboken2/libshiboken/sbkstring.cpp @@ -0,0 +1,202 @@ +/**************************************************************************** +** +** 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 "sbkstring.h" +#include "autodecref.h" + +namespace Shiboken +{ + +namespace String +{ + +bool checkType(PyTypeObject* type) +{ + return type == &PyUnicode_Type +#if PY_MAJOR_VERSION < 3 + || type == &PyString_Type +#endif + ; +} + +bool check(PyObject* obj) +{ + return obj == Py_None || +#if PY_MAJOR_VERSION < 3 + PyString_Check(obj) || +#endif + PyUnicode_Check(obj); +} + +bool checkChar(PyObject* pyobj) +{ + if (check(pyobj) && (len(pyobj) == 1)) + return true; + + return false; +} + +bool isConvertible(PyObject* obj) +{ + return check(obj); +} + +PyObject* fromCString(const char* value) +{ +#ifdef IS_PY3K + return PyUnicode_FromString(value); +#else + return PyBytes_FromString(value); +#endif +} + +PyObject* fromCString(const char* value, int len) +{ +#ifdef IS_PY3K + return PyUnicode_FromStringAndSize(value, len); +#else + return PyBytes_FromStringAndSize(value, len); +#endif +} + +const char* toCString(PyObject* str, Py_ssize_t* len) +{ + if (str == Py_None) + return NULL; +#ifdef IS_PY3K + if (PyUnicode_Check(str)) { + if (len) { + // We need to encode the unicode string into utf8 to know the size of returned char*. + Shiboken::AutoDecRef uniStr(PyUnicode_AsUTF8String(str)); + *len = PyBytes_GET_SIZE(uniStr.object()); + } + // Return unicode from str instead of uniStr, because the lifetime of the returned pointer + // depends on the lifetime of str. + return _PyUnicode_AsString(str); + } +#endif + if (PyBytes_Check(str)) { + if (len) + *len = PyBytes_GET_SIZE(str); + return PyBytes_AS_STRING(str); + } + return 0; +} + +bool concat(PyObject** val1, PyObject* val2) +{ + if (PyUnicode_Check(*val1) && PyUnicode_Check(val2)) { + PyObject* result = PyUnicode_Concat(*val1, val2); + Py_DECREF(*val1); + *val1 = result; + return true; + } + + if (PyBytes_Check(*val1) && PyBytes_Check(val2)) { + PyBytes_Concat(val1, val2); + return true; + } + +#if PY_MAJOR_VERSION < 3 + if (PyString_Check(*val1) && PyString_Check(val2)) { + PyString_Concat(val1, val2); + return true; + } +#endif + return false; +} + +PyObject* fromFormat(const char* format, ...) +{ + va_list argp; + va_start(argp, format); + PyObject* result = 0; +#ifdef IS_PY3K + result = PyUnicode_FromFormatV(format, argp); +#else + result = PyString_FromFormatV(format, argp); +#endif + va_end(argp); + return result; +} + +PyObject* fromStringAndSize(const char* str, Py_ssize_t size) +{ +#ifdef IS_PY3K + return PyUnicode_FromStringAndSize(str, size); +#else + return PyString_FromStringAndSize(str, size); +#endif +} + +int compare(PyObject* val1, const char* val2) +{ + if (PyUnicode_Check(val1)) +#ifdef IS_PY3K + return PyUnicode_CompareWithASCIIString(val1, val2); +#else + { + PyObject* uVal2 = PyUnicode_FromString(val2); + bool result = PyUnicode_Compare(val1, uVal2); + Py_XDECREF(uVal2); + return result; + } + if (PyString_Check(val1)) + return strcmp(PyString_AS_STRING(val1), val2); +#endif + return 0; + +} + +Py_ssize_t len(PyObject* str) +{ + if (str == Py_None) + return 0; + + if (PyUnicode_Check(str)) + return PyUnicode_GET_SIZE(str); + + if (PyBytes_Check(str)) + return PyBytes_GET_SIZE(str); + return 0; +} + +} // namespace String + +} // namespace Shiboken diff --git a/sources/shiboken2/libshiboken/sbkstring.h b/sources/shiboken2/libshiboken/sbkstring.h new file mode 100644 index 000000000..0d498da6d --- /dev/null +++ b/sources/shiboken2/libshiboken/sbkstring.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef SBKSTRING_H +#define SBKSTRING_H + +#include "sbkpython.h" +#include "shibokenmacros.h" + +#if PY_MAJOR_VERSION >= 3 + #define SBK_STR_NAME "unicode" +#else + #define SBK_STR_NAME "str" +#endif + +namespace Shiboken +{ +namespace String +{ + LIBSHIBOKEN_API bool check(PyObject* obj); + LIBSHIBOKEN_API bool checkType(PyTypeObject* obj); + LIBSHIBOKEN_API bool checkChar(PyObject* obj); + LIBSHIBOKEN_API bool isConvertible(PyObject* obj); + LIBSHIBOKEN_API PyObject* fromCString(const char* value); + LIBSHIBOKEN_API PyObject* fromCString(const char* value, int len); + LIBSHIBOKEN_API const char* toCString(PyObject* str, Py_ssize_t* len = 0); + LIBSHIBOKEN_API bool concat(PyObject** val1, PyObject* val2); + LIBSHIBOKEN_API PyObject* fromFormat(const char* format, ...); + LIBSHIBOKEN_API PyObject* fromStringAndSize(const char* str, Py_ssize_t size); + LIBSHIBOKEN_API int compare(PyObject* val1, const char* val2); + LIBSHIBOKEN_API Py_ssize_t len(PyObject* str); + +} // namespace String +} // namespace Shiboken + + +#endif + + diff --git a/sources/shiboken2/libshiboken/sbkversion.h.in b/sources/shiboken2/libshiboken/sbkversion.h.in new file mode 100644 index 000000000..589c5ac26 --- /dev/null +++ b/sources/shiboken2/libshiboken/sbkversion.h.in @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef SBKVERSION_H +#define SBKVERSION_H + +#define SHIBOKEN_VERSION "@shiboken_MAJOR_VERSION@.@shiboken_MINOR_VERSION@.@shiboken_MICRO_VERSION@" +#define SHIBOKEN_MAJOR_VERSION @shiboken_MAJOR_VERSION@ +#define SHIBOKEN_MINOR_VERSION @shiboken_MINOR_VERSION@ +#define SHIBOKEN_MICRO_VERSION @shiboken_MICRO_VERSION@ +#define SHIBOKEN_RELEASE_LEVEL "final" +#define SHIBOKEN_SERIAL 0 + +#endif diff --git a/sources/shiboken2/libshiboken/shiboken.h b/sources/shiboken2/libshiboken/shiboken.h new file mode 100644 index 000000000..2738bf51f --- /dev/null +++ b/sources/shiboken2/libshiboken/shiboken.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef SHIBOKEN_H +#define SHIBOKEN_H + +#include "sbkpython.h" +#include "autodecref.h" +#include "basewrapper.h" +#include "bindingmanager.h" +#include "conversions.h" +#include "gilstate.h" +#include "threadstatesaver.h" +#include "helper.h" +#include "sbkconverter.h" +#include "sbkenum.h" +#include "sbkmodule.h" +#include "sbkstring.h" +#include "shibokenmacros.h" +#include "typeresolver.h" +#include "shibokenbuffer.h" + +#endif // SHIBOKEN_H + diff --git a/sources/shiboken2/libshiboken/shibokenbuffer.cpp b/sources/shiboken2/libshiboken/shibokenbuffer.cpp new file mode 100644 index 000000000..6cc617668 --- /dev/null +++ b/sources/shiboken2/libshiboken/shibokenbuffer.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** 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 "shibokenbuffer.h" +#include +#include + +bool Shiboken::Buffer::checkType(PyObject* pyObj) +{ + return PyObject_CheckReadBuffer(pyObj); +} + +void* Shiboken::Buffer::getPointer(PyObject* pyObj, Py_ssize_t* size) +{ + +#ifdef IS_PY3K + Py_buffer view; + if (PyObject_GetBuffer(pyObj, &view, PyBUF_ND) == 0) { + if (size) + *size = view.len; + return view.buf; + } else { + return 0; + } +#else + const void* buffer = 0; + Py_ssize_t bufferSize = 0; + + PyObject_AsReadBuffer(pyObj, &buffer, &bufferSize); + + if (size) + *size = bufferSize; + return const_cast(buffer); +#endif +} + +PyObject* Shiboken::Buffer::newObject(void* memory, Py_ssize_t size, Type type) +{ + if (size == 0) + Py_RETURN_NONE; +#ifdef IS_PY3K + Py_buffer view; + memset(&view, 0, sizeof(Py_buffer)); + view.buf = memory; + view.len = size; + view.readonly = type == Shiboken::Buffer::ReadOnly; + view.ndim = 1; + view.itemsize = sizeof(char); + Py_ssize_t shape[] = { size }; + view.shape = shape; + return PyMemoryView_FromBuffer(&view); +#else + return type == ReadOnly ? PyBuffer_FromMemory(memory, size) : PyBuffer_FromReadWriteMemory(memory, size); +#endif +} + +PyObject* Shiboken::Buffer::newObject(const void* memory, Py_ssize_t size) +{ + return newObject(const_cast(memory), size, ReadOnly); +} diff --git a/sources/shiboken2/libshiboken/shibokenbuffer.h b/sources/shiboken2/libshiboken/shibokenbuffer.h new file mode 100644 index 000000000..18d86c6e1 --- /dev/null +++ b/sources/shiboken2/libshiboken/shibokenbuffer.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef SHIBOKEN_BUFFER_H +#define SHIBOKEN_BUFFER_H + +#include "sbkpython.h" +#include "shibokenmacros.h" + +namespace Shiboken +{ + +namespace Buffer +{ + enum Type { + ReadOnly, + WriteOnly, + ReadWrite + }; + + /** + * Creates a new Python buffer pointing to a contiguous memory block at + * \p memory of size \p size. + */ + LIBSHIBOKEN_API PyObject* newObject(void* memory, Py_ssize_t size, Type type); + + /** + * Creates a new read only Python buffer pointing to a contiguous memory block at + * \p memory of size \p size. + */ + LIBSHIBOKEN_API PyObject* newObject(const void* memory, Py_ssize_t size); + + /** + * Check if is ok to use \p pyObj as argument in all function under Shiboken::Buffer namespace. + */ + LIBSHIBOKEN_API bool checkType(PyObject* pyObj); + + /** + * Returns a pointer to the memory pointed by the buffer \p pyObj, \p size is filled with the buffer + * size if not null. + * + * If the \p pyObj is a non-contiguous buffer a Python error is set. + */ + LIBSHIBOKEN_API void* getPointer(PyObject* pyObj, Py_ssize_t* size = 0); + +} // namespace Buffer +} // namespace Shiboken + +#endif diff --git a/sources/shiboken2/libshiboken/shibokenmacros.h b/sources/shiboken2/libshiboken/shibokenmacros.h new file mode 100644 index 000000000..acd8f6ada --- /dev/null +++ b/sources/shiboken2/libshiboken/shibokenmacros.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef SHIBOKENMACROS_H +#define SHIBOKENMACROS_H + +// LIBSHIBOKEN_API macro is used for the public API symbols. +#if defined _WIN32 + #if LIBSHIBOKEN_EXPORTS + #define LIBSHIBOKEN_API __declspec(dllexport) + #else + #ifdef _MSC_VER + #define LIBSHIBOKEN_API __declspec(dllimport) + #endif + #endif + #define SBK_DEPRECATED(func) __declspec(deprecated) func +#elif __GNUC__ >= 4 + #define LIBSHIBOKEN_API __attribute__ ((visibility("default"))) + #define SBK_DEPRECATED(func) func __attribute__ ((deprecated)) +#endif + +#ifndef LIBSHIBOKEN_API + #define LIBSHIBOKEN_API + #define SBK_DEPRECATED(func) func +#endif + +#endif diff --git a/sources/shiboken2/libshiboken/threadstatesaver.cpp b/sources/shiboken2/libshiboken/threadstatesaver.cpp new file mode 100644 index 000000000..3d3ba81ab --- /dev/null +++ b/sources/shiboken2/libshiboken/threadstatesaver.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** 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 "threadstatesaver.h" + +namespace Shiboken +{ + +ThreadStateSaver::ThreadStateSaver() + : m_threadState(0) + {} + +ThreadStateSaver::~ThreadStateSaver() +{ + restore(); +} + +void ThreadStateSaver::save() +{ + if (PyEval_ThreadsInitialized()) + m_threadState = PyEval_SaveThread(); +} + +void ThreadStateSaver::restore() +{ + if (m_threadState) { + PyEval_RestoreThread(m_threadState); + m_threadState = 0; + } +} + +} // namespace Shiboken + diff --git a/sources/shiboken2/libshiboken/threadstatesaver.h b/sources/shiboken2/libshiboken/threadstatesaver.h new file mode 100644 index 000000000..3fe595d95 --- /dev/null +++ b/sources/shiboken2/libshiboken/threadstatesaver.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef THREADSTATESAVER_H +#define THREADSTATESAVER_H + +#include "sbkpython.h" +#include + +namespace Shiboken +{ + +class LIBSHIBOKEN_API ThreadStateSaver +{ +public: + ThreadStateSaver(); + ~ThreadStateSaver(); + void save(); + void restore(); +private: + PyThreadState* m_threadState; + + ThreadStateSaver(const ThreadStateSaver&); + ThreadStateSaver& operator=(const ThreadStateSaver&); +}; + +} // namespace Shiboken + +#endif // THREADSTATESAVER_H + diff --git a/sources/shiboken2/libshiboken/tmp-referencetopython/sbkconverter.cpp b/sources/shiboken2/libshiboken/tmp-referencetopython/sbkconverter.cpp new file mode 100644 index 000000000..c3e25014e --- /dev/null +++ b/sources/shiboken2/libshiboken/tmp-referencetopython/sbkconverter.cpp @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** 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 "sbkconverter.h" +#include "sbkconverter_p.h" +#include "basewrapper_p.h" + +#include "sbkdbg.h" + +namespace Shiboken { +namespace Conversions { + +static SbkConverter* createConverterObject(PyTypeObject* type, + PythonToCppFunc toCppPointerConvFunc, + IsConvertibleToCppFunc toCppPointerCheckFunc, + CppToPythonFunc pointerToPythonFunc, + CppToPythonFunc copyToPythonFunc) +{ + SbkConverter* converter = new SbkConverter; + converter->pythonType = type; + + converter->pointerToPython = pointerToPythonFunc; + converter->copyToPython = copyToPythonFunc; + + converter->toCppPointerConversion = std::make_pair(toCppPointerCheckFunc, toCppPointerConvFunc); + converter->toCppConversions.clear(); + + return converter; +} + +SbkConverter* createConverter(SbkObjectType* type, + PythonToCppFunc toCppPointerConvFunc, + IsConvertibleToCppFunc toCppPointerCheckFunc, + CppToPythonFunc pointerToPythonFunc, + CppToPythonFunc copyToPythonFunc) +{ + SbkConverter* converter = createConverterObject((PyTypeObject*)type, + toCppPointerConvFunc, toCppPointerCheckFunc, + pointerToPythonFunc, copyToPythonFunc); + type->d->converter = converter; + return converter; +} + +void deleteConverter(SbkConverter* converter) +{ + if (converter) { + converter->toCppConversions.clear(); + delete converter; + } +} + +void addPythonToCppValueConversion(SbkConverter* converter, + PythonToCppFunc pythonToCppFunc, + IsConvertibleToCppFunc isConvertibleToCppFunc) +{ + converter->toCppConversions.push_back(std::make_pair(isConvertibleToCppFunc, pythonToCppFunc)); +} +void addPythonToCppValueConversion(SbkObjectType* type, + PythonToCppFunc pythonToCppFunc, + IsConvertibleToCppFunc isConvertibleToCppFunc) +{ + addPythonToCppValueConversion(type->d->converter, pythonToCppFunc, isConvertibleToCppFunc); +} + +PyObject* pointerToPython(SbkObjectType* type, const void* cppIn) +{ + if (!cppIn) + Py_RETURN_NONE; + return type->d->converter->pointerToPython(cppIn); +} + +static inline PyObject* CopyCppToPython(SbkConverter* converter, const void* cppIn) +{ + assert(cppIn); + return converter->copyToPython(cppIn); +} +PyObject* copyToPython(SbkObjectType* type, const void* cppIn) +{ + return CopyCppToPython(type->d->converter, cppIn); +} +PyObject* toPython(SbkConverter* converter, const void* cppIn) +{ + return CopyCppToPython(converter, cppIn); +} + +PyObject* referenceToPython(SbkObjectType* type, const void* cppIn) +{ + assert(cppIn); + PyObject* pyOut = (PyObject*)BindingManager::instance().retrieveWrapper(cppIn); + if (pyOut) { + Py_INCREF(pyOut); + return pyOut; + } + // If it is Value Type, return a copy of the C++ object. + if (type->d->converter->copyToPython) + return type->d->converter->copyToPython(cppIn); + // If it is an Object Type, return a copy of the C++ object. + return type->d->converter->pointerToPython(cppIn); +} + +PythonToCppFunc isPythonToCppPointerConvertible(SbkObjectType* type, PyObject* pyIn) +{ + assert(pyIn); + return type->d->converter->toCppPointerConversion.first(pyIn); +} + +static inline PythonToCppFunc IsPythonToCppConvertible(SbkConverter* converter, PyObject* pyIn) +{ + assert(pyIn); + ToCppConversionList& convs = converter->toCppConversions; + for (ToCppConversionList::iterator conv = convs.begin(); conv != convs.end(); ++conv) { + PythonToCppFunc toCppFunc = 0; + if ((toCppFunc = (*conv).first(pyIn))) + return toCppFunc; + } + return 0; +} +PythonToCppFunc isPythonToCppValueConvertible(SbkObjectType* type, PyObject* pyIn) +{ + return IsPythonToCppConvertible(type->d->converter, pyIn); +} +PythonToCppFunc isPythonToCppConvertible(SbkConverter* converter, PyObject* pyIn) +{ + return IsPythonToCppConvertible(converter, pyIn); +} + +PythonToCppFunc isPythonToCppReferenceConvertible(SbkObjectType* type, PyObject* pyIn) +{ + if (pyIn != Py_None) { + PythonToCppFunc toCpp = isPythonToCppPointerConvertible(type, pyIn); + if (toCpp) + return toCpp; + } + return isPythonToCppValueConvertible(type, pyIn); +} + +void nonePythonToCppNullPtr(PyObject*, void* cppOut) +{ + assert(cppOut); + *((void**)cppOut) = 0; +} + +void pythonToCppPointer(SbkObjectType* type, PyObject* pyIn, void* cppOut) +{ + assert(pyIn); + assert(cppOut); + SbkObjectType* inType = (SbkObjectType*)pyIn->ob_type; + if (ObjectType::hasCast(inType)) + *((void**)cppOut) = ObjectType::cast(inType, (SbkObject*)pyIn, (PyTypeObject*)type); + else + *((void**)cppOut) = Object::cppPointer((SbkObject*)pyIn, (PyTypeObject*)type); +} + +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) + return false; + + // Object Types doesn't have any kind of value conversion, + // only C++ pointer retrieval. + if (type->d->converter->toCppConversions.empty()) + return false; + + // The first conversion of the non-pointer conversion list is + // a Value Type's copy to C++ function, which is not an implicit + // conversion. + // Otherwise it must be one of the implicit conversions. + // 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(); + return toCppFunc != (*conv).second; +} + +} } // namespace Shiboken::Conversions diff --git a/sources/shiboken2/libshiboken/tmp-referencetopython/sbkconverter.h b/sources/shiboken2/libshiboken/tmp-referencetopython/sbkconverter.h new file mode 100644 index 000000000..d4bbbca1d --- /dev/null +++ b/sources/shiboken2/libshiboken/tmp-referencetopython/sbkconverter.h @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef SBK_CONVERTER_H +#define SBK_CONVERTER_H + +#include +#include +#include "shibokenmacros.h" +#include "basewrapper.h" + +extern "C" +{ + +/** + * SbkConverter is used to perform type conversions from C++ + * to Python and vice-versa;.and it is also used for type checking. + * SbkConverter is a private structure that must be accessed + * using the functions provided by the converter API. + */ +struct SbkConverter; + +/** + * Given a void pointer to a C++ object, this function must return + * the proper Python object. It may be either an existing wrapper + * for the C++ object, or a newly create one. Or even the Python + * equivalent of the C++ value passed in the argument. + * + * C++ -> Python + */ +typedef PyObject* (*CppToPythonFunc)(const void*); + +/** + * This function converts a Python object to a C++ value, it may be + * a pointer, value, class, container or primitive type, passed via + * a void pointer, that will be cast properly inside the function. + * This function is usually returned by an IsConvertibleToCppFunc + * function, or obtained knowing the type of the Python object input, + * thus it will not check the Python object type, and will expect + * the void pointer to be pointing to a proper variable. + * + * Python -> C++ + */ +typedef void (*PythonToCppFunc)(PyObject*,void*); + +/** + * Checks if the Python object passed in the argument is convertible to a + * C++ type defined inside the function, it returns the converter function + * that will transform a Python argument into a C++ value. + * It returns NULL if the Python object is not convertible to the C++ type + * that the function represents. + * + * Python -> C++ ? + */ +typedef PythonToCppFunc (*IsConvertibleToCppFunc)(PyObject*); + +} // extern "C" + + +namespace Shiboken { +namespace Conversions { + +/** + * Creates a converter for a wrapper type. + * \param type A Shiboken.ObjectType that will receive the new converter. + * \param toCppPointerConvFunc Function to retrieve the C++ pointer held by a Python wrapper. + * \param toCppPointerCheckFunc Check and return the retriever function of the C++ pointer held by a Python wrapper. + * \param pointerToPythonFunc Function to convert a C++ object to a Python \p type wrapper, keeping their identity. + * \param copyToPythonFunc Function to convert a C++ object to a Python \p type, copying the object. + * \returns The new converter referred by the wrapper \p type. + */ +LIBSHIBOKEN_API SbkConverter* createConverter(SbkObjectType* type, + PythonToCppFunc toCppPointerConvFunc, + IsConvertibleToCppFunc toCppPointerCheckFunc, + CppToPythonFunc pointerToPythonFunc, + CppToPythonFunc copyToPythonFunc = 0); + +LIBSHIBOKEN_API void deleteConverter(SbkConverter* converter); + +/** + * Adds a new conversion of a Python object to a C++ value. + * This is used in copy and implicit conversions. + */ +LIBSHIBOKEN_API void addPythonToCppValueConversion(SbkConverter* converter, + PythonToCppFunc pythonToCppFunc, + IsConvertibleToCppFunc isConvertibleToCppFunc); +LIBSHIBOKEN_API void addPythonToCppValueConversion(SbkObjectType* type, + PythonToCppFunc pythonToCppFunc, + IsConvertibleToCppFunc isConvertibleToCppFunc); + +// C++ -> Python --------------------------------------------------------------------------- + +/** + * Retrieves the Python wrapper object for the given \p cppIn C++ pointer object. + * This function is used only for Value and Object Types. + * Example usage: + * TYPE* var; + * PyObject* pyVar = pointerToPython(SBKTYPE, &var); + */ +LIBSHIBOKEN_API PyObject* pointerToPython(SbkObjectType* type, const void* cppIn); + +/** + * Retrieves the Python wrapper object for the given C++ value pointed by \p cppIn. + * This function is used only for Value Types. + * Example usage: + * TYPE var; + * PyObject* pyVar = copyToPython(SBKTYPE, &var); + */ +LIBSHIBOKEN_API PyObject* copyToPython(SbkObjectType* type, const void* cppIn); + +// TODO:WRITEDOCSTRING - used only for Value Types - cppIn must point to a value +/** + * Retrieves the Python wrapper object for the given C++ reference pointed by \p cppIn. + * This function is used only for Value and Object Types. + * It differs from pointerToPython() for not checking for a NULL pointer. + * Example usage: + * TYPE& var = SOMETHING; + * PyObject* pyVar = copyToPython(SBKTYPE, &var); + */ +LIBSHIBOKEN_API PyObject* referenceToPython(SbkObjectType* type, const void* cppIn); + +// TODO:WRITEDOCSTRING - used only for Primitives and Containers (and Value Types) - cppIn must point to a primitive, container or value type +/// This is the same as copyToPython function. +LIBSHIBOKEN_API PyObject* toPython(SbkConverter* converter, const void* cppIn); + +// Python -> C++ convertibility checks ----------------------------------------------------- + +// TODO:WRITEDOCSTRING +LIBSHIBOKEN_API PythonToCppFunc isPythonToCppPointerConvertible(SbkObjectType* type, PyObject* pyIn); + +// TODO:WRITEDOCSTRING- Returns a Python to C++ conversion function if true, or NULL if false. +LIBSHIBOKEN_API PythonToCppFunc isPythonToCppValueConvertible(SbkObjectType* type, PyObject* pyIn); + +// TODO:WRITEDOCSTRING- Returns a Python to C++ conversion function if true, or NULL if false. +LIBSHIBOKEN_API PythonToCppFunc isPythonToCppReferenceConvertible(SbkObjectType* type, PyObject* pyIn); + +/// This is the same as isPythonToCppValueConvertible function. +LIBSHIBOKEN_API PythonToCppFunc isPythonToCppConvertible(SbkConverter* converter, PyObject* pyIn); + +// Python -> C++ --------------------------------------------------------------------------- + +// TODO:WRITEDOCSTRING - function used by the generated [TYPE]_PythonToCpp_[TYPE]_PTR +LIBSHIBOKEN_API void pythonToCppPointer(SbkObjectType* type, PyObject* pyIn, void* cppOut); + +// TODO:WRITEDOCSTRING - function used by the generated isConvertible when the PyObject is None, +// making a C++ NULL pointer the result of the toCpp function call. +// DRAFT: When the Python object is a Py_None, it's C++ conversion is always a NULL pointer. +LIBSHIBOKEN_API void nonePythonToCppNullPtr(PyObject*, void* cppOut); + +// TODO:WRITEDOCSTRING - tells if \p toCpp is an implicit conversion. +LIBSHIBOKEN_API bool isImplicitConversion(SbkObjectType* type, PythonToCppFunc toCpp); + +} } // namespace Shiboken::Conversions + +#endif // SBK_CONVERTER_H diff --git a/sources/shiboken2/libshiboken/typeresolver.cpp b/sources/shiboken2/libshiboken/typeresolver.cpp new file mode 100644 index 000000000..3939fff5f --- /dev/null +++ b/sources/shiboken2/libshiboken/typeresolver.cpp @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** 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 "typeresolver.h" +#include "google/dense_hash_map" +#include "sbkdbg.h" +#include +#include +#include "basewrapper_p.h" + +using namespace Shiboken; + +typedef google::dense_hash_map TypeResolverMap; +static TypeResolverMap typeResolverMap; + +struct TypeResolver::TypeResolverPrivate +{ + CppToPythonFunc cppToPython; + PythonToCppFunc pythonToCpp; + PyTypeObject* pyType; +}; + +static void deinitTypeResolver() +{ + for (TypeResolverMap::const_iterator it = typeResolverMap.begin(); it != typeResolverMap.end(); ++it) + delete it->second; + typeResolverMap.clear(); +} + +void Shiboken::initTypeResolver() +{ + assert(typeResolverMap.empty()); + typeResolverMap.set_empty_key(""); + typeResolverMap.set_deleted_key("?"); + std::atexit(deinitTypeResolver); +} + +TypeResolver::TypeResolver() : m_d(new TypeResolverPrivate) +{ +} + +TypeResolver* TypeResolver::createTypeResolver(const char* typeName, + CppToPythonFunc cppToPy, + PythonToCppFunc pyToCpp, + PyTypeObject* pyType) +{ + TypeResolver*& tr = typeResolverMap[typeName]; + if (!tr) { + tr = new TypeResolver; + tr->m_d->cppToPython = cppToPy; + tr->m_d->pythonToCpp = pyToCpp; + tr->m_d->pyType = pyType; + + /* + * Note: + * + * Value types are also registered as object types, but the generator *always* first register the value + * type version in the TypeResolver and it *must* always do it! otherwise this code wont work. + */ + if (pyType && PyType_IsSubtype(pyType, reinterpret_cast(&SbkObject_Type))) { + SbkObjectType* sbkType = reinterpret_cast(pyType); + // TODO-CONVERTERS: to be deprecated + if (!sbkType->d->type_behaviour) { + const size_t len = strlen(typeName); + sbkType->d->type_behaviour = typeName[len -1] == '*' ? BEHAVIOUR_OBJECTTYPE : BEHAVIOUR_VALUETYPE; + } + } + } + return tr; +} + +TypeResolver::~TypeResolver() +{ + delete m_d; +} + +TypeResolver* TypeResolver::get(const char* typeName) +{ + TypeResolverMap::const_iterator it = typeResolverMap.find(typeName); + if (it != typeResolverMap.end()) { + return it->second; + } else { + if (Py_VerboseFlag > 0) + SbkDbg() << "Can't find type resolver for " << typeName; + return 0; + } +} + +void TypeResolver::toCpp(PyObject* pyObj, void** place) +{ + m_d->pythonToCpp(pyObj, place); +} + +PyObject* TypeResolver::toPython(void* cppObj) +{ + return m_d->cppToPython(cppObj); +} + +PyTypeObject* TypeResolver::pythonType() +{ + return m_d->pyType; +} + +TypeResolver::Type TypeResolver::getType(const char* name) +{ + const size_t len = strlen(name); + bool isObjTypeName = name[len - 1] == '*'; + if (TypeResolver::get(name)) { + // great, we found the type in our first attempt! + return isObjTypeName ? ObjectType : ValueType; + } else { + // Type not found... let's copy the string. + std::string typeName(name); + if (isObjTypeName) + typeName.erase(len - 1, 1); + else + typeName += '*'; + isObjTypeName = !isObjTypeName; + + if (TypeResolver::get(typeName.c_str())) + return isObjTypeName ? ObjectType : ValueType; + else + return UnknownType; + } +} + diff --git a/sources/shiboken2/libshiboken/typeresolver.h b/sources/shiboken2/libshiboken/typeresolver.h new file mode 100644 index 000000000..bc56522fe --- /dev/null +++ b/sources/shiboken2/libshiboken/typeresolver.h @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef TYPERESOLVER_H +#define TYPERESOLVER_H + +#include "shibokenmacros.h" +#include "conversions.h" + +namespace Shiboken +{ + +/* To C++ convertion functions. */ +template +inline void pythonToValueType(PyObject* pyobj, void** data) +{ + *reinterpret_cast(*data) = Shiboken::Converter::toCpp(pyobj); +} + +template +inline void pythonToObjectType(PyObject* pyobj, void** data) +{ + *reinterpret_cast(*data) = Shiboken::Converter::toCpp(pyobj); +} + +template +inline PyObject* objectTypeToPython(void* cptr) +{ + return Shiboken::Converter::toPython(*reinterpret_cast(cptr)); +} + +template +inline PyObject* referenceTypeToPython(void* cptr) +{ + // cptr comes the same way it come when we have a value type, but + // we deliver a Python object of a reference + return Shiboken::Converter::toPython(*reinterpret_cast(cptr)); +} + +/** +* \internal This function is not part of the public API. +* Initialize the TypeResource internal cache. +*/ +void initTypeResolver(); + +class LIBSHIBOKEN_API TypeResolver +{ +public: + enum Type + { + ObjectType, + ValueType, + UnknownType + }; + + typedef PyObject* (*CppToPythonFunc)(void*); + typedef void (*PythonToCppFunc)(PyObject*, void**); + + ~TypeResolver(); + + template + static TypeResolver* createValueTypeResolver(const char* typeName) + { + return createTypeResolver(typeName, &Shiboken::Converter::toPython, &pythonToValueType, SbkType()); + } + + template + static TypeResolver* createObjectTypeResolver(const char* typeName) + { + return createTypeResolver(typeName, &objectTypeToPython, &pythonToObjectType, SbkType()); + } + + /** + * This kind of type resolver is used only when we have a signal with a reference in their arguments + * like on QSqlTableModel::primeInsert. + */ + template + static TypeResolver* createReferenceTypeResolver(const char* typeName) + { + return createTypeResolver(typeName, &referenceTypeToPython, &pythonToValueType, SbkType()); + } + + static Type getType(const char* name); + static TypeResolver* get(const char* typeName); + + PyObject* toPython(void* cppObj); + void toCpp(PyObject* pyObj, void** place); + PyTypeObject* pythonType(); + +private: + struct TypeResolverPrivate; + TypeResolverPrivate* m_d; + + TypeResolver(); + // disable object copy + TypeResolver(const TypeResolver&); + TypeResolver& operator=(const TypeResolver&); + + static TypeResolver* createTypeResolver(const char* typeName, CppToPythonFunc cppToPy, PythonToCppFunc pyToCpp, PyTypeObject* pyType); +}; +} + +#endif -- cgit v1.2.3