diff options
Diffstat (limited to 'sources/shiboken6/libshiboken')
79 files changed, 6085 insertions, 5527 deletions
diff --git a/sources/shiboken6/libshiboken/CMakeLists.txt b/sources/shiboken6/libshiboken/CMakeLists.txt index 2a07150ff..b5bbb498a 100644 --- a/sources/shiboken6/libshiboken/CMakeLists.txt +++ b/sources/shiboken6/libshiboken/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + 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) @@ -23,13 +26,25 @@ else() set(embedding_option "") endif() +if(SHIBOKEN_IS_CROSS_BUILD) + set(host_python_path "${QFP_PYTHON_HOST_PATH}") + set(use_pyc_in_embedding FALSE) +else() + set(host_python_path "${Python_EXECUTABLE}") + if(PYTHON_LIMITED_API) + set(use_pyc_in_embedding FALSE) + else() + set(use_pyc_in_embedding TRUE) + endif() +endif() + add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/embed/signature_bootstrap_inc.h" OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/embed/signature_inc.h" - COMMAND ${PYTHON_EXECUTABLE} -E + COMMAND ${host_python_path} -E "${CMAKE_CURRENT_SOURCE_DIR}/embed/embedding_generator.py" --cmake-dir "${CMAKE_CURRENT_BINARY_DIR}/embed" - --limited-api ${PYTHON_LIMITED_API} + --use-pyc ${use_pyc_in_embedding} ${embedding_option} DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/embed/embedding_generator.py" "${CMAKE_CURRENT_SOURCE_DIR}/embed/signature_bootstrap.py" @@ -43,40 +58,49 @@ set(libshiboken_VERSION "${libshiboken_MAJOR_VERSION}.${libshiboken_MINOR_VERSIO set(libshiboken_SOVERSION "${shiboken6_library_so_version}") set(libshiboken_SRC -basewrapper.cpp -debugfreehook.cpp -gilstate.cpp -helper.cpp -sbkarrayconverter.cpp -sbkconverter.cpp -sbkenum.cpp -sbkmodule.cpp -sbkstring.cpp -sbkstaticstrings.cpp -bindingmanager.cpp -threadstatesaver.cpp -shibokenbuffer.cpp -pep384impl.cpp -voidptr.cpp -bufferprocs_py37.cpp +autodecref.h +basewrapper.cpp basewrapper.h basewrapper_p.h +bindingmanager.cpp bindingmanager.h +bufferprocs_py37.cpp bufferprocs_py37.h +debugfreehook.cpp debugfreehook.h +gilstate.cpp gilstate.h +helper.cpp helper.h +pep384impl.cpp pep384impl.h +pyobjectholder.h +sbkarrayconverter.cpp sbkarrayconverter.h sbkarrayconverter_p.h +sbkcontainer.cpp sbkcontainer.h +sbkconverter.cpp sbkconverter.h sbkconverter_p.h +sbkcppstring.cpp sbkcppstring.h sbkcpptonumpy.h +sbkenum.cpp sbkenum.h +sbkerrors.cpp sbkerrors.h +sbkfeature_base.cpp sbkfeature_base.h +sbkmodule.cpp sbkmodule.h +sbknumpy.cpp sbknumpycheck.h +sbknumpyview.h +sbkpython.h +sbksmartpointer.cpp sbksmartpointer.h +sbkstaticstrings.cpp sbkstaticstrings.h sbkstaticstrings_p.h +sbkstring.cpp sbkstring.h +sbktypefactory.cpp sbktypefactory.h +sbkwindows.h +shiboken.h +shibokenbuffer.cpp shibokenbuffer.h +shibokenmacros.h +threadstatesaver.cpp threadstatesaver.h +voidptr.cpp voidptr.h embed/signature_bootstrap_inc.h embed/signature_inc.h -signature/signature.cpp +signature/signature.cpp signature.h signature_p.h signature/signature_globals.cpp signature/signature_extend.cpp signature/signature_helper.cpp ) -if (NOT "${NUMPY_INCLUDE_DIR}" STREQUAL "") - message(STATUS "NUMPY_INCLUDE_DIR: " ${NUMPY_INCLUDE_DIR}) - list(APPEND libshiboken_SRC sbknumpyarrayconverter.cpp) -else() - message(STATUS "NUMPY not found") -endif() - -set(APIEXTRACTOR_EXTRA_INCLUDES ${APIEXTRACTOR_EXTRA_INCLUDES} ${LIBXSLT_INCLUDE_DIR} ${LIBXML2_INCLUDE_DIR}) +# This is needed to let the header obey a variable in "pep384impl.h". +# Note: This must be set on the cpp file! +set_property(SOURCE "pep384impl.cpp" PROPERTY SKIP_UNITY_BUILD_INCLUSION ON) add_library(libshiboken SHARED ${libshiboken_SRC}) add_library(Shiboken6::libshiboken ALIAS libshiboken) @@ -88,10 +112,16 @@ target_include_directories(libshiboken PUBLIC ) if (NOT "${NUMPY_INCLUDE_DIR}" STREQUAL "") + message(STATUS "NUMPY_INCLUDE_DIR: " ${NUMPY_INCLUDE_DIR}) target_include_directories(libshiboken PRIVATE ${NUMPY_INCLUDE_DIR}) target_compile_definitions(libshiboken PRIVATE -DHAVE_NUMPY PRIVATE -DNPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION) +else() + message(STATUS "NUMPY not found") +endif() +if(SHIBOKEN_IS_CROSS_BUILD) + target_compile_definitions(libshiboken PRIVATE -DSHIBOKEN_NO_EMBEDDING_PYC=1) endif() shiboken_compute_python_includes() @@ -120,6 +150,8 @@ set_target_properties(libshiboken PROPERTIES OUTPUT_NAME "shiboken6${shiboken6_S SOVERSION ${libshiboken_SOVERSION} DEFINE_SYMBOL BUILD_LIBSHIBOKEN) +qfp_strip_library("libshiboken") + install(FILES autodecref.h basewrapper.h @@ -127,25 +159,36 @@ install(FILES bindingmanager.h gilstate.h helper.h + pyobjectholder.h sbkarrayconverter.h + sbkcontainer.h sbkconverter.h + sbkcpptonumpy.h sbkenum.h - sbkenum_p.h + sbkerrors.h + sbkfeature_base.h sbkmodule.h + sbknumpycheck.h + sbknumpyview.h sbkstring.h + sbkcppstring.h + sbksmartpointer.h sbkstaticstrings.h + sbktypefactory.h shiboken.h shibokenmacros.h threadstatesaver.h shibokenbuffer.h sbkpython.h + sbkwindows.h pep384impl.h + pep384ext.h voidptr.h bufferprocs_py37.h "${CMAKE_CURRENT_BINARY_DIR}/sbkversion.h" signature.h - signature/signature_p.h + signature_p.h DESTINATION include/shiboken6${shiboken6_SUFFIX}) install(TARGETS libshiboken EXPORT Shiboken6Targets @@ -153,4 +196,4 @@ install(TARGETS libshiboken EXPORT Shiboken6Targets ARCHIVE DESTINATION "${LIB_INSTALL_DIR}" RUNTIME DESTINATION bin) install(EXPORT Shiboken6Targets NAMESPACE Shiboken6:: - DESTINATION ${LIB_INSTALL_DIR}/cmake/Shiboken6-${shiboken6_VERSION}) + DESTINATION ${LIB_INSTALL_DIR}/cmake/Shiboken6) diff --git a/sources/shiboken6/libshiboken/autodecref.h b/sources/shiboken6/libshiboken/autodecref.h index 45a42d6a5..62a8584e1 100644 --- a/sources/shiboken6/libshiboken/autodecref.h +++ b/sources/shiboken6/libshiboken/autodecref.h @@ -1,47 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef AUTODECREF_H #define AUTODECREF_H #include "sbkpython.h" -#include "basewrapper.h" + +#include <utility> struct SbkObject; namespace Shiboken @@ -50,29 +15,27 @@ namespace Shiboken /** * AutoDecRef holds a PyObject pointer and decrement its reference counter when destroyed. */ -struct LIBSHIBOKEN_API AutoDecRef +struct AutoDecRef { public: AutoDecRef(const AutoDecRef &) = delete; - AutoDecRef(AutoDecRef &&) = delete; + AutoDecRef(AutoDecRef &&o) noexcept : m_pyObj{std::exchange(o.m_pyObj, nullptr)} {} AutoDecRef &operator=(const AutoDecRef &) = delete; - AutoDecRef &operator=(AutoDecRef &&) = delete; + AutoDecRef &operator=(AutoDecRef &&o) noexcept + { + m_pyObj = std::exchange(o.m_pyObj, nullptr); + return *this; + } - /** - * 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<PyObject *>(pyObj)) {} - /** - * AutoDecref constructor. - * To be used later with reset(): - */ - AutoDecRef() : m_pyObj(nullptr) {} + /// AutoDecRef constructor. + /// \param pyobj A borrowed reference to a Python object + explicit AutoDecRef(PyObject *pyObj) noexcept : m_pyObj(pyObj) {} + /// AutoDecRef constructor. + /// \param pyobj A borrowed reference to a wrapped Python object + explicit AutoDecRef(SbkObject *pyObj) noexcept : m_pyObj(reinterpret_cast<PyObject *>(pyObj)) {} + /// AutoDecref default constructor. + /// To be used later with reset(): + AutoDecRef() noexcept = default; /// Decref the borrowed python reference ~AutoDecRef() @@ -80,18 +43,19 @@ public: Py_XDECREF(m_pyObj); } - inline bool isNull() const { return m_pyObj == nullptr; } + [[nodiscard]] bool isNull() const { return m_pyObj == nullptr; } /// Returns the pointer of the Python object being held. - inline PyObject *object() { return m_pyObj; } - inline operator PyObject *() { return m_pyObj; } + [[nodiscard]] PyObject *object() const { return m_pyObj; } + [[nodiscard]] operator PyObject *() const { return m_pyObj; } #ifndef Py_LIMITED_API - inline operator PyTupleObject *() { return reinterpret_cast<PyTupleObject *>(m_pyObj); } + [[deprecated]] inline operator PyTupleObject *() + { return reinterpret_cast<PyTupleObject *>(m_pyObj); } #endif inline operator bool() const { return m_pyObj != nullptr; } inline PyObject *operator->() { return m_pyObj; } template<typename T> - T cast() + [[deprecated]] T cast() { return reinterpret_cast<T>(m_pyObj); } @@ -106,11 +70,18 @@ public: m_pyObj = other; Py_XDECREF(_py_tmp); } + + PyObject *release() + { + PyObject *result = m_pyObj; + m_pyObj = nullptr; + return result; + } + private: - PyObject *m_pyObj; + PyObject *m_pyObj = nullptr; }; } // namespace Shiboken #endif // AUTODECREF_H - diff --git a/sources/shiboken6/libshiboken/basewrapper.cpp b/sources/shiboken6/libshiboken/basewrapper.cpp index 42a63f398..1ac65c00c 100644 --- a/sources/shiboken6/libshiboken/basewrapper.cpp +++ b/sources/shiboken6/libshiboken/basewrapper.cpp @@ -1,48 +1,16 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "basewrapper.h" #include "basewrapper_p.h" #include "bindingmanager.h" #include "helper.h" +#include "pep384ext.h" #include "sbkconverter.h" #include "sbkenum.h" +#include "sbkerrors.h" +#include "sbkfeature_base.h" +#include "sbkmodule.h" #include "sbkstring.h" #include "sbkstaticstrings.h" #include "sbkstaticstrings_p.h" @@ -56,6 +24,7 @@ #include <algorithm> #include "threadstatesaver.h" #include "signature.h" +#include "signature_p.h" #include "voidptr.h" #include <iostream> @@ -68,7 +37,73 @@ namespace { void _destroyParentInfo(SbkObject *obj, bool keepReference); } -static void callDestructor(const Shiboken::DtorAccumulatorVisitor::DestructorEntries &dts) +namespace Shiboken +{ +// Walk through the first level of non-user-type Sbk base classes relevant for +// C++ object allocation. Return true from the predicate to terminate. +template <class Predicate> +bool walkThroughBases(PyTypeObject *currentType, Predicate predicate) +{ + PyObject *bases = currentType->tp_bases; + const Py_ssize_t numBases = PyTuple_Size(bases); + bool result = false; + for (Py_ssize_t i = 0; !result && i < numBases; ++i) { + auto type = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(bases, i)); + if (PyType_IsSubtype(type, SbkObject_TypeF()) != 0) { + result = PepType_SOTP(type)->is_user_type + ? walkThroughBases(type, predicate) : predicate(type); + } + } + return result; +} + +int getTypeIndexOnHierarchy(PyTypeObject *baseType, PyTypeObject *desiredType) +{ + int index = -1; + walkThroughBases(baseType, [&index, desiredType](PyTypeObject *node) { + ++index; + return PyType_IsSubtype(node, desiredType) != 0; + }); + return index; +} + +int getNumberOfCppBaseClasses(PyTypeObject *baseType) +{ + int count = 0; + walkThroughBases(baseType, [&count](PyTypeObject *) { + ++count; + return false; + }); + return count; +} + +std::vector<PyTypeObject *> getCppBaseClasses(PyTypeObject *baseType) +{ + std::vector<PyTypeObject *> cppBaseClasses; + walkThroughBases(baseType, [&cppBaseClasses](PyTypeObject *node) { + cppBaseClasses.push_back(node); + return false; + }); + return cppBaseClasses; +} + +using DestructorEntries = std::vector<DestructorEntry>; + +DestructorEntries getDestructorEntries(SbkObject *o) +{ + DestructorEntries result; + void **cptrs = o->d->cptr; + walkThroughBases(Py_TYPE(o), [&result, cptrs](PyTypeObject *node) { + auto *sotp = PepType_SOTP(node); + auto index = result.size(); + result.push_back(DestructorEntry{sotp->cpp_dtor, + cptrs[index]}); + return false; + }); + return result; +} + +static void callDestructor(const DestructorEntries &dts) { for (const auto &e : dts) { Shiboken::ThreadStateSaver threadSaver; @@ -77,6 +112,8 @@ static void callDestructor(const Shiboken::DtorAccumulatorVisitor::DestructorEnt } } +} // namespace Shiboken + extern "C" { @@ -88,13 +125,12 @@ void Sbk_object_dealloc(PyObject *self) // This was not needed before Python 3.8 (Python issue 35810) Py_DECREF(Py_TYPE(self)); } - Py_TYPE(self)->tp_free(self); + PepExt_TypeCallFree(self); } -static void SbkObjectTypeDealloc(PyTypeObject *pyType); -static PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *kwds); +static void SbkObjectType_tp_dealloc(PyTypeObject *pyType); +static PyTypeObject *SbkObjectType_tp_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds); -static SelectableFeatureHook SelectFeatureSet = nullptr; static DestroyQAppHook DestroyQApplication = nullptr; // PYSIDE-1470: Provide a hook to kill an Application from Shiboken. @@ -103,7 +139,24 @@ void setDestroyQApplication(DestroyQAppHook func) DestroyQApplication = func; } -static PyObject *Sbk_TypeGet___dict__(PyTypeObject *type, void *context); // forward +// PYSIDE-535: Use the C API in PyPy instead of `op->ob_dict`, directly +LIBSHIBOKEN_API PyObject *SbkObject_GetDict_NoRef(PyObject *op) +{ + assert(Shiboken::Object::checkType(op)); +#ifdef PYPY_VERSION + Shiboken::GilState state; + auto *ret = PyObject_GenericGetDict(op, nullptr); + Py_DECREF(ret); + return ret; +#else + auto *sbkObj = reinterpret_cast<SbkObject *>(op); + if (!sbkObj->ob_dict) { + Shiboken::GilState state; + sbkObj->ob_dict = PyDict_New(); + } + return sbkObj->ob_dict; +#endif +} static int check_set_special_type_attr(PyTypeObject *type, PyObject *value, const char *name) @@ -123,20 +176,18 @@ check_set_special_type_attr(PyTypeObject *type, PyObject *value, const char *nam // PYSIDE-1177: Add a setter to allow setting type doc. static int -type_set_doc(PyTypeObject *type, PyObject *value, void *context) +type_set_doc(PyTypeObject *type, PyObject *value, void * /* context */) { if (!check_set_special_type_attr(type, value, "__doc__")) return -1; PyType_Modified(type); - return PyDict_SetItem(type->tp_dict, Shiboken::PyMagicName::doc(), value); + Shiboken::AutoDecRef tpDict(PepType_GetDict(type)); + return PyDict_SetItem(tpDict.object(), Shiboken::PyMagicName::doc(), value); } // PYSIDE-908: The function PyType_Modified does not work in PySide, so we need to -// explicitly pass __doc__. For __signature__ it _did_ actually work, because -// it was not existing before. We add them both for clarity. -static PyGetSetDef SbkObjectType_Type_getsetlist[] = { - {const_cast<char *>("__signature__"), reinterpret_cast<getter>(Sbk_TypeGet___signature__), - nullptr, nullptr, nullptr}, +// explicitly pass __doc__. +static PyGetSetDef SbkObjectType_tp_getset[] = { {const_cast<char *>("__doc__"), reinterpret_cast<getter>(Sbk_TypeGet___doc__), reinterpret_cast<setter>(type_set_doc), nullptr, nullptr}, {const_cast<char *>("__dict__"), reinterpret_cast<getter>(Sbk_TypeGet___dict__), @@ -144,56 +195,99 @@ static PyGetSetDef SbkObjectType_Type_getsetlist[] = { {nullptr, nullptr, nullptr, nullptr, nullptr} // Sentinel }; -static PyObject *(*type_getattro)(PyObject *type, PyObject *name); // forward -static PyObject *mangled_type_getattro(PyTypeObject *type, PyObject *name); // forward - -static PyType_Slot SbkObjectType_Type_slots[] = { - {Py_tp_dealloc, reinterpret_cast<void *>(SbkObjectTypeDealloc)}, - {Py_tp_getattro, reinterpret_cast<void *>(mangled_type_getattro)}, - {Py_tp_base, static_cast<void *>(&PyType_Type)}, - {Py_tp_alloc, reinterpret_cast<void *>(PyType_GenericAlloc)}, - {Py_tp_new, reinterpret_cast<void *>(SbkObjectTypeTpNew)}, - {Py_tp_free, reinterpret_cast<void *>(PyObject_GC_Del)}, - {Py_tp_getset, reinterpret_cast<void *>(SbkObjectType_Type_getsetlist)}, - {0, nullptr} -}; -static PyType_Spec SbkObjectType_Type_spec = { - "1:Shiboken.ObjectType", - 0, - sizeof(PyMemberDef), - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, - SbkObjectType_Type_slots, -}; +static PyTypeObject *createObjectTypeType() +{ + // PYSIDE-2676: When using the new type extension, we need to use an + // extra meta type that provides the extra size. + // This is a hairy part of Python 3.12 . + // + // The problem here is that we use the type extension both in types + // and also in meta types. This was invisible with extender dicts. + // Please study carefully: + // https://docs.python.org/3/c-api/type.html#c.PyType_Spec.basicsize + + PyType_Slot SbkObjectTypeMeta_Type_slots[] = { + {Py_tp_base, static_cast<void *>(&PyType_Type)}, + {Py_tp_alloc, reinterpret_cast<void *>(PyType_GenericAlloc)}, + {0, nullptr} + }; + + PyType_Spec SbkObjectTypeMeta_Type_spec = { + "1:Shiboken.ObjectTypeMeta", + -long(sizeof(SbkObjectTypePrivate)), + 0, // sizeof(PyMemberDef), not for PyPy without a __len__ defined + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_TYPE_SUBCLASS, + SbkObjectTypeMeta_Type_slots, + }; + + auto specMeta = &SbkObjectTypeMeta_Type_spec; + + PyType_Slot SbkObjectType_Type_slots[] = { + {Py_tp_dealloc, reinterpret_cast<void *>(SbkObjectType_tp_dealloc)}, + {Py_tp_getattro, reinterpret_cast<void *>(mangled_type_getattro)}, + {Py_tp_base, static_cast<void *>(&PyType_Type)}, + {Py_tp_alloc, reinterpret_cast<void *>(PyType_GenericAlloc)}, + {Py_tp_new, reinterpret_cast<void *>(SbkObjectType_tp_new)}, + {Py_tp_free, reinterpret_cast<void *>(PyObject_GC_Del)}, + {Py_tp_getset, reinterpret_cast<void *>(SbkObjectType_tp_getset)}, + {0, nullptr} + }; + + // PYSIDE-535: The tp_itemsize field is inherited and does not need to be set. + // In PyPy, it _must_ not be set, because it would have the meanin + // that a `__len__` field must be defined. Not doing so creates + // a hard-to-find crash. + // + // PYSIDE-2230: In Python < 3.12, the decision which base class should create + // the instance is arbitrarily drawn by the size of the type. + // Ignoring this creates a bug in the new version of bug_825 that + // selects the wrong metatype. + // + PyType_Spec SbkObjectType_Type_spec = { + "1:Shiboken.ObjectType", + static_cast<int>(PyType_Type.tp_basicsize) + 1, // see above + 0, // sizeof(PyMemberDef), not for PyPy without a __len__ defined + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_TYPE_SUBCLASS, + SbkObjectType_Type_slots, + }; + + PyType_Spec SbkObjectType_Type_spec_312 = { + "1:Shiboken.ObjectType", + -long(sizeof(SbkObjectTypePrivate)), + 0, // sizeof(PyMemberDef), not for PyPy without a __len__ defined + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_TYPE_SUBCLASS, + SbkObjectType_Type_slots, + }; + + if (_PepRuntimeVersion() >= 0x030C00) { + auto *meta = SbkType_FromSpec(specMeta); + auto spec = &SbkObjectType_Type_spec_312; + return SbkType_FromSpecWithMeta(spec, meta); + } + + auto spec = &SbkObjectType_Type_spec; + return SbkType_FromSpec(spec); +} PyTypeObject *SbkObjectType_TypeF(void) { - static PyTypeObject *type = nullptr; - if (!type) { - // PYSIDE-1019: Insert the default tp_getattro explicitly here - // so we can overwrite it a bit. - type_getattro = PyType_Type.tp_getattro; - type = reinterpret_cast<PyTypeObject *>(SbkType_FromSpec(&SbkObjectType_Type_spec)); - } + static auto *type = createObjectTypeType(); return type; } static PyObject *SbkObjectGetDict(PyObject *pObj, void *) { - auto *obj = reinterpret_cast<SbkObject *>(pObj); - if (!obj->ob_dict) - obj->ob_dict = PyDict_New(); - if (!obj->ob_dict) - return nullptr; - Py_INCREF(obj->ob_dict); - return obj->ob_dict; + auto ret = SbkObject_GetDict_NoRef(pObj); + Py_XINCREF(ret); + return ret; } -static PyGetSetDef SbkObjectGetSetList[] = { +static PyGetSetDef SbkObject_tp_getset[] = { {const_cast<char *>("__dict__"), SbkObjectGetDict, nullptr, nullptr, nullptr}, {nullptr, nullptr, nullptr, nullptr, nullptr} // Sentinel }; -static int SbkObject_traverse(PyObject *self, visitproc visit, void *arg) +static int SbkObject_tp_traverse(PyObject *self, visitproc visit, void *arg) { auto *sbkSelf = reinterpret_cast<SbkObject *>(self); @@ -214,14 +308,12 @@ static int SbkObject_traverse(PyObject *self, visitproc visit, void *arg) if (sbkSelf->ob_dict) Py_VISIT(sbkSelf->ob_dict); -#if PY_VERSION_HEX >= 0x03090000 // This was not needed before Python 3.9 (Python issue 35810 and 40217) Py_VISIT(Py_TYPE(self)); -#endif return 0; } -static int SbkObject_clear(PyObject *self) +static int SbkObject_tp_clear(PyObject *self) { auto *sbkSelf = reinterpret_cast<SbkObject *>(self); @@ -237,45 +329,53 @@ static int SbkObject_clear(PyObject *self) return 0; } -static PyObject *SbkObject_GenericGetAttr(PyObject *obj, PyObject *name); -static int SbkObject_GenericSetAttr(PyObject *obj, PyObject *name, PyObject *value); +static PyTypeObject *createObjectType() +{ + PyType_Slot SbkObject_Type_slots[] = { + {Py_tp_getattro, reinterpret_cast<void *>(SbkObject_GenericGetAttr)}, + {Py_tp_setattro, reinterpret_cast<void *>(SbkObject_GenericSetAttr)}, + {Py_tp_dealloc, reinterpret_cast<void *>(SbkDeallocWrapperWithPrivateDtor)}, + {Py_tp_traverse, reinterpret_cast<void *>(SbkObject_tp_traverse)}, + {Py_tp_clear, reinterpret_cast<void *>(SbkObject_tp_clear)}, + // unsupported: {Py_tp_weaklistoffset, (void *)offsetof(SbkObject, weakreflist)}, + {Py_tp_getset, reinterpret_cast<void *>(SbkObject_tp_getset)}, + // unsupported: {Py_tp_dictoffset, (void *)offsetof(SbkObject, ob_dict)}, + {0, nullptr} + }; + + PyType_Spec SbkObject_Type_spec = { + "1:Shiboken.Object", + sizeof(SbkObject), + 0, + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, + SbkObject_Type_slots, + }; + + // PYSIDE-2230: When creating this type, we cannot easily handle the metaclass. + // In versions < Python 3.12, the metaclass can only be set + // indirectly by a base which has that metaclass. + // But before 3.12 is the minimum version, we cannot use the new + // function, although we would need this for 3.12 :-D + // We do a special patching here that is triggered through Py_None. + auto *type = SbkType_FromSpec_BMDWB(&SbkObject_Type_spec, + Py_None, // bases, spectial flag! + SbkObjectType_TypeF(), + offsetof(SbkObject, ob_dict), + offsetof(SbkObject, weakreflist), + nullptr); // bufferprocs + return type; +} -static PyType_Slot SbkObject_Type_slots[] = { - {Py_tp_getattro, reinterpret_cast<void *>(SbkObject_GenericGetAttr)}, - {Py_tp_setattro, reinterpret_cast<void *>(SbkObject_GenericSetAttr)}, - {Py_tp_dealloc, reinterpret_cast<void *>(SbkDeallocWrapperWithPrivateDtor)}, - {Py_tp_traverse, reinterpret_cast<void *>(SbkObject_traverse)}, - {Py_tp_clear, reinterpret_cast<void *>(SbkObject_clear)}, - // unsupported: {Py_tp_weaklistoffset, (void *)offsetof(SbkObject, weakreflist)}, - {Py_tp_getset, reinterpret_cast<void *>(SbkObjectGetSetList)}, - // unsupported: {Py_tp_dictoffset, (void *)offsetof(SbkObject, ob_dict)}, - {0, nullptr} -}; -static PyType_Spec SbkObject_Type_spec = { - "1:Shiboken.Object", - sizeof(SbkObject), - 0, - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, - SbkObject_Type_slots, -}; +PyTypeObject *SbkObject_TypeF(void) +{ + static auto *type = createObjectType(); // bufferprocs + return type; +} static const char *SbkObject_SignatureStrings[] = { "Shiboken.Object(self)", nullptr}; // Sentinel -SbkObjectType *SbkObject_TypeF(void) -{ - static PyTypeObject *type = nullptr; - if (!type) { - type = reinterpret_cast<PyTypeObject *>(SbkType_FromSpec(&SbkObject_Type_spec)); - Py_TYPE(type) = SbkObjectType_TypeF(); - Py_INCREF(Py_TYPE(type)); - type->tp_weaklistoffset = offsetof(SbkObject, weakreflist); - type->tp_dictoffset = offsetof(SbkObject, ob_dict); - } - return reinterpret_cast<SbkObjectType *>(type); -} - static int mainThreadDeletionHandler(void *) { if (Py_IsInitialized()) @@ -287,7 +387,6 @@ static void SbkDeallocWrapperCommon(PyObject *pyObj, bool canDelete) { auto *sbkObj = reinterpret_cast<SbkObject *>(pyObj); PyTypeObject *pyType = Py_TYPE(pyObj); - auto *sbkType = reinterpret_cast<SbkObjectType *>(pyType); // Need to decref the type if this is the dealloc func; if type // is subclassed, that dealloc func will decref (see subtype_dealloc @@ -333,15 +432,14 @@ static void SbkDeallocWrapperCommon(PyObject *pyObj, bool canDelete) PyObject_ClearWeakRefs(pyObj); // If I have ownership and is valid delete C++ pointer - auto *sotp = PepType_SOTP(sbkType); + auto *sotp = PepType_SOTP(pyType); canDelete &= sbkObj->d->hasOwnership && sbkObj->d->validCppObject; if (canDelete) { if (sotp->delete_in_main_thread && Shiboken::currentThreadId() != Shiboken::mainThreadId()) { auto &bindingManager = Shiboken::BindingManager::instance(); if (sotp->is_multicpp) { - Shiboken::DtorAccumulatorVisitor visitor(sbkObj); - Shiboken::walkThroughClassHierarchy(Py_TYPE(pyObj), &visitor); - for (const auto &e : visitor.entries()) + const auto entries = Shiboken::getDestructorEntries(sbkObj); + for (const auto &e : entries) bindingManager.addToDeletionInMainThread(e); } else { Shiboken::DestructorEntry e{sotp->cpp_dtor, sbkObj->d->cptr[0]}; @@ -359,10 +457,9 @@ static void SbkDeallocWrapperCommon(PyObject *pyObj, bool canDelete) if (canDelete) { if (sotp->is_multicpp) { - Shiboken::DtorAccumulatorVisitor visitor(sbkObj); - Shiboken::walkThroughClassHierarchy(Py_TYPE(pyObj), &visitor); + const auto entries = Shiboken::getDestructorEntries(sbkObj); Shiboken::Object::deallocData(sbkObj, true); - callDestructor(visitor.entries()); + callDestructor(entries); } else { void *cptr = sbkObj->d->cptr[0]; Shiboken::Object::deallocData(sbkObj, true); @@ -388,6 +485,17 @@ static void SbkDeallocWrapperCommon(PyObject *pyObj, bool canDelete) } } +static inline PyObject *_Sbk_NewVarObject(PyTypeObject *type) +{ + // PYSIDE-1970: Support __slots__, implemented by PyVarObject + auto const baseSize = sizeof(SbkObject); + auto varCount = Py_SIZE(type); + auto *self = PyObject_GC_NewVar(PyObject, type, varCount); + if (varCount) + std::memset(reinterpret_cast<char *>(self) + baseSize, 0, varCount * sizeof(void *)); + return self; +} + void SbkDeallocWrapper(PyObject *pyObj) { SbkDeallocWrapperCommon(pyObj, true); @@ -405,15 +513,18 @@ void SbkDeallocWrapperWithPrivateDtor(PyObject *self) SbkDeallocWrapperCommon(self, false); } -void SbkObjectTypeDealloc(PyTypeObject *type) +void SbkObjectType_tp_dealloc(PyTypeObject *sbkType) { - SbkObjectType *sbkType = reinterpret_cast<SbkObjectType *>(type); SbkObjectTypePrivate *sotp = PepType_SOTP(sbkType); - auto pyObj = reinterpret_cast<PyObject *>(type); + auto pyObj = reinterpret_cast<PyObject *>(sbkType); PyObject_GC_UnTrack(pyObj); -#ifndef Py_LIMITED_API +#if !defined(Py_LIMITED_API) && !defined(PYPY_VERSION) +# if PY_VERSION_HEX >= 0x030A0000 + Py_TRASHCAN_BEGIN(pyObj, 1); +# else Py_TRASHCAN_SAFE_BEGIN(pyObj); +# endif #endif if (sotp) { if (sotp->user_data && sotp->d_func) { @@ -422,12 +533,16 @@ void SbkObjectTypeDealloc(PyTypeObject *type) } free(sotp->original_name); sotp->original_name = nullptr; - if (!Shiboken::ObjectType::isUserType(type)) + if (!Shiboken::ObjectType::isUserType(sbkType)) Shiboken::Conversions::deleteConverter(sotp->converter); PepType_SOTP_delete(sbkType); } -#ifndef Py_LIMITED_API +#if !defined(Py_LIMITED_API) && !defined(PYPY_VERSION) +# if PY_VERSION_HEX >= 0x030A0000 + Py_TRASHCAN_END; +# else Py_TRASHCAN_SAFE_END(pyObj); +# endif #endif if (PepRuntime_38_flag) { // PYSIDE-939: Handling references correctly. @@ -451,7 +566,8 @@ PyObject *MakeQAppWrapper(PyTypeObject *type) // protecting from multiple application instances if (!(type == nullptr || qApp_last == Py_None)) { - const char *res_name = PepType_GetNameStr(Py_TYPE(qApp_last)); + const char *res_name = qApp_last != nullptr + ? PepType_GetNameStr(Py_TYPE(qApp_last)) : "<Unknown>"; const char *type_name = PepType_GetNameStr(type); PyErr_Format(PyExc_RuntimeError, "Please destroy the %s singleton before" " creating a new %s instance.", res_name, type_name); @@ -459,7 +575,7 @@ PyObject *MakeQAppWrapper(PyTypeObject *type) } // monitoring the last application state - PyObject *qApp_curr = type != nullptr ? PyObject_GC_New(PyObject, type) : Py_None; + PyObject *qApp_curr = type != nullptr ? _Sbk_NewVarObject(type) : Py_None; static PyObject *builtins = PyEval_GetBuiltins(); if (PyDict_SetItem(builtins, Shiboken::PyName::qApp(), qApp_curr) < 0) return nullptr; @@ -469,109 +585,14 @@ PyObject *MakeQAppWrapper(PyTypeObject *type) // exactly the needed reference that keeps qApp alive from alone! Py_INCREF(qApp_curr); // PYSIDE-1470: As a side effect, the interactive "_" variable tends to - // create reference cycles. It was found when using gc.collect(). But using - // PyGC_collect() inside the C code had no effect in the interactive shell. - // The cycle exists only in the eval loop of the interpreter! - if (PyDict_GetItem(builtins, Shiboken::PyName::underscore())) - PyDict_SetItem(builtins, Shiboken::PyName::underscore(), Py_None); + // create reference cycles. This is disturbing when trying + // to remove qApp with del. + // PYSIDE-1758: Since we moved to an explicit qApp.shutdown() call, we + // no longer initialize "_" with Py_None. return qApp_curr; } -////////////////////////////////////////////////////////////////////////////// -// -// PYSIDE-1019: Support switchable extensions -// -// We simply exchange the complete class dicts. -// -// This is done in which replaces -// --------------- -------------- -// mangled_type_getattro type_getattro -// Sbk_TypeGet___dict__ type_dict -// SbkObject_GenericGetAttr PyObject_GenericGetAttr -// SbkObject_GenericSetAttr PyObject_GenericSetAttr -// - -SelectableFeatureHook initSelectableFeature(SelectableFeatureHook func) -{ - auto ret = SelectFeatureSet; - SelectFeatureSet = func; - return ret; -} - -static PyObject *mangled_type_getattro(PyTypeObject *type, PyObject *name) -{ - /* - * Note: This `type_getattro` version is only the default that comes - * from `PyType_Type.tp_getattro`. This does *not* interfere in any way - * with the complex `tp_getattro` of `QObject` and other instances. - * What we change here is the meta class of `QObject`. - */ - if (SelectFeatureSet != nullptr) - type->tp_dict = SelectFeatureSet(type); - return type_getattro(reinterpret_cast<PyObject *>(type), name); -} - -static PyObject *Sbk_TypeGet___dict__(PyTypeObject *type, void *context) -{ - /* - * This is the override for getting a dict. - */ - auto dict = type->tp_dict; - if (dict == nullptr) - Py_RETURN_NONE; - if (SelectFeatureSet != nullptr) - dict = SelectFeatureSet(type); - return PyDictProxy_New(dict); -} - -// These functions replace the standard PyObject_Generic(Get|Set)Attr functions. -// They provide the default that "object" inherits. -// Everything else is directly handled by cppgenerator that calls `Feature::Select`. -static PyObject *SbkObject_GenericGetAttr(PyObject *obj, PyObject *name) -{ - auto type = Py_TYPE(obj); - if (SelectFeatureSet != nullptr) - type->tp_dict = SelectFeatureSet(type); - return PyObject_GenericGetAttr(obj, name); -} - -static int SbkObject_GenericSetAttr(PyObject *obj, PyObject *name, PyObject *value) -{ - auto type = Py_TYPE(obj); - if (SelectFeatureSet != nullptr) - type->tp_dict = SelectFeatureSet(type); - return PyObject_GenericSetAttr(obj, name, value); -} - -// Caching the select Id. -int SbkObjectType_GetReserved(PyTypeObject *type) -{ - auto *sbkType = reinterpret_cast<SbkObjectType *>(type); - return PepType_SOTP(sbkType)->pyside_reserved_bits; -} - -void SbkObjectType_SetReserved(PyTypeObject *type, int value) -{ - auto *sbkType = reinterpret_cast<SbkObjectType *>(type); - PepType_SOTP(sbkType)->pyside_reserved_bits = value; -} - -const char **SbkObjectType_GetPropertyStrings(PyTypeObject *type) -{ - auto *sbkType = reinterpret_cast<SbkObjectType *>(type); - return PepType_SOTP(sbkType)->propertyStrings; -} - -void SbkObjectType_SetPropertyStrings(PyTypeObject *type, const char **strings) -{ - auto *sbkType = reinterpret_cast<SbkObjectType *>(type); - PepType_SOTP(sbkType)->propertyStrings = strings; -} - -// -////////////////////////////////////////////////////////////////////////////// - -static PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *kwds) +static PyTypeObject *SbkObjectType_tp_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) { // Check if all bases are new style before calling type.tp_new // Was causing gc assert errors in test_bug704.py when @@ -587,7 +608,7 @@ static PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyOb PyObject *dict; static const char *kwlist[] = { "name", "bases", "dict", nullptr}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO!O!:sbktype", const_cast<char **>(kwlist), + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO!O!:sbktype", const_cast<char **>(kwlist), &name, &PyTuple_Type, &pyBases, &PyDict_Type, &dict)) @@ -595,32 +616,33 @@ static PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyOb for (int i=0, i_max=PyTuple_GET_SIZE(pyBases); i < i_max; i++) { PyObject *baseType = PyTuple_GET_ITEM(pyBases, i); - if (reinterpret_cast<PyTypeObject *>(baseType)->tp_new == SbkDummyNew) { + if (PepExt_Type_GetNewSlot(reinterpret_cast<PyTypeObject *>(baseType)) == SbkDummyNew) { // PYSIDE-595: A base class does not allow inheritance. - return SbkDummyNew(metatype, args, kwds); + return reinterpret_cast<PyTypeObject *>(SbkDummyNew(metatype, args, kwds)); } } - // The meta type creates a new type when the Python programmer extends a wrapped C++ class. - auto type_new = reinterpret_cast<newfunc>(PyType_Type.tp_new); - - // PYSIDE-939: This is a temporary patch that circumvents the problem - // with Py_TPFLAGS_METHOD_DESCRIPTOR until this is finally solved. - // PyType_Ready uses mro(). We need to temporarily remove the flag from it's type. - // We cannot use PyMethodDescr_Type since it is not exported by Python 2.7 . - static PyTypeObject *PyMethodDescr_TypePtr = Py_TYPE( - PyObject_GetAttr(reinterpret_cast<PyObject *>(&PyType_Type), Shiboken::PyName::mro())); - auto hold = PyMethodDescr_TypePtr->tp_flags; - PyMethodDescr_TypePtr->tp_flags &= ~Py_TPFLAGS_METHOD_DESCRIPTOR; - auto *newType = reinterpret_cast<SbkObjectType *>(type_new(metatype, args, kwds)); - PyMethodDescr_TypePtr->tp_flags = hold; + // PYSIDE-939: This is still a temporary patch that circumvents the problem + // with Py_TPFLAGS_METHOD_DESCRIPTOR. The problem exists in Python 3.8 + // until 3.9.12, only. We check the runtime and hope for this version valishing. + // https://github.com/python/cpython/issues/92112 will not be fixed for 3.8 :/ + PyTypeObject *newType{}; + static auto triplet = _PepRuntimeVersion(); + if (triplet >= (3 << 16 | 8 << 8 | 0) && triplet < (3 << 16 | 9 << 8 | 13)) { + auto hold = PyMethodDescr_Type.tp_flags; + PyMethodDescr_Type.tp_flags &= ~Py_TPFLAGS_METHOD_DESCRIPTOR; + newType = PepType_Type_tp_new(metatype, args, kwds); + PyMethodDescr_Type.tp_flags = hold; + } else { + newType = PepType_Type_tp_new(metatype, args, kwds); + } if (!newType) return nullptr; SbkObjectTypePrivate *sotp = PepType_SOTP(newType); - const auto bases = Shiboken::getCppBaseClasses(&newType->type); + const auto bases = Shiboken::getCppBaseClasses(newType); if (bases.size() == 1) { SbkObjectTypePrivate *parentType = PepType_SOTP(bases.front()); sotp->mi_offsets = parentType->mi_offsets; @@ -653,20 +675,20 @@ static PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyOb // PYSIDE-1463: Prevent feature switching while in the creation process auto saveFeature = initSelectableFeature(nullptr); - for (SbkObjectType *base : bases) { + for (PyTypeObject *base : bases) { sotp = PepType_SOTP(base); if (sotp->subtype_init) sotp->subtype_init(newType, args, kwds); } initSelectableFeature(saveFeature); - return reinterpret_cast<PyObject *>(newType); + return newType; } -static PyObject *_setupNew(SbkObject *self, PyTypeObject *subtype) +static PyObject *_setupNew(PyObject *obSelf, PyTypeObject *subtype) { auto *obSubtype = reinterpret_cast<PyObject *>(subtype); - auto *sbkSubtype = reinterpret_cast<SbkObjectType *>(subtype); - auto *obSelf = reinterpret_cast<PyObject *>(self); + auto *sbkSubtype = subtype; + auto *self = reinterpret_cast<SbkObject *>(obSelf); Py_INCREF(obSubtype); auto d = new SbkObjectPrivate; @@ -682,25 +704,27 @@ static PyObject *_setupNew(SbkObject *self, PyTypeObject *subtype) d->parentInfo = nullptr; d->referredObjects = nullptr; d->cppObjectCreated = 0; + d->isQAppSingleton = 0; self->ob_dict = nullptr; self->weakreflist = nullptr; self->d = d; PyObject_GC_Track(obSelf); - return reinterpret_cast<PyObject *>(self); + return obSelf; } -PyObject *SbkObjectTpNew(PyTypeObject *subtype, PyObject *, PyObject *) +PyObject *SbkObject_tp_new(PyTypeObject *subtype, PyObject * /* args */, PyObject * /* kwds */) { - SbkObject *self = PyObject_GC_New(SbkObject, subtype); + PyObject *self = _Sbk_NewVarObject(subtype); return _setupNew(self, subtype); } -PyObject *SbkQAppTpNew(PyTypeObject *subtype, PyObject *, PyObject *) +PyObject *SbkQApp_tp_new(PyTypeObject *subtype, PyObject *, PyObject *) { - auto self = reinterpret_cast<SbkObject *>(MakeQAppWrapper(subtype)); + auto *obSelf = MakeQAppWrapper(subtype); + auto *self = reinterpret_cast<SbkObject *>(obSelf); if (self == nullptr) return nullptr; - auto ret = _setupNew(self, subtype); + auto ret = _setupNew(obSelf, subtype); auto priv = self->d; priv->isQAppSingleton = 1; return ret; @@ -709,57 +733,12 @@ PyObject *SbkQAppTpNew(PyTypeObject *subtype, PyObject *, PyObject *) PyObject *SbkDummyNew(PyTypeObject *type, PyObject *, PyObject *) { // PYSIDE-595: Give the same error as type_call does when tp_new is NULL. + const char regret[] = "¯\\_(ツ)_/¯"; PyErr_Format(PyExc_TypeError, - "cannot create '%.100s' instances ¯\\_(ツ)_/¯", - type->tp_name); + "cannot create '%.100s' instances %s", type->tp_name, regret); return nullptr; } -PyObject *SbkType_FromSpec(PyType_Spec *spec) -{ - return SbkType_FromSpecWithBases(spec, nullptr); -} - -PyObject *SbkType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) -{ - // PYSIDE-1286: Generate correct __module__ and __qualname__ - // The name field can now be extended by an "n:" prefix which is - // the number of modules in the name. The default is 1. - // - // Example: - // "2:mainmod.submod.mainclass.subclass" - // results in - // __module__ : "mainmod.submod" - // __qualname__ : "mainclass.subclass" - // __name__ : "subclass" - - PyType_Spec new_spec = *spec; - const char *colon = strchr(spec->name, ':'); - assert(colon); - int package_level = atoi(spec->name); - const char *mod = new_spec.name = colon + 1; - - PyObject *type = PyType_FromSpecWithBases(&new_spec, bases); - if (type == nullptr) - return nullptr; - - const char *qual = mod; - for (int idx = package_level; idx > 0; --idx) { - const char *dot = strchr(qual, '.'); - if (!dot) - break; - qual = dot + 1; - } - int mlen = qual - mod - 1; - Shiboken::AutoDecRef module(Shiboken::String::fromCString(mod, mlen)); - Shiboken::AutoDecRef qualname(Shiboken::String::fromCString(qual)); - if (PyObject_SetAttr(type, Shiboken::PyMagicName::module(), module) < 0) - return nullptr; - if (PyObject_SetAttr(type, Shiboken::PyMagicName::qualname(), qualname) < 0) - return nullptr; - return type; -} - // PYSIDE-74: Fallback used in all types now. PyObject *FallbackRichCompare(PyObject *self, PyObject *other, int op) { @@ -787,6 +766,12 @@ PyObject *FallbackRichCompare(PyObject *self, PyObject *other, int op) return res; } +bool SbkObjectType_Check(PyTypeObject *type) +{ + static auto *meta = SbkObjectType_TypeF(); + return Py_TYPE(type) == meta || PyType_IsSubtype(Py_TYPE(type), meta); +} + } //extern "C" @@ -811,53 +796,9 @@ void _destroyParentInfo(SbkObject *obj, bool keepReference) namespace Shiboken { -bool walkThroughClassHierarchy(PyTypeObject *currentType, HierarchyVisitor *visitor) -{ - PyObject *bases = currentType->tp_bases; - Py_ssize_t numBases = PyTuple_GET_SIZE(bases); - bool result = false; - for (int i = 0; !result && i < numBases; ++i) { - auto type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(bases, i)); - if (PyType_IsSubtype(type, reinterpret_cast<PyTypeObject *>(SbkObject_TypeF()))) { - auto sbkType = reinterpret_cast<SbkObjectType *>(type); - result = PepType_SOTP(sbkType)->is_user_type - ? walkThroughClassHierarchy(type, visitor) : visitor->visit(sbkType); - } - } - return result; -} // Wrapper metatype and base type ---------------------------------------------------------- -HierarchyVisitor::HierarchyVisitor() = default; -HierarchyVisitor::~HierarchyVisitor() = default; - -bool BaseCountVisitor::visit(SbkObjectType *) -{ - m_count++; - return false; -} - -bool BaseAccumulatorVisitor::visit(SbkObjectType *node) -{ - m_bases.push_back(node); - return false; -} - -bool GetIndexVisitor::visit(SbkObjectType *node) -{ - m_index++; - return PyType_IsSubtype(reinterpret_cast<PyTypeObject *>(node), m_desiredType); -} - -bool DtorAccumulatorVisitor::visit(SbkObjectType *node) -{ - auto *sotp = PepType_SOTP(node); - m_entries.push_back(DestructorEntry{sotp->cpp_dtor, - m_pyObject->d->cptr[m_entries.size()]}); - return false; -} - void _initMainThreadId(); // helper.cpp namespace Conversions { void init(); } @@ -875,13 +816,10 @@ void init() //Init private data Pep384_Init(); - if (PyType_Ready(SbkEnumType_TypeF()) < 0) - Py_FatalError("[libshiboken] Failed to initialize Shiboken.SbkEnumType metatype."); - if (PyType_Ready(SbkObjectType_TypeF()) < 0) Py_FatalError("[libshiboken] Failed to initialize Shiboken.BaseWrapperType metatype."); - if (PyType_Ready(reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())) < 0) + if (PyType_Ready(SbkObject_TypeF()) < 0) Py_FatalError("[libshiboken] Failed to initialize Shiboken.BaseWrapper type."); VoidPtr::init(); @@ -890,14 +828,20 @@ void init() } // PYSIDE-1415: Publish Shiboken objects. -void initSignature(PyObject *module) +// PYSIDE-1735: Initialize the whole Shiboken startup. +void initShibokenSupport(PyObject *module) { - auto type = reinterpret_cast<PyTypeObject *>(SbkObject_TypeF()); - if (InitSignatureStrings(type, SbkObject_SignatureStrings) < 0) - return; - Py_INCREF(SbkObject_TypeF()); PyModule_AddObject(module, "Object", reinterpret_cast<PyObject *>(SbkObject_TypeF())); + + // PYSIDE-1735: When the initialization was moved into Shiboken import, this + // Py_INCREF became necessary. No idea why. + Py_INCREF(module); + init_shibokensupport_module(); + + auto *type = SbkObject_TypeF(); + if (InitSignatureStrings(type, SbkObject_SignatureStrings) < 0) + Py_FatalError("Error in initShibokenSupport"); } // setErrorAboutWrongArguments now gets overload info from the signature module. @@ -907,19 +851,48 @@ void setErrorAboutWrongArguments(PyObject *args, const char *funcName, PyObject SetError_Argument(args, funcName, info); } -class FindBaseTypeVisitor : public HierarchyVisitor +PyObject *returnWrongArguments(PyObject *args, const char *funcName, PyObject *info) { -public: - explicit FindBaseTypeVisitor(PyTypeObject *typeToFind) : m_typeToFind(typeToFind) {} + setErrorAboutWrongArguments(args, funcName, info); + return {}; +} - bool visit(SbkObjectType *node) override - { - return reinterpret_cast<PyTypeObject *>(node) == m_typeToFind; - } +int returnWrongArguments_Zero(PyObject *args, const char *funcName, PyObject *info) +{ + setErrorAboutWrongArguments(args, funcName, info); + return 0; +} -private: - PyTypeObject *m_typeToFind; -}; +int returnWrongArguments_MinusOne(PyObject *args, const char *funcName, PyObject *info) +{ + setErrorAboutWrongArguments(args, funcName, info); + return -1; +} + +PyObject *returnFromRichCompare(PyObject *result) +{ + if (result && !PyErr_Occurred()) + return result; + Shiboken::Errors::setOperatorNotImplemented(); + return {}; +} + +PyObject *checkInvalidArgumentCount(Py_ssize_t numArgs, Py_ssize_t minArgs, Py_ssize_t maxArgs) +{ + PyObject *result = nullptr; + // for seterror_argument(), signature/errorhandler.py + if (numArgs > maxArgs) { + static PyObject *const tooMany = Shiboken::String::createStaticString(">"); + result = tooMany; + Py_INCREF(result); + } else if (numArgs < minArgs) { + static PyObject *const tooFew = Shiboken::String::createStaticString("<"); + static PyObject *const noArgs = Shiboken::String::createStaticString("0"); + result = numArgs > 0 ? tooFew : noArgs; + Py_INCREF(result); + } + return result; +} std::vector<SbkObject *> splitPyObject(PyObject *pyObj) { @@ -951,44 +924,42 @@ namespace ObjectType bool checkType(PyTypeObject *type) { - return PyType_IsSubtype(type, reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())) != 0; + return PyType_IsSubtype(type, SbkObject_TypeF()) != 0; } bool isUserType(PyTypeObject *type) { - auto *objType = reinterpret_cast<SbkObjectType *>(type); - return checkType(type) && PepType_SOTP(objType)->is_user_type; + return checkType(type) && PepType_SOTP(type)->is_user_type; } bool canCallConstructor(PyTypeObject *myType, PyTypeObject *ctorType) { - FindBaseTypeVisitor visitor(ctorType); - if (!walkThroughClassHierarchy(myType, &visitor)) { + auto findBasePred = [ctorType](PyTypeObject *type) { return type == ctorType; }; + if (!walkThroughBases(myType, findBasePred)) { PyErr_Format(PyExc_TypeError, "%s isn't a direct base class of %s", ctorType->tp_name, myType->tp_name); return false; } return true; } -bool hasCast(SbkObjectType *type) +bool hasCast(PyTypeObject *type) { return PepType_SOTP(type)->mi_specialcast != nullptr; } -void *cast(SbkObjectType *sourceType, SbkObject *obj, PyTypeObject *pyTargetType) +void *cast(PyTypeObject *sourceType, SbkObject *obj, PyTypeObject *pyTargetType) { auto *sotp = PepType_SOTP(sourceType); - auto *targetType = reinterpret_cast<SbkObjectType *>(pyTargetType); - return sotp->mi_specialcast(Object::cppPointer(obj, pyTargetType), targetType); + return sotp->mi_specialcast(Object::cppPointer(obj, pyTargetType), pyTargetType); } -void setCastFunction(SbkObjectType *type, SpecialCastFunction func) +void setCastFunction(PyTypeObject *type, SpecialCastFunction func) { auto *sotp = PepType_SOTP(type); sotp->mi_specialcast = func; } -void setOriginalName(SbkObjectType *type, const char *name) +void setOriginalName(PyTypeObject *type, const char *name) { auto *sotp = PepType_SOTP(type); if (sotp->original_name) @@ -996,17 +967,17 @@ void setOriginalName(SbkObjectType *type, const char *name) sotp->original_name = strdup(name); } -const char *getOriginalName(SbkObjectType *type) +const char *getOriginalName(PyTypeObject *type) { return PepType_SOTP(type)->original_name; } -void setTypeDiscoveryFunctionV2(SbkObjectType *type, TypeDiscoveryFuncV2 func) +void setTypeDiscoveryFunctionV2(PyTypeObject *type, TypeDiscoveryFuncV2 func) { PepType_SOTP(type)->type_discovery = func; } -void copyMultipleInheritance(SbkObjectType *type, SbkObjectType *other) +void copyMultipleInheritance(PyTypeObject *type, PyTypeObject *other) { auto *sotp_type = PepType_SOTP(type); auto *sotp_other = PepType_SOTP(other); @@ -1015,99 +986,100 @@ void copyMultipleInheritance(SbkObjectType *type, SbkObjectType *other) sotp_type->mi_specialcast = sotp_other->mi_specialcast; } -void setMultipleInheritanceFunction(SbkObjectType *type, MultipleInheritanceInitFunction function) +void setMultipleInheritanceFunction(PyTypeObject *type, MultipleInheritanceInitFunction function) { PepType_SOTP(type)->mi_init = function; } -MultipleInheritanceInitFunction getMultipleInheritanceFunction(SbkObjectType *type) +MultipleInheritanceInitFunction getMultipleInheritanceFunction(PyTypeObject *type) { return PepType_SOTP(type)->mi_init; } -void setDestructorFunction(SbkObjectType *type, ObjectDestructor func) +void setDestructorFunction(PyTypeObject *type, ObjectDestructor func) { PepType_SOTP(type)->cpp_dtor = func; } -SbkObjectType * +PyTypeObject * introduceWrapperType(PyObject *enclosingObject, const char *typeName, const char *originalName, PyType_Spec *typeSpec, ObjectDestructor cppObjDtor, - SbkObjectType *baseType, - PyObject *baseTypes, + PyObject *bases, unsigned wrapperFlags) { - typeSpec->slots[0].pfunc = reinterpret_cast<void *>(baseType ? baseType : SbkObject_TypeF()); + const auto basesSize = PySequence_Fast_GET_SIZE(bases); + assert(basesSize > 0); + typeSpec->slots[0].pfunc = PySequence_Fast_GET_ITEM(bases, 0); - PyObject *heaptype = SbkType_FromSpecWithBases(typeSpec, baseTypes); - Py_TYPE(heaptype) = SbkObjectType_TypeF(); - Py_INCREF(Py_TYPE(heaptype)); - auto *type = reinterpret_cast<SbkObjectType *>(heaptype); - if (baseType) { - if (baseTypes) { - for (int i = 0; i < PySequence_Fast_GET_SIZE(baseTypes); ++i) - BindingManager::instance().addClassInheritance(reinterpret_cast<SbkObjectType *>(PySequence_Fast_GET_ITEM(baseTypes, i)), type); - } else { - BindingManager::instance().addClassInheritance(baseType, type); - } - } - if (PyType_Ready(reinterpret_cast<PyTypeObject *>(type)) < 0) - return nullptr; + auto *type = SbkType_FromSpecBasesMeta(typeSpec, bases, SbkObjectType_TypeF()); auto sotp = PepType_SOTP(type); if (wrapperFlags & DeleteInMainThread) sotp->delete_in_main_thread = 1; + sotp->type_behaviour = (wrapperFlags & Value) != 0 + ? BEHAVIOUR_VALUETYPE : BEHAVIOUR_OBJECTTYPE; setOriginalName(type, originalName); setDestructorFunction(type, cppObjDtor); auto *ob_type = reinterpret_cast<PyObject *>(type); - if (wrapperFlags & InnerClass) + if (wrapperFlags & InnerClass) { + // PYSIDE-2230: Instead of tp_dict, use the enclosing type. + // This stays interface compatible. + if (PyType_Check(enclosingObject)) { + AutoDecRef tpDict(PepType_GetDict(reinterpret_cast<PyTypeObject *>(enclosingObject))); + return PyDict_SetItemString(tpDict, typeName, ob_type) == 0 ? type : nullptr; + } + assert(PyDict_Check(enclosingObject)); return PyDict_SetItemString(enclosingObject, typeName, ob_type) == 0 ? type : nullptr; + } // PyModule_AddObject steals type's reference. Py_INCREF(ob_type); if (PyModule_AddObject(enclosingObject, typeName, ob_type) != 0) { std::cerr << "Warning: " << __FUNCTION__ << " returns nullptr for " << typeName << '/' << originalName << " due to PyModule_AddObject(enclosingObject=" - << enclosingObject << ",ob_type=" << ob_type << ") failing\n"; + << enclosingObject << ", ob_type=" << ob_type << ") failing\n"; return nullptr; } return type; } -void setSubTypeInitHook(SbkObjectType *type, SubTypeInitHook func) +void setSubTypeInitHook(PyTypeObject *type, SubTypeInitHook func) { + assert(SbkObjectType_Check(type)); PepType_SOTP(type)->subtype_init = func; } -void *getTypeUserData(SbkObjectType *type) +void *getTypeUserData(PyTypeObject *type) { + assert(SbkObjectType_Check(type)); return PepType_SOTP(type)->user_data; } -void setTypeUserData(SbkObjectType *type, void *userData, DeleteUserDataFunc d_func) +void setTypeUserData(PyTypeObject *type, void *userData, DeleteUserDataFunc d_func) { + assert(SbkObjectType_Check(type)); auto *sotp = PepType_SOTP(type); sotp->user_data = userData; sotp->d_func = d_func; } // Try to find the exact type of cptr. -SbkObjectType *typeForTypeName(const char *typeName) +PyTypeObject *typeForTypeName(const char *typeName) { - SbkObjectType *result{}; + PyTypeObject *result{}; if (typeName) { if (PyTypeObject *pyType = Shiboken::Conversions::getPythonTypeObject(typeName)) - result = reinterpret_cast<SbkObjectType *>(pyType); + result = pyType; } return result; } -bool hasSpecialCastFunction(SbkObjectType *sbkType) +bool hasSpecialCastFunction(PyTypeObject *sbkType) { const auto *d = PepType_SOTP(sbkType); return d != nullptr && d->mi_specialcast != nullptr; @@ -1199,12 +1171,9 @@ void callCppDestructors(SbkObject *pyObj) return; } PyTypeObject *type = Py_TYPE(pyObj); - auto *sbkType = reinterpret_cast<SbkObjectType *>(type); - auto *sotp = PepType_SOTP(sbkType); + auto *sotp = PepType_SOTP(type); if (sotp->is_multicpp) { - Shiboken::DtorAccumulatorVisitor visitor(pyObj); - Shiboken::walkThroughClassHierarchy(type, &visitor); - callDestructor(visitor.entries()); + callDestructor(getDestructorEntries(pyObj)); } else { Shiboken::ThreadStateSaver threadSaver; threadSaver.save(); @@ -1259,7 +1228,7 @@ void getOwnership(PyObject *pyObj) void releaseOwnership(SbkObject *self) { // skip if the ownership have already moved to c++ - auto *selfType = reinterpret_cast<SbkObjectType *>(Py_TYPE(self)); + auto *selfType = Py_TYPE(self); if (!self->d->hasOwnership || Shiboken::Conversions::pythonTypeIsValueType(PepType_SOTP(selfType)->converter)) return; @@ -1353,11 +1322,10 @@ void makeValid(SbkObject *self) // If has ref to other objects make all valid again if (self->d->referredObjects) { - RefCountMap &refCountMap = *(self->d->referredObjects); - RefCountMap::iterator iter; - for (auto it = refCountMap.begin(), end = refCountMap.end(); it != end; ++it) { - if (Shiboken::Object::checkType(it->second)) - makeValid(reinterpret_cast<SbkObject *>(it->second)); + const RefCountMap &refCountMap = *(self->d->referredObjects); + for (const auto &p : refCountMap) { + if (Shiboken::Object::checkType(p.second)) + makeValid(reinterpret_cast<SbkObject *>(p.second)); } } } @@ -1365,8 +1333,7 @@ void makeValid(SbkObject *self) void *cppPointer(SbkObject *pyObj, PyTypeObject *desiredType) { PyTypeObject *pyType = Py_TYPE(pyObj); - auto *type = reinterpret_cast<SbkObjectType *>(pyType); - auto *sotp = PepType_SOTP(type); + auto *sotp = PepType_SOTP(pyType); int idx = 0; if (sotp->is_multicpp) idx = getTypeIndexOnHierarchy(pyType, desiredType); @@ -1388,14 +1355,14 @@ std::vector<void *> cppPointers(SbkObject *pyObj) bool setCppPointer(SbkObject *sbkObj, PyTypeObject *desiredType, void *cptr) { PyTypeObject *type = Py_TYPE(sbkObj); - auto *sbkType = reinterpret_cast<SbkObjectType *>(type); int idx = 0; - if (PepType_SOTP(sbkType)->is_multicpp) + if (PepType_SOTP(type)->is_multicpp) idx = getTypeIndexOnHierarchy(type, desiredType); const bool alreadyInitialized = sbkObj->d->cptr[idx] != nullptr; if (alreadyInitialized) - PyErr_SetString(PyExc_RuntimeError, "You can't initialize an object twice!"); + PyErr_Format(PyExc_RuntimeError, "You can't initialize an %s object in class %s twice!", + desiredType->tp_name, type->tp_name); else sbkObj->d->cptr[idx] = cptr; @@ -1406,6 +1373,7 @@ bool setCppPointer(SbkObject *sbkObj, PyTypeObject *desiredType, void *cptr) bool isValid(PyObject *pyObj) { if (!pyObj || pyObj == Py_None + || PyType_Check(pyObj) != 0 || Py_TYPE(Py_TYPE(pyObj)) != SbkObjectType_TypeF()) { return true; } @@ -1453,14 +1421,14 @@ bool isValid(SbkObject *pyObj, bool throwPyError) bool isValid(PyObject *pyObj, bool throwPyError) { if (!pyObj || pyObj == Py_None || - !PyType_IsSubtype(Py_TYPE(pyObj), reinterpret_cast<PyTypeObject *>(SbkObject_TypeF()))) { + !PyType_IsSubtype(Py_TYPE(pyObj), SbkObject_TypeF())) { return true; } return isValid(reinterpret_cast<SbkObject *>(pyObj), throwPyError); } SbkObject *findColocatedChild(SbkObject *wrapper, - const SbkObjectType *instanceType) + const PyTypeObject *instanceType) { // Degenerate case, wrapper is the correct wrapper. if (reinterpret_cast<const void *>(Py_TYPE(wrapper)) == reinterpret_cast<const void *>(instanceType)) @@ -1486,7 +1454,7 @@ SbkObject *findColocatedChild(SbkObject *wrapper, return nullptr; } -PyObject *newObject(SbkObjectType *instanceType, +PyObject *newObject(PyTypeObject *instanceType, void *cptr, bool hasOwnership, bool isExactType, @@ -1494,10 +1462,15 @@ PyObject *newObject(SbkObjectType *instanceType, { // Try to find the exact type of cptr. if (!isExactType) { - if (SbkObjectType *exactType = ObjectType::typeForTypeName(typeName)) + if (PyTypeObject *exactType = ObjectType::typeForTypeName(typeName)) { instanceType = exactType; - else - instanceType = BindingManager::instance().resolveType(&cptr, instanceType); + } else { + auto resolved = BindingManager::instance().findDerivedType(cptr, instanceType); + if (resolved.first != nullptr) { + instanceType = resolved.first; + cptr = resolved.second; + } + } } bool shouldCreate = true; @@ -1529,7 +1502,7 @@ PyObject *newObject(SbkObjectType *instanceType, } if (shouldCreate) { - self = reinterpret_cast<SbkObject *>(SbkObjectTpNew(reinterpret_cast<PyTypeObject *>(instanceType), nullptr, nullptr)); + self = reinterpret_cast<SbkObject *>(SbkObject_tp_new(instanceType, nullptr, nullptr)); self->d->cptr[0] = cptr; self->d->hasOwnership = hasOwnership; self->d->validCppObject = 1; @@ -1708,14 +1681,13 @@ void deallocData(SbkObject *self, bool cleanup) } delete self->d; // PYSIDE-205: always delete d. Py_XDECREF(self->ob_dict); - Py_TYPE(self)->tp_free(self); + PepExt_TypeCallFree(reinterpret_cast<PyObject *>(self)); } void setTypeUserData(SbkObject *wrapper, void *userData, DeleteUserDataFunc d_func) { auto *type = Py_TYPE(wrapper); - auto *sbkType = reinterpret_cast<SbkObjectType *>(type); - auto *sotp = PepType_SOTP(sbkType); + auto *sotp = PepType_SOTP(type); if (sotp->user_data) sotp->d_func(sotp->user_data); @@ -1726,8 +1698,7 @@ void setTypeUserData(SbkObject *wrapper, void *userData, DeleteUserDataFunc d_fu void *getTypeUserData(SbkObject *wrapper) { auto *type = Py_TYPE(wrapper); - auto *sbkType = reinterpret_cast<SbkObjectType *>(type); - return PepType_SOTP(sbkType)->user_data; + return PepType_SOTP(type)->user_data; } static inline bool isNone(const PyObject *o) @@ -1793,22 +1764,66 @@ void clearReferences(SbkObject *self) self->d->referredObjects->clear(); } +// Helpers for debug / info formatting + +static std::vector<PyTypeObject *> getBases(SbkObject *self) +{ + return ObjectType::isUserType(Py_TYPE(self)) + ? getCppBaseClasses(Py_TYPE(self)) + : std::vector<PyTypeObject *>(1, Py_TYPE(self)); +} + +static bool isValueType(SbkObject *self) +{ + return PepType_SOTP(Py_TYPE(self))->type_behaviour == BEHAVIOUR_VALUETYPE; +} + +void _debugFormat(std::ostream &s, SbkObject *self) +{ + assert(self); + auto *d = self->d; + if (!d) { + s << "[Invalid]"; + return; + } + if (d->cptr) { + const std::vector<PyTypeObject *> bases = getBases(self); + for (size_t i = 0, size = bases.size(); i < size; ++i) + s << ", C++: " << bases[i]->tp_name << '/' << self->d->cptr[i]; + } else { + s << " [Deleted]"; + } + if (d->hasOwnership) + s << " [hasOwnership]"; + if (d->containsCppWrapper) + s << " [containsCppWrapper]"; + if (d->validCppObject) + s << " [validCppObject]"; + if (d->cppObjectCreated) + s << " [wasCreatedByPython]"; + s << (isValueType(self) ? " [value]" : " [object]"); + + if (d->parentInfo) { + if (auto *parent = d->parentInfo->parent) + s << ", parent=" << reinterpret_cast<PyObject *>(parent)->ob_type->tp_name + << '/' << parent; + if (!d->parentInfo->children.empty()) + s << ", " << d->parentInfo->children.size() << " child(ren)"; + } + if (d->referredObjects && !d->referredObjects->empty()) + s << ", " << d->referredObjects->size() << " referred object(s)"; +} + std::string info(SbkObject *self) { std::ostringstream s; if (self->d && self->d->cptr) { - std::vector<SbkObjectType *> bases; - if (ObjectType::isUserType(Py_TYPE(self))) - bases = getCppBaseClasses(Py_TYPE(self)); - else - bases.push_back(reinterpret_cast<SbkObjectType *>(Py_TYPE(self))); + const std::vector<PyTypeObject *> bases = getBases(self); s << "C++ address....... "; - for (size_t i = 0, size = bases.size(); i < size; ++i) { - auto base = reinterpret_cast<PyTypeObject *>(bases[i]); - s << base->tp_name << '/' << self->d->cptr[i] << ' '; - } + for (size_t i = 0, size = bases.size(); i < size; ++i) + s << bases[i]->tp_name << '/' << self->d->cptr[i] << ' '; s << "\n"; } else { @@ -1818,8 +1833,9 @@ std::string info(SbkObject *self) s << "hasOwnership...... " << bool(self->d->hasOwnership) << "\n" "containsCppWrapper " << self->d->containsCppWrapper << "\n" "validCppObject.... " << self->d->validCppObject << "\n" - "wasCreatedByPython " << self->d->cppObjectCreated << "\n"; - + "wasCreatedByPython " << self->d->cppObjectCreated << "\n" + "value...... " << isValueType(self) << "\n" + "reference count... " << reinterpret_cast<PyObject *>(self)->ob_refcnt << '\n'; if (self->d->parentInfo && self->d->parentInfo->parent) { s << "parent............ "; @@ -1837,17 +1853,17 @@ std::string info(SbkObject *self) } if (self->d->referredObjects && !self->d->referredObjects->empty()) { - Shiboken::RefCountMap &map = *self->d->referredObjects; + const Shiboken::RefCountMap &map = *self->d->referredObjects; s << "referred objects.. "; std::string lastKey; - for (auto it = map.begin(), end = map.end(); it != end; ++it) { - if (it->first != lastKey) { + for (const auto &p : map) { + if (p.first != lastKey) { if (!lastKey.empty()) s << " "; - s << '"' << it->first << "\" => "; - lastKey = it->first; + s << '"' << p.first << "\" => "; + lastKey = p.first; } - Shiboken::AutoDecRef obj(PyObject_Str(it->second)); + Shiboken::AutoDecRef obj(PyObject_Str(p.second)); s << String::toCString(obj) << ' '; } s << '\n'; diff --git a/sources/shiboken6/libshiboken/basewrapper.h b/sources/shiboken6/libshiboken/basewrapper.h index a97242606..4835c4810 100644 --- a/sources/shiboken6/libshiboken/basewrapper.h +++ b/sources/shiboken6/libshiboken/basewrapper.h @@ -1,47 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef BASEWRAPPER_H #define BASEWRAPPER_H #include "sbkpython.h" #include "shibokenmacros.h" +#include "sbktypefactory.h" #include <vector> #include <string> @@ -72,59 +37,60 @@ LIBSHIBOKEN_API void SbkDeallocWrapper(PyObject *pyObj); LIBSHIBOKEN_API void SbkDeallocQAppWrapper(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 *); +using MultipleInheritanceInitFunction = int *(*)(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 *); +using SpecialCastFunction = void *(*)(void *, PyTypeObject *); +using TypeDiscoveryFunc = PyTypeObject *(*)(void *, PyTypeObject *); +using TypeDiscoveryFuncV2 = void *(*)(void *, PyTypeObject *); // Used in userdata dealloc function -typedef void (*DeleteUserDataFunc)(void *); +using DeleteUserDataFunc = void (*)(void *); -typedef void (*ObjectDestructor)(void *); +using ObjectDestructor = void (*)(void *); -typedef void (*SubTypeInitHook)(SbkObjectType *, PyObject *, PyObject *); +using SubTypeInitHook = void (*)(PyTypeObject *, PyObject *, PyObject *); /// PYSIDE-1019: Set the function to select the current feature. /// Return value is the previous content. -typedef PyObject *(*SelectableFeatureHook)(PyTypeObject *); +using SelectableFeatureHook = void (*)(PyTypeObject *); +using SelectableFeatureCallback = void (*)(bool); LIBSHIBOKEN_API SelectableFeatureHook initSelectableFeature(SelectableFeatureHook func); +LIBSHIBOKEN_API void setSelectableFeatureCallback(SelectableFeatureCallback func); -// PYSIDE-1019: Get access to PySide reserved bits. -LIBSHIBOKEN_API int SbkObjectType_GetReserved(PyTypeObject *type); -LIBSHIBOKEN_API void SbkObjectType_SetReserved(PyTypeObject *type, int value); +/// PYSIDE-1626: Enforcing a context switch without further action. +LIBSHIBOKEN_API void SbkObjectType_UpdateFeature(PyTypeObject *type); -// PYSIDE-1019: Get access to PySide property strings. +/// PYSIDE-1019: Get access to PySide property strings. LIBSHIBOKEN_API const char **SbkObjectType_GetPropertyStrings(PyTypeObject *type); LIBSHIBOKEN_API void SbkObjectType_SetPropertyStrings(PyTypeObject *type, const char **strings); +/// PYSIDE-1735: Store the enumFlagInfo. +LIBSHIBOKEN_API void SbkObjectType_SetEnumFlagInfo(PyTypeObject *type, const char **strings); + /// PYSIDE-1470: Set the function to kill a Q*Application. -typedef void(*DestroyQAppHook)(); +using DestroyQAppHook = void(*)(); LIBSHIBOKEN_API void setDestroyQApplication(DestroyQAppHook func); +/// PYSIDE-535: Use the C API in PyPy instead of `op->ob_dict`, directly (borrowed ref) +LIBSHIBOKEN_API PyObject *SbkObject_GetDict_NoRef(PyObject *op); + extern LIBSHIBOKEN_API PyTypeObject *SbkObjectType_TypeF(void); -extern LIBSHIBOKEN_API SbkObjectType *SbkObject_TypeF(void); +extern LIBSHIBOKEN_API PyTypeObject *SbkObject_TypeF(void); struct SbkObjectTypePrivate; /// PyTypeObject extended with C++ multiple inheritance information. -struct LIBSHIBOKEN_API SbkObjectType -{ - PyTypeObject type; -}; -LIBSHIBOKEN_API PyObject *SbkObjectTpNew(PyTypeObject *subtype, PyObject *, PyObject *); +LIBSHIBOKEN_API PyObject *SbkObject_tp_new(PyTypeObject *subtype, PyObject *, PyObject *); /// The special case of a switchable singleton Q*Application. -LIBSHIBOKEN_API PyObject *SbkQAppTpNew(PyTypeObject *subtype, PyObject *, PyObject *); +LIBSHIBOKEN_API PyObject *SbkQApp_tp_new(PyTypeObject *subtype, PyObject *, PyObject *); /// Create a new Q*Application wrapper and monitor it. LIBSHIBOKEN_API PyObject *MakeQAppWrapper(PyTypeObject *type); @@ -133,20 +99,22 @@ LIBSHIBOKEN_API PyObject *MakeQAppWrapper(PyTypeObject *type); * PYSIDE-832: Use object_dealloc instead of nullptr. * * When moving to heaptypes, we were struck by a special default behavior of - * PyType_FromSpecWithBases that inserts subtype_dealloc when tp_dealloc is + * PyType_FromSpec that inserts subtype_dealloc when tp_dealloc is * nullptr. But the default before conversion to heaptypes was to assign * object_dealloc. This seems to be a bug in the Limited API. */ /// PYSIDE-939: Replaced by Sbk_object_dealloc. LIBSHIBOKEN_API PyObject *SbkDummyNew(PyTypeObject *type, PyObject *, PyObject *); -/// PYSIDE-1286: Generate correct __module__ and __qualname__ -LIBSHIBOKEN_API PyObject *SbkType_FromSpec(PyType_Spec *); -LIBSHIBOKEN_API PyObject *SbkType_FromSpecWithBases(PyType_Spec *, PyObject *); - /// PYSIDE-74: Fallback used in all types now. LIBSHIBOKEN_API PyObject *FallbackRichCompare(PyObject *self, PyObject *other, int op); +/// PYSIDE-1970: Be easily able to see what is happening in the running code. +LIBSHIBOKEN_API void disassembleFrame(const char *marker); + +/// PYSIDE-2230: Check if an object is an SbkObject. +LIBSHIBOKEN_API bool SbkObjectType_Check(PyTypeObject *type); + } // extern "C" namespace Shiboken @@ -158,7 +126,7 @@ namespace Shiboken LIBSHIBOKEN_API void init(); /// PYSIDE-1415: Publish Shiboken objects. -LIBSHIBOKEN_API void initSignature(PyObject *module); +LIBSHIBOKEN_API void initShibokenSupport(PyObject *module); /// Delete the class T allocated on \p cptr. template<typename T> @@ -167,11 +135,30 @@ void callCppDestructor(void *cptr) delete reinterpret_cast<T *>(cptr); } -// setErrorAboutWrongArguments now gets overload information from the signature module. -// The extra info argument can contain additional data about the error. +/// setErrorAboutWrongArguments now gets overload information from the signature module. +/// The extra info argument can contain additional data about the error. LIBSHIBOKEN_API void setErrorAboutWrongArguments(PyObject *args, const char *funcName, PyObject *info); +/// Return values for the different retun variants. +/// This is used instead of goto. +LIBSHIBOKEN_API PyObject *returnWrongArguments(PyObject *args, const char *funcName, + PyObject *info); + +LIBSHIBOKEN_API int returnWrongArguments_Zero(PyObject *args, const char *funcName, + PyObject *info); + +LIBSHIBOKEN_API int returnWrongArguments_MinusOne(PyObject *args, const char *funcName, + PyObject *info); + +/// A simple special version for the end of rich comparison. +LIBSHIBOKEN_API PyObject *returnFromRichCompare(PyObject *result); + +// Return error information object if the argument count is wrong +LIBSHIBOKEN_API PyObject *checkInvalidArgumentCount(Py_ssize_t numArgs, + Py_ssize_t minArgs, + Py_ssize_t maxArgs); + namespace ObjectType { /** @@ -197,30 +184,31 @@ LIBSHIBOKEN_API bool canCallConstructor(PyTypeObject *myType, PyTypeObject *ctor * \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); +LIBSHIBOKEN_API bool hasCast(PyTypeObject *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); +LIBSHIBOKEN_API void *cast(PyTypeObject *sourceType, SbkObject *obj, PyTypeObject *targetType); /// Set the C++ cast function for \p type. -LIBSHIBOKEN_API void setCastFunction(SbkObjectType *type, SpecialCastFunction func); +LIBSHIBOKEN_API void setCastFunction(PyTypeObject *type, SpecialCastFunction func); -LIBSHIBOKEN_API void setOriginalName(SbkObjectType *self, const char *name); -LIBSHIBOKEN_API const char *getOriginalName(SbkObjectType *self); +LIBSHIBOKEN_API void setOriginalName(PyTypeObject *self, const char *name); +LIBSHIBOKEN_API const char *getOriginalName(PyTypeObject *self); -LIBSHIBOKEN_API void setTypeDiscoveryFunctionV2(SbkObjectType *self, TypeDiscoveryFuncV2 func); -LIBSHIBOKEN_API void copyMultipleInheritance(SbkObjectType *self, SbkObjectType *other); -LIBSHIBOKEN_API void setMultipleInheritanceFunction(SbkObjectType *self, MultipleInheritanceInitFunction func); -LIBSHIBOKEN_API MultipleInheritanceInitFunction getMultipleInheritanceFunction(SbkObjectType *self); +LIBSHIBOKEN_API void setTypeDiscoveryFunctionV2(PyTypeObject *self, TypeDiscoveryFuncV2 func); +LIBSHIBOKEN_API void copyMultipleInheritance(PyTypeObject *self, PyTypeObject *other); +LIBSHIBOKEN_API void setMultipleInheritanceFunction(PyTypeObject *self, MultipleInheritanceInitFunction func); +LIBSHIBOKEN_API MultipleInheritanceInitFunction getMultipleInheritanceFunction(PyTypeObject *type); -LIBSHIBOKEN_API void setDestructorFunction(SbkObjectType *self, ObjectDestructor func); +LIBSHIBOKEN_API void setDestructorFunction(PyTypeObject *self, ObjectDestructor func); enum WrapperFlags { InnerClass = 0x1, - DeleteInMainThread = 0x2 + DeleteInMainThread = 0x2, + Value = 0x4 }; /** @@ -240,14 +228,13 @@ enum WrapperFlags * wrapper type. * \returns true if the initialization went fine, false otherwise. */ -LIBSHIBOKEN_API SbkObjectType *introduceWrapperType(PyObject *enclosingObject, - const char *typeName, - const char *originalName, - PyType_Spec *typeSpec, - ObjectDestructor cppObjDtor, - SbkObjectType *baseType, - PyObject *baseTypes, - unsigned wrapperFlags = 0); +LIBSHIBOKEN_API PyTypeObject *introduceWrapperType(PyObject *enclosingObject, + const char *typeName, + const char *originalName, + PyType_Spec *typeSpec, + ObjectDestructor cppObjDtor, + PyObject *bases, + unsigned wrapperFlags = 0); /** * Set the subtype init hook for a type. @@ -256,28 +243,28 @@ LIBSHIBOKEN_API SbkObjectType *introduceWrapperType(PyObject *enclosingObject, * 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); +LIBSHIBOKEN_API void setSubTypeInitHook(PyTypeObject *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); +LIBSHIBOKEN_API void *getTypeUserData(PyTypeObject *self); +LIBSHIBOKEN_API void setTypeUserData(PyTypeObject *self, void *userData, DeleteUserDataFunc d_func); /** - * Return an instance of SbkObjectType for a C++ type name as determined by + * Return an instance of PyTypeObject for a C++ type name as determined by * typeinfo().name(). * \param typeName Type name * \since 5.12 */ -LIBSHIBOKEN_API SbkObjectType *typeForTypeName(const char *typeName); +LIBSHIBOKEN_API PyTypeObject *typeForTypeName(const char *typeName); /** - * Returns whether SbkObjectType has a special cast function (multiple inheritance) + * Returns whether PyTypeObject has a special cast function (multiple inheritance) * \param sbkType Sbk type * \since 5.12 */ -LIBSHIBOKEN_API bool hasSpecialCastFunction(SbkObjectType *sbkType); +LIBSHIBOKEN_API bool hasSpecialCastFunction(PyTypeObject *sbkType); } namespace Object { @@ -307,7 +294,7 @@ 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); + const PyTypeObject *instanceType); /** * Bind a C++ object to Python. @@ -318,7 +305,7 @@ LIBSHIBOKEN_API SbkObject *findColocatedChild(SbkObject *wrapper, * 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, +LIBSHIBOKEN_API PyObject *newObject(PyTypeObject *instanceType, void *cptr, bool hasOwnership = true, bool isExactType = false, diff --git a/sources/shiboken6/libshiboken/basewrapper_p.h b/sources/shiboken6/libshiboken/basewrapper_p.h index 478a7d52e..fb9140793 100644 --- a/sources/shiboken6/libshiboken/basewrapper_p.h +++ b/sources/shiboken6/libshiboken/basewrapper_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef BASEWRAPPER_P_H #define BASEWRAPPER_P_H @@ -47,9 +11,9 @@ #include <set> #include <string> #include <vector> +#include <iosfwd> struct SbkObject; -struct SbkObjectType; struct SbkConverter; namespace Shiboken @@ -66,14 +30,12 @@ using ChildrenList = std::set<SbkObject *>; /// Structure used to store information about object parent and children. struct ParentInfo { - /// Default ctor. - ParentInfo() : parent(nullptr), hasWrapperRef(false) {} /// Pointer to parent object. - SbkObject *parent; + SbkObject *parent = nullptr; /// List of object children. ChildrenList children; /// has internal ref - bool hasWrapperRef; + bool hasWrapperRef = false; }; } // namespace Shiboken @@ -87,6 +49,12 @@ extern "C" */ struct SbkObjectPrivate { + SbkObjectPrivate() noexcept = default; + SbkObjectPrivate(const SbkObjectPrivate &) = delete; + SbkObjectPrivate(SbkObjectPrivate &&o) = delete; + SbkObjectPrivate &operator=(const SbkObjectPrivate &) = delete; + SbkObjectPrivate &operator=(SbkObjectPrivate &&o) = delete; + /// Pointer to the C++ class. void ** cptr; /// True when Python is responsible for freeing the used memory. @@ -133,22 +101,25 @@ struct SbkObjectTypePrivate TypeDiscoveryFuncV2 type_discovery; /// Pointer to a function responsible for deletion of the C++ instance calling the proper destructor. ObjectDestructor cpp_dtor; - /// PYSIDE-1019: Caching the current select Id - unsigned int pyside_reserved_bits : 8; // MSVC has bug with the sign bit! - /// True if this type holds two or more C++ instances, e.g.: a Python class which inherits from two C++ classes. - unsigned int is_multicpp : 1; - /// True if this type was defined by the user. - unsigned int is_user_type : 1; - /// Tells is the type is a value type or an object-type, see BEHAVIOUR_ *constants. - unsigned int type_behaviour : 2; - unsigned int delete_in_main_thread : 1; /// C++ name char *original_name; /// Type user data void *user_data; DeleteUserDataFunc d_func; - void (*subtype_init)(SbkObjectType *, PyObject *, PyObject *); + void (*subtype_init)(PyTypeObject *, PyObject *, PyObject *); const char **propertyStrings; + const char **enumFlagInfo; + PyObject *enumFlagsDict; + PyObject *enumTypeDict; + + /// True if this type holds two or more C++ instances, e.g.: a Python class which inherits from two C++ classes. + unsigned int is_multicpp : 1; + /// True if this type was defined by the user (a class written in Python inheriting + /// a class provided by a Shiboken binding). + unsigned int is_user_type : 1; + /// Tells is the type is a value type or an object-type, see BEHAVIOUR_ *constants. + unsigned int type_behaviour : 2; + unsigned int delete_in_main_thread : 1; }; @@ -172,107 +143,7 @@ struct DestructorEntry **/ std::vector<SbkObject *> splitPyObject(PyObject *pyObj); -/** -* Visitor class used by walkOnClassHierarchy function. -*/ -class HierarchyVisitor -{ -public: - HierarchyVisitor(const HierarchyVisitor &) = delete; - HierarchyVisitor(HierarchyVisitor &&) = delete; - HierarchyVisitor &operator=(const HierarchyVisitor &) = delete; - HierarchyVisitor &operator=(HierarchyVisitor &&) = delete; - - HierarchyVisitor(); - virtual ~HierarchyVisitor(); - - virtual bool visit(SbkObjectType *node) = 0; // return true to terminate -}; - -class BaseCountVisitor : public HierarchyVisitor -{ -public: - bool visit(SbkObjectType *) override; - - int count() const { return m_count; } - -private: - int m_count = 0; -}; - -class BaseAccumulatorVisitor : public HierarchyVisitor -{ -public: - using Result = std::vector<SbkObjectType *>; - - bool visit(SbkObjectType *node) override; - - Result bases() const { return m_bases; } - -private: - Result m_bases; -}; - -class GetIndexVisitor : public HierarchyVisitor -{ -public: - explicit GetIndexVisitor(PyTypeObject *desiredType) : m_desiredType(desiredType) {} - - bool visit(SbkObjectType *node) override; - - int index() const { return m_index; } - -private: - int m_index = -1; - PyTypeObject *m_desiredType; -}; - -/// Collect destructors and C++ instances of each C++ object held by a Python -/// object -class DtorAccumulatorVisitor : public HierarchyVisitor -{ -public: - explicit DtorAccumulatorVisitor(SbkObject *pyObj) : m_pyObject(pyObj) {} - - bool visit(SbkObjectType *node) override; - - using DestructorEntries = std::vector<DestructorEntry>; - - const DestructorEntries &entries() const { return m_entries; } - -private: - DestructorEntries m_entries; - SbkObject *m_pyObject; -}; - -/// \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. -*/ -bool 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::vector<SbkObjectType *> getCppBaseClasses(PyTypeObject *baseType) -{ - BaseAccumulatorVisitor visitor; - walkThroughClassHierarchy(baseType, &visitor); - return visitor.bases(); -} +int getNumberOfCppBaseClasses(PyTypeObject *baseType); namespace Object { @@ -287,6 +158,8 @@ void clearReferences(SbkObject *self); **/ void deallocData(SbkObject *self, bool doCleanup); + +void _debugFormat(std::ostream &str, SbkObject *self); } // namespace Object } // namespace Shiboken diff --git a/sources/shiboken6/libshiboken/bindingmanager.cpp b/sources/shiboken6/libshiboken/bindingmanager.cpp index f3b2ef1c8..83c927ae5 100644 --- a/sources/shiboken6/libshiboken/bindingmanager.cpp +++ b/sources/shiboken6/libshiboken/bindingmanager.cpp @@ -1,156 +1,188 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "autodecref.h" #include "basewrapper.h" #include "basewrapper_p.h" #include "bindingmanager.h" #include "gilstate.h" +#include "helper.h" +#include "sbkmodule.h" #include "sbkstring.h" #include "sbkstaticstrings.h" +#include "sbkfeature_base.h" #include "debugfreehook.h" #include <cstddef> +#include <cstring> #include <fstream> +#include <iostream> +#include <mutex> +#include <string_view> #include <unordered_map> +#include <unordered_set> + +// GraphNode for the dependency graph. It keeps a pointer to +// the TypeInitStruct to be able to lazily create the type and hashes +// by the full type name. +struct GraphNode +{ + explicit GraphNode(Shiboken::Module::TypeInitStruct *i) : name(i->fullName), initStruct(i) {} + explicit GraphNode(const char *n) : name(n), initStruct(nullptr) {} // Only for searching + + std::string_view name; + Shiboken::Module::TypeInitStruct *initStruct; + + friend bool operator==(const GraphNode &n1, const GraphNode &n2) { return n1.name == n2.name; } + friend bool operator!=(const GraphNode &n1, const GraphNode &n2) { return n1.name != n2.name; } +}; + +template <> +struct std::hash<GraphNode> { + size_t operator()(const GraphNode &n) const noexcept + { + return std::hash<std::string_view>{}(n.name); + } +}; namespace Shiboken { using WrapperMap = std::unordered_map<const void *, SbkObject *>; -class Graph +template <class NodeType> +class BaseGraph { public: - using NodeList = std::vector<SbkObjectType *>; - using Edges = std::unordered_map<SbkObjectType *, NodeList>; + using NodeList = std::vector<NodeType>; + using NodeSet = std::unordered_set<NodeType>; + + using Edges = std::unordered_map<NodeType, NodeList>; Edges m_edges; - Graph() = default; + BaseGraph() = default; - void addEdge(SbkObjectType *from, SbkObjectType *to) + void addEdge(NodeType from, NodeType to) { m_edges[from].push_back(to); } -#ifndef NDEBUG - void dumpDotGraph() + NodeSet nodeSet() const { - std::ofstream file("/tmp/shiboken_graph.dot"); - - file << "digraph D {\n"; - - for (auto i = m_edges.begin(), end = m_edges.end(); i != end; ++i) { - auto node1 = reinterpret_cast<const PyTypeObject *>(i->first); - const NodeList &nodeList = i->second; - for (const SbkObjectType *o : nodeList) { - auto node2 = reinterpret_cast<const PyTypeObject *>(o); - file << '"' << node2->tp_name << "\" -> \"" - << node1->tp_name << "\"\n"; - } + NodeSet result; + for (const auto &p : m_edges) { + result.insert(p.first); + for (const auto node2 : p.second) + result.insert(node2); } - file << "}\n"; + return result; } -#endif +}; - SbkObjectType *identifyType(void **cptr, SbkObjectType *type, SbkObjectType *baseType) const +class Graph : public BaseGraph<GraphNode> +{ +public: + using TypeCptrPair = BindingManager::TypeCptrPair; + + TypeCptrPair identifyType(void *cptr, PyTypeObject *type, PyTypeObject *baseType) const { - auto edgesIt = m_edges.find(type); - if (edgesIt != m_edges.end()) { - const NodeList &adjNodes = m_edges.find(type)->second; - for (SbkObjectType *node : adjNodes) { - SbkObjectType *newType = identifyType(cptr, node, baseType); - if (newType) - return newType; - } - } - void *typeFound = nullptr; - auto *sotp = PepType_SOTP(type); - if (sotp->type_discovery) - typeFound = sotp->type_discovery(*cptr, baseType); - if (typeFound) { - // This "typeFound != type" is needed for backwards compatibility with old modules using a newer version of - // libshiboken because old versions of type_discovery function used to return a SbkObjectType *instead of - // a possible variation of the C++ instance pointer (*cptr). - if (typeFound != type) - *cptr = typeFound; - return type; - } - return nullptr; + return identifyType(cptr, GraphNode(type->tp_name), type, baseType); } -}; + bool dumpTypeGraph(const char *fileName) const; -#ifndef NDEBUG -static void showWrapperMap(const WrapperMap &wrapperMap) +private: + TypeCptrPair identifyType(void *cptr, const GraphNode &typeNode, PyTypeObject *type, + PyTypeObject *baseType) const; +}; + +Graph::TypeCptrPair Graph::identifyType(void *cptr, + const GraphNode &typeNode, PyTypeObject *type, + PyTypeObject *baseType) const { - if (Py_VerboseFlag > 0) { - fprintf(stderr, "-------------------------------\n"); - fprintf(stderr, "WrapperMap: %p (size: %d)\n", &wrapperMap, (int) wrapperMap.size()); - for (auto it = wrapperMap.begin(), end = wrapperMap.end(); it != end; ++it) { - const SbkObject *sbkObj = it->second; - fprintf(stderr, "key: %p, value: %p (%s, refcnt: %d)\n", it->first, - static_cast<const void *>(sbkObj), - (Py_TYPE(sbkObj))->tp_name, - int(reinterpret_cast<const PyObject *>(sbkObj)->ob_refcnt)); + assert(typeNode.initStruct != nullptr || type != nullptr); + auto edgesIt = m_edges.find(typeNode); + if (edgesIt != m_edges.end()) { + const NodeList &adjNodes = edgesIt->second; + for (const auto &node : adjNodes) { + auto newType = identifyType(cptr, node, nullptr, baseType); + if (newType.first != nullptr) + return newType; } - fprintf(stderr, "-------------------------------\n"); } + + if (type == nullptr) { + if (typeNode.initStruct->type == nullptr) // Layzily create type + type = Shiboken::Module::get(*typeNode.initStruct); + else + type = typeNode.initStruct->type; + } + + auto *sotp = PepType_SOTP(type); + if (sotp->type_discovery != nullptr) { + if (void *derivedCPtr = sotp->type_discovery(cptr, baseType)) + return {type, derivedCPtr}; + } + return {nullptr, nullptr}; +} + +static void formatDotNode(std::string_view name, std::ostream &file) +{ + auto lastDot = name.rfind('.'); + file << " \"" << name << "\" [ label="; + if (lastDot != std::string::npos) { + file << '"' << name.substr(lastDot + 1) << "\" tooltip=\"" + << name.substr(0, lastDot) << '"'; + } else { + file << '"' << name << '"'; + } + file << " ]\n"; +} + +bool Graph::dumpTypeGraph(const char *fileName) const +{ + std::ofstream file(fileName); + if (!file.good()) + return false; + + file << "digraph D {\n"; + + // Define nodes with short names + for (const auto &node : nodeSet()) + formatDotNode(node.name, file); + + // Write edges + for (const auto &p : m_edges) { + const auto &node1 = p.first; + const NodeList &nodeList = p.second; + for (const auto &node2 : nodeList) + file << " \"" << node2.name << "\" -> \"" << node1.name << "\"\n"; + } + file << "}\n"; + return true; } -#endif struct BindingManager::BindingManagerPrivate { using DestructorEntries = std::vector<DestructorEntry>; WrapperMap wrapperMapper; + // Guard wrapperMapper mainly for QML which calls into the generated + // QObject::metaObject() and elsewhere from threads without GIL, causing + // crashes for example in retrieveWrapper(). std::shared_mutex was rejected due to: + // https://stackoverflow.com/questions/50972345/when-is-stdshared-timed-mutex-slower-than-stdmutex-and-when-not-to-use-it + std::recursive_mutex wrapperMapLock; Graph classHierarchy; DestructorEntries deleteInMainThread; - bool destroying; - BindingManagerPrivate() : destroying(false) {} - bool releaseWrapper(void *cptr, SbkObject *wrapper); - void assignWrapper(SbkObject *wrapper, const void *cptr); + bool releaseWrapper(void *cptr, SbkObject *wrapper, const int *bases = nullptr); + bool releaseWrapperHelper(void *cptr, SbkObject *wrapper); + void assignWrapper(SbkObject *wrapper, const void *cptr, const int *bases = nullptr); + void assignWrapperHelper(SbkObject *wrapper, const void *cptr); }; -bool BindingManager::BindingManagerPrivate::releaseWrapper(void *cptr, SbkObject *wrapper) +inline bool BindingManager::BindingManagerPrivate::releaseWrapperHelper(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. @@ -163,14 +195,41 @@ bool BindingManager::BindingManagerPrivate::releaseWrapper(void *cptr, SbkObject return false; } -void BindingManager::BindingManagerPrivate::assignWrapper(SbkObject *wrapper, const void *cptr) +bool BindingManager::BindingManagerPrivate::releaseWrapper(void *cptr, SbkObject *wrapper, + const int *bases) { assert(cptr); + std::lock_guard<std::recursive_mutex> guard(wrapperMapLock); + const bool result = releaseWrapperHelper(cptr, wrapper); + if (bases != nullptr) { + auto *base = static_cast<uint8_t *>(cptr); + for (const auto *offset = bases; *offset != -1; ++offset) + releaseWrapperHelper(base + *offset, wrapper); + } + return result; +} + +inline void BindingManager::BindingManagerPrivate::assignWrapperHelper(SbkObject *wrapper, + const void *cptr) +{ auto iter = wrapperMapper.find(cptr); if (iter == wrapperMapper.end()) wrapperMapper.insert(std::make_pair(cptr, wrapper)); } +void BindingManager::BindingManagerPrivate::assignWrapper(SbkObject *wrapper, const void *cptr, + const int *bases) +{ + assert(cptr); + std::lock_guard<std::recursive_mutex> guard(wrapperMapLock); + assignWrapperHelper(wrapper, cptr); + if (bases != nullptr) { + const auto *base = static_cast<const uint8_t *>(cptr); + for (const auto *offset = bases; *offset != -1; ++offset) + assignWrapperHelper(wrapper, base + *offset); + } +} + BindingManager::BindingManager() { m_d = new BindingManager::BindingManagerPrivate; @@ -186,12 +245,14 @@ BindingManager::~BindingManager() debugRemoveFreeHook(); #endif #ifndef NDEBUG - showWrapperMap(m_d->wrapperMapper); + if (Shiboken::pyVerbose() > 0) + dumpWrapperMap(); #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 + std::lock_guard<std::recursive_mutex> guard(m_d->wrapperMapLock); while (!m_d->wrapperMapper.empty()) { Object::destroy(m_d->wrapperMapper.begin()->second, const_cast<void *>(m_d->wrapperMapper.begin()->first)); } @@ -207,12 +268,13 @@ BindingManager &BindingManager::instance() { bool BindingManager::hasWrapper(const void *cptr) { + std::lock_guard<std::recursive_mutex> guard(m_d->wrapperMapLock); return m_d->wrapperMapper.find(cptr) != m_d->wrapperMapper.end(); } void BindingManager::registerWrapper(SbkObject *pyObj, void *cptr) { - auto *instanceType = reinterpret_cast<SbkObjectType *>(Py_TYPE(pyObj)); + auto *instanceType = Py_TYPE(pyObj); auto *d = PepType_SOTP(instanceType); if (!d) @@ -220,35 +282,20 @@ void BindingManager::registerWrapper(SbkObject *pyObj, void *cptr) 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<void *>(reinterpret_cast<uintptr_t>(cptr) + *offset)); - offset++; - } - } + m_d->assignWrapper(pyObj, cptr, d->mi_offsets); } void BindingManager::releaseWrapper(SbkObject *sbkObj) { - auto *sbkType = reinterpret_cast<SbkObjectType *>(Py_TYPE(sbkObj)); + auto *sbkType = Py_TYPE(sbkObj); auto *d = PepType_SOTP(sbkType); int numBases = ((d && d->is_multicpp) ? getNumberOfCppBaseClasses(Py_TYPE(sbkObj)) : 1); void ** cptrs = reinterpret_cast<SbkObject *>(sbkObj)->d->cptr; + const int *mi_offsets = d != nullptr ? d->mi_offsets : nullptr; for (int i = 0; i < numBases; ++i) { - auto *cptr = reinterpret_cast<unsigned char *>(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<void *>(reinterpret_cast<uintptr_t>(cptr) + *offset), sbkObj); - offset++; - } - } + if (cptrs[i] != nullptr) + m_d->releaseWrapper(cptrs[i], sbkObj, mi_offsets); } sbkObj->d->validCppObject = false; } @@ -267,20 +314,13 @@ void BindingManager::addToDeletionInMainThread(const DestructorEntry &e) SbkObject *BindingManager::retrieveWrapper(const void *cptr) { + std::lock_guard<std::recursive_mutex> guard(m_d->wrapperMapLock); auto iter = m_d->wrapperMapper.find(cptr); if (iter == m_d->wrapperMapper.end()) return nullptr; return iter->second; } -static inline int currentSelectId(PyTypeObject *type) -{ - int sel = SbkObjectType_GetReserved(type); - // This could theoretically be -1 if used too early. - assert(sel >= 0); - return sel; -} - PyObject *BindingManager::getOverride(const void *cptr, PyObject *nameCache[], const char *methodName) @@ -288,16 +328,14 @@ PyObject *BindingManager::getOverride(const void *cptr, 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<const PyObject *>(wrapper)->ob_refcnt == 0) + if (!wrapper || Py_REFCNT(reinterpret_cast<const PyObject *>(wrapper)) == 0) return nullptr; + // PYSIDE-1626: Touch the type to initiate switching early. + SbkObjectType_UpdateFeature(Py_TYPE(wrapper)); + int flag = currentSelectId(Py_TYPE(wrapper)); int propFlag = isdigit(methodName[0]) ? methodName[0] - '0' : 0; - if ((flag & 0x02) != 0 && (propFlag & 3) != 0) { - // PYSIDE-1019: Handle overriding with properties. - // They cannot be overridden (make that sure by the metaclass). - return nullptr; - } bool is_snake = flag & 0x01; PyObject *pyMethodName = nameCache[is_snake]; // borrowed if (pyMethodName == nullptr) { @@ -307,12 +345,13 @@ PyObject *BindingManager::getOverride(const void *cptr, nameCache[is_snake] = pyMethodName; } - if (wrapper->ob_dict) { - PyObject *method = PyDict_GetItem(wrapper->ob_dict, pyMethodName); - if (method) { - Py_INCREF(method); - return method; - } + auto *obWrapper = reinterpret_cast<PyObject *>(wrapper); + auto *wrapper_dict = SbkObject_GetDict_NoRef(obWrapper); + if (PyObject *method = PyDict_GetItem(wrapper_dict, pyMethodName)) { + // Note: This special case was implemented for duck-punching, which happens + // in the instance dict. It does not work with properties. + Py_INCREF(method); + return method; } PyObject *method = PyObject_GetAttr(reinterpret_cast<PyObject *>(wrapper), pyMethodName); @@ -322,6 +361,7 @@ PyObject *BindingManager::getOverride(const void *cptr, // PYSIDE-1523: PyMethod_Check is not accepting compiled methods, we do this rather // crude check for them. if (method) { + // PYSIDE-535: This macro is redefined in a compatible way in pep384 if (PyMethod_Check(method)) { if (PyMethod_GET_SELF(method) == reinterpret_cast<PyObject *>(wrapper)) { function = PyMethod_GET_FUNCTION(method); @@ -330,7 +370,8 @@ PyObject *BindingManager::getOverride(const void *cptr, method = nullptr; } } else if (PyObject_HasAttr(method, PyName::im_self()) - && PyObject_HasAttr(method, PyName::im_func())) { + && PyObject_HasAttr(method, PyName::im_func()) + && PyObject_HasAttr(method, Shiboken::PyMagicName::code())) { PyObject *im_self = PyObject_GetAttr(method, PyName::im_self()); // Not retaining a reference inline with what PyMethod_GET_SELF does. Py_DECREF(im_self); @@ -350,41 +391,59 @@ PyObject *BindingManager::getOverride(const void *cptr, } if (method != nullptr) { - PyObject *defaultMethod; + PyObject *defaultMethod{}; PyObject *mro = Py_TYPE(wrapper)->tp_mro; int size = PyTuple_GET_SIZE(mro); + bool defaultFound = false; // 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 idx = 1; idx < size - 1; ++idx) { auto *parent = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx)); - if (parent->tp_dict) { - defaultMethod = PyDict_GetItem(parent->tp_dict, pyMethodName); - if (defaultMethod && function != defaultMethod) - return method; + AutoDecRef tpDict(PepType_GetDict(parent)); + auto *parentDict = tpDict.object(); + if (parentDict) { + defaultMethod = PyDict_GetItem(parentDict, pyMethodName); + if (defaultMethod) { + defaultFound = true; + if (function != defaultMethod) + return method; + } } } - + // PYSIDE-2255: If no default method was found, use the method. + if (!defaultFound) + return method; Py_DECREF(method); } return nullptr; } -void BindingManager::addClassInheritance(SbkObjectType *parent, SbkObjectType *child) +void BindingManager::addClassInheritance(Module::TypeInitStruct *parent, + Module::TypeInitStruct *child) +{ + m_d->classHierarchy.addEdge(GraphNode(parent), GraphNode(child)); +} + +BindingManager::TypeCptrPair BindingManager::findDerivedType(void *cptr, PyTypeObject *type) const { - m_d->classHierarchy.addEdge(parent, child); + return m_d->classHierarchy.identifyType(cptr, type, type); } -SbkObjectType *BindingManager::resolveType(void **cptr, SbkObjectType *type) +// FIXME PYSIDE7: remove, just for compatibility +PyTypeObject *BindingManager::resolveType(void **cptr, PyTypeObject *type) { - SbkObjectType *identifiedType = m_d->classHierarchy.identifyType(cptr, type, type); - return identifiedType ? identifiedType : type; + auto result = findDerivedType(*cptr, type); + if (result.second != nullptr) + *cptr = result.second; + return result.first != nullptr ? result.first : type; } std::set<PyObject *> BindingManager::getAllPyObjects() { std::set<PyObject *> pyObjects; + std::lock_guard<std::recursive_mutex> guard(m_d->wrapperMapLock); const WrapperMap &wrappersMap = m_d->wrapperMapper; auto it = wrappersMap.begin(); for (; it != wrappersMap.end(); ++it) @@ -396,10 +455,94 @@ std::set<PyObject *> BindingManager::getAllPyObjects() void BindingManager::visitAllPyObjects(ObjectVisitor visitor, void *data) { WrapperMap copy = m_d->wrapperMapper; - for (auto it = copy.begin(); it != copy.end(); ++it) { - if (hasWrapper(it->first)) - visitor(it->second, data); + for (const auto &p : copy) { + if (hasWrapper(p.first)) + visitor(p.second, data); + } +} + +bool BindingManager::dumpTypeGraph(const char *fileName) const +{ + return m_d->classHierarchy.dumpTypeGraph(fileName); +} + +void BindingManager::dumpWrapperMap() +{ + const auto &wrapperMap = m_d->wrapperMapper; + std::cerr << "-------------------------------\n" + << "WrapperMap size: " << wrapperMap.size() << " Types: " + << m_d->classHierarchy.nodeSet().size() << '\n'; + for (auto it = wrapperMap.begin(), end = wrapperMap.end(); it != end; ++it) { + const SbkObject *sbkObj = it->second; + std::cerr << "key: " << it->first << ", value: " + << static_cast<const void *>(sbkObj) << " (" + << (Py_TYPE(sbkObj))->tp_name << ", refcnt: " + << Py_REFCNT(reinterpret_cast<const PyObject *>(sbkObj)) << ")\n"; + } + std::cerr << "-------------------------------\n"; +} + +static bool isPythonType(PyTypeObject *type) +{ + // This is a type which should be called by multiple inheritance. + // It is either a pure Python type or a derived PySide type. + return !ObjectType::checkType(type) || ObjectType::isUserType(type); +} + +bool callInheritedInit(PyObject *self, PyObject *args, PyObject *kwds, + const char *fullName) +{ + using Shiboken::AutoDecRef; + + static PyObject *const _init = String::createStaticString("__init__"); + static PyObject *objectInit = + PyObject_GetAttr(reinterpret_cast<PyObject *>(&PyBaseObject_Type), _init); + + // A native C++ self cannot have multiple inheritance. + if (!Object::isUserType(self)) + return false; + + auto *startType = Py_TYPE(self); + auto *mro = startType->tp_mro; + Py_ssize_t idx, n = PyTuple_GET_SIZE(mro); + auto classNameLen = std::strrchr(fullName, '.') - fullName; + /* No need to check the last one: it's gonna be skipped anyway. */ + for (idx = 0; idx + 1 < n; ++idx) { + auto *lookType = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx)); + const char *lookName = lookType->tp_name; + auto lookLen = long(std::strlen(lookName)); + if (std::strncmp(lookName, fullName, classNameLen) == 0 && lookLen == classNameLen) + break; + } + // We are now at the first non-Python class `QObject`. + // mro: ('C', 'A', 'QObject', 'Object', 'B', 'object') + // We want to catch class `B` and call its `__init__`. + for (idx += 1; idx + 1 < n; ++idx) { + auto *t = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx)); + if (isPythonType(t)) + break; } + if (idx >= n) + return false; + + auto *obSubType = PyTuple_GET_ITEM(mro, idx); + auto *subType = reinterpret_cast<PyTypeObject *>(obSubType); + if (subType == &PyBaseObject_Type) + return false; + AutoDecRef func(PyObject_GetAttr(obSubType, _init)); + // PYSIDE-2654: If this has no implementation then we get object.__init__ + // but that is the same case like above. + if (func == objectInit) + return false; + // PYSIDE-2294: We need to explicitly ignore positional args in a mixin class. + SBK_UNUSED(args); + AutoDecRef newArgs(PyTuple_New(1)); + auto *newArgsOb = newArgs.object(); + Py_INCREF(self); + PyTuple_SET_ITEM(newArgsOb, 0, self); + // Note: This can fail, so please always check the error status. + AutoDecRef result(PyObject_Call(func, newArgs, kwds)); + return true; } } // namespace Shiboken diff --git a/sources/shiboken6/libshiboken/bindingmanager.h b/sources/shiboken6/libshiboken/bindingmanager.h index 5b2246685..54c4e486a 100644 --- a/sources/shiboken6/libshiboken/bindingmanager.h +++ b/sources/shiboken6/libshiboken/bindingmanager.h @@ -1,58 +1,27 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef BINDINGMANAGER_H #define BINDINGMANAGER_H #include "sbkpython.h" -#include <set> #include "shibokenmacros.h" +#include <set> +#include <utility> + struct SbkObject; -struct SbkObjectType; namespace Shiboken { +namespace Module { +struct TypeInitStruct; +} + struct DestructorEntry; -typedef void (*ObjectVisitor)(SbkObject *, void *); +using ObjectVisitor = void (*)(SbkObject *, void *); class LIBSHIBOKEN_API BindingManager { @@ -75,7 +44,15 @@ public: SbkObject *retrieveWrapper(const void *cptr); PyObject *getOverride(const void *cptr, PyObject *nameCache[], const char *methodName); - void addClassInheritance(SbkObjectType *parent, SbkObjectType *child); + void addClassInheritance(Module::TypeInitStruct *parent, Module::TypeInitStruct *child); + /// Try to find the correct type of cptr via type discovery knowing that it's at least + /// of type \p type. If a derived class is found, it returns a cptr cast to the type + /// (which may be different in case of multiple inheritance. + /// \param cptr a pointer to the instance of type \p type + /// \param type type of cptr + using TypeCptrPair = std::pair<PyTypeObject *, void *>; + TypeCptrPair findDerivedType(void *cptr, PyTypeObject *type) const; + /** * 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. @@ -83,7 +60,7 @@ public: * \param type type of *cptr * \warning This function is slow, use it only as last resort. */ - SbkObjectType *resolveType(void **cptr, SbkObjectType *type); + [[deprecated]] PyTypeObject *resolveType(void **cptr, PyTypeObject *type); std::set<PyObject *> getAllPyObjects(); @@ -96,6 +73,9 @@ public: */ void visitAllPyObjects(ObjectVisitor visitor, void *data); + bool dumpTypeGraph(const char *fileName) const; + void dumpWrapperMap(); + private: ~BindingManager(); BindingManager(); @@ -104,6 +84,9 @@ private: BindingManagerPrivate *m_d; }; +LIBSHIBOKEN_API bool callInheritedInit(PyObject *self, PyObject *args, PyObject *kwds, + const char *fullName); + } // namespace Shiboken #endif // BINDINGMANAGER_H diff --git a/sources/shiboken6/libshiboken/bufferprocs_py37.cpp b/sources/shiboken6/libshiboken/bufferprocs_py37.cpp index da6bb00a3..4ccf970e5 100644 --- a/sources/shiboken6/libshiboken/bufferprocs_py37.cpp +++ b/sources/shiboken6/libshiboken/bufferprocs_py37.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /***************************************************************************** * diff --git a/sources/shiboken6/libshiboken/bufferprocs_py37.h b/sources/shiboken6/libshiboken/bufferprocs_py37.h index 6fc7a3ece..e16194e50 100644 --- a/sources/shiboken6/libshiboken/bufferprocs_py37.h +++ b/sources/shiboken6/libshiboken/bufferprocs_py37.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /* PSF LICENSE AGREEMENT FOR PYTHON 3.7.0 @@ -103,8 +67,8 @@ typedef struct bufferinfo { void *internal; } Pep_buffer; -typedef int (*getbufferproc)(PyObject *, Pep_buffer *, int); -typedef void (*releasebufferproc)(PyObject *, Pep_buffer *); +using getbufferproc =int (*)(PyObject *, Pep_buffer *, int); +using releasebufferproc = void (*)(PyObject *, Pep_buffer *); /* Maximum number of dimensions */ #define PyBUF_MAX_NDIM 64 diff --git a/sources/shiboken6/libshiboken/debugfreehook.cpp b/sources/shiboken6/libshiboken/debugfreehook.cpp index 3d52d88dc..13df6bd6c 100644 --- a/sources/shiboken6/libshiboken/debugfreehook.cpp +++ b/sources/shiboken6/libshiboken/debugfreehook.cpp @@ -1,49 +1,13 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "debugfreehook.h" #include "bindingmanager.h" #include "gilstate.h" #if defined(_WIN32) && defined(_DEBUG) -#include <crtdbg.h> -#include <windows.h> +# include <sbkwindows.h> +# include <crtdbg.h> #endif #ifdef __GLIBC__ diff --git a/sources/shiboken6/libshiboken/debugfreehook.h b/sources/shiboken6/libshiboken/debugfreehook.h index fdf98d5d3..c19e36ab2 100644 --- a/sources/shiboken6/libshiboken/debugfreehook.h +++ b/sources/shiboken6/libshiboken/debugfreehook.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef DEBUGFREEHOOK_H #define DEBUGFREEHOOK_H diff --git a/sources/shiboken6/libshiboken/embed/embedding_generator.py b/sources/shiboken6/libshiboken/embed/embedding_generator.py index 1477ff541..a058fd372 100644 --- a/sources/shiboken6/libshiboken/embed/embedding_generator.py +++ b/sources/shiboken6/libshiboken/embed/embedding_generator.py @@ -1,41 +1,5 @@ -############################################################################# -## -## Copyright (C) 2019 The Qt Company Ltd. -## Contact: https://www.qt.io/licensing/ -## -## This file is part of PySide6. -## -## $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$ -## -############################################################################# +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only """ embedding_generator.py @@ -81,7 +45,7 @@ def runpy(cmd, **kw): subprocess.call([sys.executable, '-E'] + cmd.split(), **kw) -def create_zipfile(limited_api, quiet): +def create_zipfile(use_pyc, quiet): """ Collect all Python files, compile them, create a zip file and make a chunked base64 encoded file from it. @@ -110,7 +74,7 @@ def create_zipfile(limited_api, quiet): if embed_dir != work_dir: utils.copyfile(embed_dir / "signature_bootstrap.py", work_dir) - if limited_api: + if not use_pyc: pass # We cannot compile, unless we have folders per Python version else: files = ' '.join(fn for fn in os.listdir('.')) @@ -126,9 +90,9 @@ def create_zipfile(limited_api, quiet): tmp.close() # also generate a simple embeddable .pyc file for signature_bootstrap.pyc - boot_name = "signature_bootstrap.py" if limited_api else "signature_bootstrap.pyc" + boot_name = "signature_bootstrap.py" if not use_pyc else "signature_bootstrap.pyc" with open(boot_name, "rb") as ldr, open("signature_bootstrap_inc.h", "w") as inc: - _embed_bytefile(ldr, inc, limited_api) + _embed_bytefile(ldr, inc, not use_pyc) os.chdir(cur_dir) if quiet: return @@ -158,22 +122,23 @@ def _embed_file(fin, fout): limit = 50 text = fin.readlines() print(textwrap.dedent(""" - /* - * This is a ZIP archive of all Python files in the directory - * "shiboken6/shibokenmodule/files.dir/shibokensupport/signature" - * There is also a toplevel file "signature_bootstrap.py[c]" that will be - * directly executed from C++ as a bootstrap loader. - */ + // This is a ZIP archive of all Python files in the directory + // "shiboken6/shibokenmodule/files.dir/shibokensupport/signature" + // There is also a toplevel file "signature_bootstrap.py[c]" that will be + // directly executed from C++ as a bootstrap loader. """).strip(), file=fout) block, blocks = 0, len(text) // limit + 1 for idx, line in enumerate(text): if idx % limit == 0: + if block: + fout.write(')"\n') comma = "," if block else "" block += 1 - print(file=fout) - print(f'/* Block {block} of {blocks} */{comma}', file=fout) - print(f'\"{line.strip()}\"', file=fout) - print(f'/* Sentinel */, \"\"', file=fout) + fout.write(f'\n{comma} // Block {block} of {blocks}\nR"(') + else: + fout.write('\n') + fout.write(line.strip()) + fout.write(')"\n\n/* Sentinel */, ""\n') def _embed_bytefile(fin, fout, is_text): @@ -247,9 +212,9 @@ def str2bool(v): if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument('--cmake-dir', nargs="?") - parser.add_argument('--limited-api', type=str2bool) + parser.add_argument('--use-pyc', type=str2bool) parser.add_argument('--quiet', action='store_true') args = parser.parse_args() if args.cmake_dir: work_dir = Path(args.cmake_dir).resolve() - create_zipfile(args.limited_api, args.quiet) + create_zipfile(args.use_pyc, args.quiet) diff --git a/sources/shiboken6/libshiboken/embed/module_collector.py b/sources/shiboken6/libshiboken/embed/module_collector.py index 42ce2667c..a58ce6e4f 100644 --- a/sources/shiboken6/libshiboken/embed/module_collector.py +++ b/sources/shiboken6/libshiboken/embed/module_collector.py @@ -1,44 +1,5 @@ -# This Python file uses the following encoding: utf-8 -# It has been edited by fix-complaints.py . - -############################################################################# -## -## Copyright (C) 2019 The Qt Company Ltd. -## Contact: https://www.qt.io/licensing/ -## -## This file is part of Qt for Python. -## -## $QT_BEGIN_LICENSE:LGPL$ -## Commercial License Usage -## Licensees holding valid commercial Qt licenses may use this file in -## accordance with the commercial license agreement provided with the -## Software or, alternatively, in accordance with the terms contained in -## a written agreement between you and The Qt Company. For licensing terms -## and conditions see https://www.qt.io/terms-conditions. For further -## information use the contact form at https://www.qt.io/contact-us. -## -## GNU Lesser General Public License Usage -## Alternatively, this file may be used under the terms of the GNU Lesser -## General Public License version 3 as published by the Free Software -## Foundation and appearing in the file LICENSE.LGPL3 included in the -## packaging of this file. Please review the following information to -## ensure the GNU Lesser General Public License version 3 requirements -## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -## -## GNU General Public License Usage -## Alternatively, this file may be used under the terms of the GNU -## General Public License version 2.0 or (at your option) the GNU General -## Public license version 3 or any later version approved by the KDE Free -## Qt Foundation. The licenses are as published by the Free Software -## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -## included in the packaging of this file. Please review the following -## information to ensure the GNU General Public License requirements will -## be met: https://www.gnu.org/licenses/gpl-2.0.html and -## https://www.gnu.org/licenses/gpl-3.0.html. -## -## $QT_END_LICENSE$ -## -############################################################################# +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only """ module_collector.py diff --git a/sources/shiboken6/libshiboken/embed/qt_python_license.txt b/sources/shiboken6/libshiboken/embed/qt_python_license.txt index b5f8c581a..e5fdfdf4d 100644 --- a/sources/shiboken6/libshiboken/embed/qt_python_license.txt +++ b/sources/shiboken6/libshiboken/embed/qt_python_license.txt @@ -1,44 +1,5 @@ -# This Python file uses the following encoding: utf-8 -# It has been edited by fix-complaints.py . - -############################################################################# -## -## Copyright (C) 2019 The Qt Company Ltd. -## Contact: https://www.qt.io/licensing/ -## -## This file is part of Qt for Python. -## -## $QT_BEGIN_LICENSE:LGPL$ -## Commercial License Usage -## Licensees holding valid commercial Qt licenses may use this file in -## accordance with the commercial license agreement provided with the -## Software or, alternatively, in accordance with the terms contained in -## a written agreement between you and The Qt Company. For licensing terms -## and conditions see https://www.qt.io/terms-conditions. For further -## information use the contact form at https://www.qt.io/contact-us. -## -## GNU Lesser General Public License Usage -## Alternatively, this file may be used under the terms of the GNU Lesser -## General Public License version 3 as published by the Free Software -## Foundation and appearing in the file LICENSE.LGPL3 included in the -## packaging of this file. Please review the following information to -## ensure the GNU Lesser General Public License version 3 requirements -## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -## -## GNU General Public License Usage -## Alternatively, this file may be used under the terms of the GNU -## General Public License version 2.0 or (at your option) the GNU General -## Public license version 3 or any later version approved by the KDE Free -## Qt Foundation. The licenses are as published by the Free Software -## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -## included in the packaging of this file. Please review the following -## information to ensure the GNU General Public License requirements will -## be met: https://www.gnu.org/licenses/gpl-2.0.html and -## https://www.gnu.org/licenses/gpl-3.0.html. -## -## $QT_END_LICENSE$ -## -############################################################################# +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only ## ## PSF LICENSE AGREEMENT FOR PYTHON 3.7.0 diff --git a/sources/shiboken6/libshiboken/embed/signature_bootstrap.py b/sources/shiboken6/libshiboken/embed/signature_bootstrap.py index 3078ac90a..37f95cd35 100644 --- a/sources/shiboken6/libshiboken/embed/signature_bootstrap.py +++ b/sources/shiboken6/libshiboken/embed/signature_bootstrap.py @@ -1,41 +1,5 @@ -############################################################################# -## -## Copyright (C) 2019 The Qt Company Ltd. -## Contact: https://www.qt.io/licensing/ -## -## This file is part of PySide6. -## -## $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$ -## -############################################################################# +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only """ signature_bootstrap.py @@ -52,22 +16,27 @@ This file replaces the hard to read Python stub in 'signature.cpp', and we could distinguish better between bootstrap related functions and loader functions. It is embedded into 'signature.cpp' as "embed/signature_bootstrap.inc". + +# PYSIDE-1436: Python 3.10 had a problem with EmbeddableZipImporter because the +imports were in the functions. Moved them outside into the globals. """ recursion_trap = 0 -# We avoid real imports in phase 1 that could fail (simply removed all). -# Python 2 is not able to import when the extension import is still active. -# Phase 1 simply defines the functions, which will be used in Phase 2. -# PYSIDE-1621: This can be removed after the backport but we leave it so. +import base64 +import importlib +import io +import os +import sys +import traceback +import zipfile + +from contextlib import contextmanager +from importlib.machinery import ModuleSpec +from pathlib import Path + def bootstrap(): - import sys - import os - import tempfile - import traceback - from contextlib import contextmanager - from pathlib import Path global recursion_trap if recursion_trap: @@ -93,52 +62,83 @@ def bootstrap(): import shibokensupport yield except Exception as e: - print("Problem importing shibokensupport:") - print(f"{e.__class__.__name__}: {e}") + f = sys.stderr + print("Problem importing shibokensupport:", file=f) + print(f"{e.__class__.__name__}: {e}", file=f) traceback.print_exc() - print("sys.path:") + print("sys.path:", file=f) for p in sys.path: - print(" " + p) - sys.stdout.flush() + print(" " + p, file=f) + f.flush() sys.exit(-1) target.remove(support_path) - import shiboken6 as root - path = Path(root.__file__) - rp = path.parent.resolve() - # This can be the shiboken6 directory or the binary module, so search. - look_for = Path("files.dir") / "shibokensupport" / "signature" / "loader.py" - while not (rp / look_for).exists(): - dir = rp.parent - if dir == rp: # Hit root, '/', 'C:\', '\\server\share' - break - rp = dir - - # Here we decide if we work embedded or not. - embedding_var = "pyside_uses_embedding" - use_embedding = bool(getattr(sys, embedding_var, False)) - loader_path = rp / look_for - files_dir = loader_path.parents[2] - assert files_dir.name == "files.dir" - - if not loader_path.exists(): - use_embedding = True - setattr(sys, embedding_var, use_embedding) - - if use_embedding: - target, support_path = prepare_zipfile() + # Here we decide if we re-incarnate the embedded files or use embedding. + incarnated = find_incarnated_files() + if incarnated: + target, support_path = sys.path, os.fspath(incarnated) else: - target, support_path = sys.path, os.fspath(files_dir) + target, support_path = prepare_zipfile() + with ensure_shibokensupport(target, support_path): + from shibokensupport.signature import loader + return loader + +# Newer functionality: +# This function checks if the support directory exist and returns it. +# If does not exist, we try to create it and return it. +# Otherwise, we return None. +def find_incarnated_files(): + import shiboken6 as root + files_dir = Path(root.__file__).resolve().parent / "files.dir" + handle_embedding_switch(files_dir) + if files_dir.exists(): + sys.path.insert(0, os.fspath(files_dir)) + # Note: To avoid recursion problems, we need to preload the loader. + # But that has the side-effect that we need to delay the feature + # initialization until all function pointers are set. + # See `post_init_func` in signature_globals.cpp . + import shibokensupport.signature.loader + del sys.path[0] + return files_dir + return None + + +def handle_embedding_switch(files_dir): + """ + This handles the optional environment variable `SBK_EMBED` + if not set : do nothing + if set to 0, false, no : de-virtualize the Python files + if set to 1, true, yes : virtualize again (delete "files.dir") + """ + env_name = "SBK_EMBED" + env_var = os.environ.get(env_name) + if not env_var: + return + if env_var.lower() in ("1", "t", "true", "y", "yes"): + import shutil + shutil.rmtree(files_dir, ignore_errors=True) + elif env_var.lower() in ("0", "f", "false", "n", "no"): + reincarnate_files(files_dir) + + +def reincarnate_files(files_dir): + target, zip = prepare_zipfile() + names = (_ for _ in zip.zfile.namelist() if _.endswith(".py")) + try: + # First check mkdir to get an error when we cannot write. + files_dir.mkdir(exist_ok=True) + except os.error as e: + print(f"SBK_EMBED=False: Warning: Cannot write into {files_dir}") + return None try: - with ensure_shibokensupport(target, support_path): - from shibokensupport.signature import loader + # Then check for a real error when unpacking the zip file. + zip.zfile.extractall(path=files_dir, members=names) + return files_dir except Exception as e: - print('Exception:', e) - traceback.print_exc(file=sys.stdout) - - return loader - + print(f"{e.__class__.__name__}: {e}", file=sys.stderr) + traceback.print_exc() + raise # New functionality: Loading from a zip archive. # There exists the zip importer, but as it is written, only real zip files are @@ -159,10 +159,6 @@ def prepare_zipfile(): The sys.path way does not work, instead we need to use sys.meta_path . See https://docs.python.org/3/library/sys.html#sys.meta_path """ - import base64 - import io - import sys - import zipfile # 'zipstring_sequence' comes from signature.cpp zipbytes = base64.b64decode(''.join(zipstring_sequence)) @@ -181,31 +177,28 @@ class EmbeddableZipImporter(object): return None self.zfile = zip_file - self._path2mod = {_.filename : p2m(_.filename) for _ in zip_file.filelist} - self._mod2path = {_[1] : _[0] for _ in self._path2mod.items()} + self._mod2path = {p2m(_.filename) : _.filename for _ in zip_file.filelist} - def find_module(self, fullname, path): - return self if self._mod2path.get(fullname) else None + def find_spec(self, fullname, path, target=None): + path = self._mod2path.get(fullname) + return ModuleSpec(fullname, self) if path else None - def load_module(self, fullname): - import importlib - import sys + def create_module(self, spec): + return None - filename = self._mod2path.get(fullname) - if filename not in self._path2mod: - raise ImportError(fullname) - module_spec = importlib.machinery.ModuleSpec(fullname, None) - new_module = importlib.util.module_from_spec(module_spec) + def exec_module(self, module): + fullname = module.__spec__.name + filename = self._mod2path[fullname] with self.zfile.open(filename, "r") as f: # "rb" not for zipfile - exec(f.read(), new_module.__dict__) - new_module.__file__ = filename - new_module.__loader__ = self + codeob = compile(f.read(), filename, "exec") + exec(codeob, module.__dict__) + module.__file__ = filename + module.__loader__ = self if filename.endswith("/__init__.py"): - new_module.__path__ = [] - new_module.__package__ = fullname + module.__path__ = [] + module.__package__ = fullname else: - new_module.__package__ = fullname.rpartition('.')[0] - sys.modules[fullname] = new_module - return new_module + module.__package__ = fullname.rpartition('.')[0] + sys.modules[fullname] = module # eof diff --git a/sources/shiboken6/libshiboken/gilstate.cpp b/sources/shiboken6/libshiboken/gilstate.cpp index 76a4d0e61..8daa93b8b 100644 --- a/sources/shiboken6/libshiboken/gilstate.cpp +++ b/sources/shiboken6/libshiboken/gilstate.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "gilstate.h" diff --git a/sources/shiboken6/libshiboken/gilstate.h b/sources/shiboken6/libshiboken/gilstate.h index fbf39ead0..abad9d955 100644 --- a/sources/shiboken6/libshiboken/gilstate.h +++ b/sources/shiboken6/libshiboken/gilstate.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef GILSTATE_H #define GILSTATE_H diff --git a/sources/shiboken6/libshiboken/helper.cpp b/sources/shiboken6/libshiboken/helper.cpp index 02f8177f3..46af68956 100644 --- a/sources/shiboken6/libshiboken/helper.cpp +++ b/sources/shiboken6/libshiboken/helper.cpp @@ -1,67 +1,64 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "helper.h" +#include "basewrapper_p.h" #include "sbkstring.h" #include "sbkstaticstrings.h" +#include "sbkstaticstrings.h" +#include "pep384impl.h" + +#include <algorithm> +#include <optional> #include <iomanip> #include <iostream> - +#include <climits> +#include <cstring> #include <cstdarg> +#include <cctype> #ifdef _WIN32 -# ifndef NOMINMAX -# define NOMINMAX -# endif -# include <windows.h> +# include <sbkwindows.h> #else # include <pthread.h> #endif -#include <algorithm> +static std::optional<std::string> getStringAttr(PyObject *obj, const char *what) +{ + if (PyObject_HasAttrString(obj, what) != 0) { // Check first to suppress error. + Shiboken::AutoDecRef result(PyObject_GetAttrString(obj, what)); + if (PyUnicode_Check(result.object()) != 0) + return _PepUnicode_AsString(result.object()); + } + return std::nullopt; +} -static void formatPyTypeObject(const PyTypeObject *obj, std::ostream &str) +static std::optional<int> getIntAttr(PyObject *obj, const char *what) { - if (obj) { - str << '"' << obj->tp_name << "\", 0x" << std::hex - << obj->tp_flags << std::dec; + if (PyObject_HasAttrString(obj, what) != 0) { // Check first to suppress error. + Shiboken::AutoDecRef result(PyObject_GetAttrString(obj, what)); + if (PyLong_Check(result.object()) != 0) + return PyLong_AsLong(result.object()); + } + return std::nullopt; +} + +static bool verbose = false; + +static void formatTypeTuple(PyObject *t, const char *what, std::ostream &str); + +static void formatPyTypeObject(const PyTypeObject *obj, std::ostream &str, bool verbose) +{ + if (obj == nullptr) { + str << '0'; + return; + } + + str << '"' << obj->tp_name << '"'; + if (verbose) { + bool immutableType = false; + str << ", 0x" << std::hex << obj->tp_flags << std::dec; if (obj->tp_flags & Py_TPFLAGS_HEAPTYPE) str << " [heaptype]"; if (obj->tp_flags & Py_TPFLAGS_BASETYPE) @@ -84,8 +81,59 @@ static void formatPyTypeObject(const PyTypeObject *obj, std::ostream &str) str << " [type]"; if (obj->tp_flags & Py_TPFLAGS_IS_ABSTRACT) str << " [abstract]"; - } else { - str << '0'; + if (obj->tp_flags & Py_TPFLAGS_READY) + str << " [ready]"; + if (obj->tp_flags & Py_TPFLAGS_READYING) + str << " [readying]"; + if (obj->tp_flags & Py_TPFLAGS_METHOD_DESCRIPTOR) + str << " [method_descriptor]"; +# ifndef Py_LIMITED_API + if (obj->tp_flags & Py_TPFLAGS_HAVE_VECTORCALL) + str << " [vectorcall]"; +# endif // !Py_LIMITED_API +# if PY_VERSION_HEX >= 0x030A0000 + immutableType = (obj->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) != 0; + if (immutableType) + str << " [immutabletype]"; + if (obj->tp_flags & Py_TPFLAGS_DISALLOW_INSTANTIATION) + str << " [disallow_instantiation]"; +# ifndef Py_LIMITED_API + if (obj->tp_flags & Py_TPFLAGS_MAPPING) + str << " [mapping]"; + if (obj->tp_flags & Py_TPFLAGS_SEQUENCE) + str << " [sequence]"; +# endif // !Py_LIMITED_API +# endif // 3.10 + if (obj->tp_basicsize != 0) + str << ", basicsize=" << obj->tp_basicsize; + if (verbose) { + formatTypeTuple(obj->tp_bases, "bases", str); + formatTypeTuple(obj->tp_mro, "mro", str); + if (!immutableType) { + auto *underlying = reinterpret_cast<const PyObject *>(obj)->ob_type; + if (underlying != nullptr && underlying != obj) { + str << ", underlying=\"" << underlying->tp_name << '"'; + } + } + } + } +} + +static void formatTypeTuple(PyObject *t, const char *what, std::ostream &str) +{ + const Py_ssize_t size = t != nullptr && PyTuple_Check(t) != 0 ? PyTuple_Size(t) : 0; + if (size > 0) { + str << ", " << what << "=[" << size << "]{"; + for (Py_ssize_t i = 0; i < size; ++i) { + if (i != 0) + str << ", "; + Shiboken::AutoDecRef item(PyTuple_GetItem(t, i)); + if (item.isNull()) + str << '0'; // Observed with non-ready types + else + str << '"' << reinterpret_cast<PyTypeObject *>(item.object())->tp_name << '"'; + } + str << '}'; } } @@ -110,24 +158,224 @@ static void formatPySequence(PyObject *obj, std::ostream &str) str << '>'; } -static void formatPyObject(PyObject *obj, std::ostream &str) +static void formatPyTuple(PyObject *obj, std::ostream &str) +{ + + const Py_ssize_t size = PyTuple_Size(obj); + str << size << " <"; + for (Py_ssize_t i = 0; i < size; ++i) { + if (i) + str << ", "; + str << '('; + PyObject *item = PyTuple_GetItem(obj, i); + formatPyObject(item, str); + str << ')'; + Py_XDECREF(item); + } + str << '>'; +} + +static void formatPyDict(PyObject *obj, std::ostream &str) { - if (obj) { - formatPyTypeObject(obj->ob_type, str); - str << ", "; - if (PyLong_Check(obj)) - str << PyLong_AsLong(obj); - else if (PyFloat_Check(obj)) - str << PyFloat_AsDouble(obj); - else if (PyUnicode_Check(obj)) - str << '"' << _PepUnicode_AsString(obj) << '"'; - else if (PySequence_Check(obj)) - formatPySequence(obj, str); + PyObject *key; + PyObject *value; + Py_ssize_t pos = 0; + str << '{'; + while (PyDict_Next(obj, &pos, &key, &value) != 0) { + if (pos) + str << ", "; + str << Shiboken::debugPyObject(key) << '=' << Shiboken::debugPyObject(value); + } + str << '}'; +} + +// Helper to format a 0-terminated character sequence +template <class Char> +static void formatCharSequence(const Char *s, std::ostream &str) +{ + str << '"'; + const auto oldFillC = str.fill('0'); + str << std::hex; + for (; *s; ++s) { + const unsigned c = *s; + if (c < 127) + str << char(c); else - str << "<unknown>"; + str << "0x" << std::right << std::setw(sizeof(Char) * 2) << c << std::left; + } + str << std::dec; + str.fill(oldFillC); + str << '"'; +} + +static void formatPyUnicode(PyObject *obj, std::ostream &str) +{ + // Note: The below call create the PyCompactUnicodeObject.utf8 representation + str << '"' << _PepUnicode_AsString(obj) << '"'; + if (!verbose) + return; + + str << " (" << PyUnicode_GetLength(obj) << ')'; + const auto kind = _PepUnicode_KIND(obj); + switch (kind) { +#if PY_VERSION_HEX < 0x030C0000 + case PepUnicode_WCHAR_KIND: + str << " [wchar]"; + break; +#endif + case PepUnicode_1BYTE_KIND: + str << " [1byte]"; + break; + case PepUnicode_2BYTE_KIND: + str << " [2byte]"; + break; + case PepUnicode_4BYTE_KIND: + str << " [4byte]"; + break; + } + + const bool ascii = _PepUnicode_IS_ASCII(obj); + if (ascii) + str << " [ascii]"; + const bool compact = _PepUnicode_IS_COMPACT(obj); + if (compact) + str << " [compact]"; + void *data =_PepUnicode_DATA(obj); + str << ", data="; + switch (kind) { +#if PY_VERSION_HEX < 0x030C0000 + case PepUnicode_WCHAR_KIND: + formatCharSequence(reinterpret_cast<const wchar_t *>(data), str); +#endif + break; + case PepUnicode_1BYTE_KIND: + formatCharSequence(reinterpret_cast<const Py_UCS1 *>(data), str); + break; + case PepUnicode_2BYTE_KIND: + formatCharSequence(reinterpret_cast<const Py_UCS2 *>(data), str); + break; + case PepUnicode_4BYTE_KIND: + formatCharSequence(reinterpret_cast<const Py_UCS4 *>(data), str); + break; + } + +#ifndef Py_LIMITED_API + const char *utf8 = nullptr; + if (!ascii && compact && kind == PepUnicode_1BYTE_KIND) { + const auto *compactObj = reinterpret_cast<const PyCompactUnicodeObject *>(obj); + if (compactObj->utf8_length) + utf8 = compactObj->utf8; + } + if (utf8) { + str << ", utf8="; + formatCharSequence(reinterpret_cast<const Py_UCS1 *>(utf8), str); } else { - str << '0'; + str << ", no-utf8"; } +#endif // !Py_LIMITED_API +} + +static std::string getQualName(PyObject *obj) +{ + Shiboken::AutoDecRef result(PyObject_GetAttr(obj, Shiboken::PyMagicName::qualname())); + return result.object() != nullptr + ? _PepUnicode_AsString(result.object()) : std::string{}; +} + +static void formatPyFunction(PyObject *obj, std::ostream &str) +{ + str << '"' << getQualName(obj) << "()\""; +} + +static void formatPyMethod(PyObject *obj, std::ostream &str) +{ + if (auto *func = PyMethod_Function(obj)) + formatPyFunction(func, str); + str << ", instance=" << PyMethod_Self(obj); +} + +static void formatPyCodeObject(PyObject *obj, std::ostream &str) +{ + if (auto name = getStringAttr(obj, "co_name")) + str << '"' << name.value() << '"'; + if (auto qualName = getStringAttr(obj, "co_qualname")) + str << ", co_qualname=\"" << qualName.value() << '"'; + if (auto flags = getIntAttr(obj, "co_flags")) + str << ", flags=0x" << std::hex << flags.value() << std::dec; + if (auto c = getIntAttr(obj, "co_argcount")) + str << ", co_argcounts=" << c.value(); + if (auto c = getIntAttr(obj, "co_posonlyargcount")) + str << ", co_posonlyargcount=" << c.value(); + if (auto c = getIntAttr(obj, "co_kwonlyargcount")) + str << ", co_kwonlyargcount=" << c.value(); + if (auto fileName = getStringAttr(obj, "co_filename")) { + str << " @" << fileName.value(); + if (auto l = getIntAttr(obj, "co_firstlineno")) + str << ':'<< l.value(); + } +} + +static void formatPyObjectHelper(PyObject *obj, std::ostream &str) +{ + str << ", "; + if (obj == Py_None) { + str << "None"; + return; + } + if (obj == Py_True) { + str << "True"; + return; + } + if (obj == Py_False) { + str << "False"; + return; + } + const auto refs = Py_REFCNT(obj); + if (refs == UINT_MAX) // _Py_IMMORTAL_REFCNT + str << "immortal, "; + else + str << "refs=" << refs << ", "; + if (PyType_Check(obj)) { + str << "type: "; + formatPyTypeObject(reinterpret_cast<PyTypeObject *>(obj), str, true); + return; + } + formatPyTypeObject(obj->ob_type, str, false); + str << ", "; + if (PyLong_Check(obj)) { + const auto llv = PyLong_AsLongLong(obj); + if (PyErr_Occurred() != PyExc_OverflowError) { + str << llv; + } else { + PyErr_Clear(); + str << "0x" << std::hex << PyLong_AsUnsignedLongLong(obj) << std::dec; + } + } + else if (PyFloat_Check(obj)) + str << PyFloat_AsDouble(obj); + else if (PyUnicode_Check(obj)) + formatPyUnicode(obj, str); + else if (PyFunction_Check(obj) != 0) + formatPyFunction(obj, str); + else if (PyMethod_Check(obj) != 0) + formatPyMethod(obj, str); + else if (PepCode_Check(obj) != 0) + formatPyCodeObject(obj, str); + else if (PySequence_Check(obj)) + formatPySequence(obj, str); + else if (PyDict_Check(obj)) + formatPyDict(obj, str); + else if (PyTuple_CheckExact(obj)) + formatPyTuple(obj, str); + else + str << "<unknown>"; +} + +static void formatPyObject(PyObject *obj, std::ostream &str) +{ + str << obj; + if (obj) + formatPyObjectHelper(obj, str); } namespace Shiboken @@ -137,6 +385,10 @@ debugPyObject::debugPyObject(PyObject *o) : m_object(o) { } +debugSbkObject::debugSbkObject(SbkObject *o) : m_object(o) +{ +} + debugPyTypeObject::debugPyTypeObject(const PyTypeObject *o) : m_object(o) { } @@ -148,7 +400,18 @@ debugPyBuffer::debugPyBuffer(const Py_buffer &b) : m_buffer(b) std::ostream &operator<<(std::ostream &str, const debugPyTypeObject &o) { str << "PyTypeObject("; - formatPyTypeObject(o.m_object, str); + formatPyTypeObject(o.m_object, str, true); + str << ')'; + return str; +} + +std::ostream &operator<<(std::ostream &str, const debugSbkObject &o) +{ + str << "SbkObject(" << o.m_object; + if (o.m_object) { + Shiboken::Object::_debugFormat(str, o.m_object); + formatPyObjectHelper(reinterpret_cast<PyObject *>(o.m_object), str); + } str << ')'; return str; } @@ -173,6 +436,18 @@ std::ostream &operator<<(std::ostream &str, const debugPyBuffer &b) return str; } +std::ios_base &debugVerbose(std::ios_base &s) +{ + verbose = true; + return s; +} + +std::ios_base &debugBrief(std::ios_base &s) +{ + verbose = false; + return s; +} + #ifdef _WIN32 // Converts a Unicode string to a string encoded in the Windows console's // code page via wchar_t for use with argv (PYSIDE-1425). @@ -255,12 +530,12 @@ int *sequenceToIntArray(PyObject *obj, bool zeroTerminated) for (int i = 0; i < size; i++) { PyObject *item = PySequence_Fast_GET_ITEM(seq.object(), i); - if (!PyInt_Check(item)) { + if (!PyLong_Check(item)) { PyErr_SetString(PyExc_TypeError, "Sequence of ints expected"); delete[] array; return nullptr; } - array[i] = PyInt_AsLong(item); + array[i] = PyLong_AsLong(item); } if (zeroTerminated) @@ -316,4 +591,47 @@ ThreadId mainThreadId() return _mainThreadId; } +const char *typeNameOf(const char *typeIdName) +{ + auto size = std::strlen(typeIdName); +#if defined(Q_CC_MSVC) // MSVC: "class QPaintDevice * __ptr64" + if (auto *lastStar = strchr(typeName, '*')) { + // MSVC: "class QPaintDevice * __ptr64" + while (*--lastStar == ' ') { + } + size = lastStar - typeName + 1; + } +#else // g++, Clang: "QPaintDevice *" -> "P12QPaintDevice" + if (size > 2 && typeIdName[0] == 'P' && std::isdigit(typeIdName[1])) { + ++typeIdName; + --size; + } +#endif + char *result = new char[size + 1]; + result[size] = '\0'; + std::memcpy(result, typeIdName, size); + return result; +} + +#if !defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x030A0000 && !defined(PYPY_VERSION) +static int _getPyVerbose() +{ + PyConfig config; + PyConfig_InitPythonConfig(&config); + return config.verbose; +} +#endif // !Py_LIMITED_API >= 3.10 + +int pyVerbose() +{ +#ifdef Py_LIMITED_API + return Pep_GetVerboseFlag(); +#elif PY_VERSION_HEX >= 0x030A0000 && !defined(PYPY_VERSION) + static const int result = _getPyVerbose(); + return result; +#else + return Py_VerboseFlag; +#endif +} + } // namespace Shiboken diff --git a/sources/shiboken6/libshiboken/helper.h b/sources/shiboken6/libshiboken/helper.h index 8221d68b0..f226e8c24 100644 --- a/sources/shiboken6/libshiboken/helper.h +++ b/sources/shiboken6/libshiboken/helper.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef HELPER_H #define HELPER_H @@ -72,6 +36,10 @@ LIBSHIBOKEN_API bool listToArgcArgv(PyObject *argList, int *argc, char ***argv, */ LIBSHIBOKEN_API int *sequenceToIntArray(PyObject *obj, bool zeroTerminated = false); +/// Fix a type name returned by typeid(t).name(), depending on compiler. +/// \returns Fixed name (allocated). +LIBSHIBOKEN_API const char *typeNameOf(const char *typeIdName); + /** * Creates and automatically deallocates C++ arrays. */ @@ -84,8 +52,9 @@ class AutoArrayPointer AutoArrayPointer &operator=(const AutoArrayPointer &) = delete; AutoArrayPointer &operator=(AutoArrayPointer &&) = delete; - explicit AutoArrayPointer(ssize_t size) { data = new T[size]; } - T &operator[](ssize_t pos) { return data[pos]; } + + explicit AutoArrayPointer(Py_ssize_t size) { data = new T[size]; } + T &operator[](Py_ssize_t pos) { return data[pos]; } operator T *() const { return data; } ~AutoArrayPointer() { delete[] data; } private: @@ -96,6 +65,8 @@ using ThreadId = unsigned long long; LIBSHIBOKEN_API ThreadId currentThreadId(); LIBSHIBOKEN_API ThreadId mainThreadId(); +LIBSHIBOKEN_API int pyVerbose(); + /** * An utility function used to call PyErr_WarnEx with a formatted message. */ @@ -108,6 +79,13 @@ struct LIBSHIBOKEN_API debugPyObject PyObject *m_object; }; +struct LIBSHIBOKEN_API debugSbkObject +{ + explicit debugSbkObject(SbkObject *o); + + SbkObject *m_object; +}; + struct LIBSHIBOKEN_API debugPyTypeObject { explicit debugPyTypeObject(const PyTypeObject *o); @@ -122,10 +100,20 @@ struct LIBSHIBOKEN_API debugPyBuffer const Py_buffer &m_buffer; }; +struct debugPyArrayObject +{ + explicit debugPyArrayObject(PyObject *object) : m_object(object) {} + + PyObject *m_object; +}; + LIBSHIBOKEN_API std::ostream &operator<<(std::ostream &str, const debugPyObject &o); +LIBSHIBOKEN_API std::ostream &operator<<(std::ostream &str, const debugSbkObject &o); LIBSHIBOKEN_API std::ostream &operator<<(std::ostream &str, const debugPyTypeObject &o); LIBSHIBOKEN_API std::ostream &operator<<(std::ostream &str, const debugPyBuffer &b); - +LIBSHIBOKEN_API std::ostream &operator<<(std::ostream &str, const debugPyArrayObject &b); +LIBSHIBOKEN_API std::ios_base &debugVerbose(std::ios_base &s); +LIBSHIBOKEN_API std::ios_base &debugBrief(std::ios_base &s); } // namespace Shiboken diff --git a/sources/shiboken6/libshiboken/pep384_issue33738.cpp b/sources/shiboken6/libshiboken/pep384_issue33738.cpp deleted file mode 100644 index c20edeefa..000000000 --- a/sources/shiboken6/libshiboken/pep384_issue33738.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// There is a bug in Python 3.6 that turned the Index_Check function -// into a macro without taking care of the limited API. -// This leads to the single problem that we don't have -// access to PyLong_Type's nb_index field which is no heap type. -// We cannot easily create this function by inheritance since it is -// not inherited. -// -// Simple solution: Create the structure and write such a function. -// Long term: Submit a patch to python.org . - -// Update: I did the long-term solution for python 3.7 in issue 33738. - -typedef struct { - /* Number implementations must check *both* - arguments for proper type and implement the necessary conversions - in the slot functions themselves. */ - - binaryfunc nb_add; - binaryfunc nb_subtract; - binaryfunc nb_multiply; - binaryfunc nb_remainder; - binaryfunc nb_divmod; - ternaryfunc nb_power; - unaryfunc nb_negative; - unaryfunc nb_positive; - unaryfunc nb_absolute; - inquiry nb_bool; - unaryfunc nb_invert; - binaryfunc nb_lshift; - binaryfunc nb_rshift; - binaryfunc nb_and; - binaryfunc nb_xor; - binaryfunc nb_or; - unaryfunc nb_int; - void *nb_reserved; /* the slot formerly known as nb_long */ - unaryfunc nb_float; - - binaryfunc nb_inplace_add; - binaryfunc nb_inplace_subtract; - binaryfunc nb_inplace_multiply; - binaryfunc nb_inplace_remainder; - ternaryfunc nb_inplace_power; - binaryfunc nb_inplace_lshift; - binaryfunc nb_inplace_rshift; - binaryfunc nb_inplace_and; - binaryfunc nb_inplace_xor; - binaryfunc nb_inplace_or; - - binaryfunc nb_floor_divide; - binaryfunc nb_true_divide; - binaryfunc nb_inplace_floor_divide; - binaryfunc nb_inplace_true_divide; - - unaryfunc nb_index; - - binaryfunc nb_matrix_multiply; - binaryfunc nb_inplace_matrix_multiply; -} PyNumberMethods; - -// temporary structure until we have a generator for the offsets -typedef struct _oldtypeobject { - PyVarObject ob_base; - void *X01; // const char *tp_name; - void *X02; // Py_ssize_t tp_basicsize; - void *X03; // Py_ssize_t tp_itemsize; - void *X04; // destructor tp_dealloc; - void *X05; // printfunc tp_print; - void *X06; // getattrfunc tp_getattr; - void *X07; // setattrfunc tp_setattr; - void *X08; // PyAsyncMethods *tp_as_async; - void *X09; // reprfunc tp_repr; - PyNumberMethods *tp_as_number; - -} PyOldTypeObject; - -int PyIndex_Check(PyObject *obj) -{ - PyOldTypeObject *type = reinterpret_cast<PyOldTypeObject *>(Py_TYPE(obj)); - return type->tp_as_number != NULL && - type->tp_as_number->nb_index != NULL; -} - diff --git a/sources/shiboken6/libshiboken/pep384ext.h b/sources/shiboken6/libshiboken/pep384ext.h new file mode 100644 index 000000000..021c53d16 --- /dev/null +++ b/sources/shiboken6/libshiboken/pep384ext.h @@ -0,0 +1,89 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef PEP384EXT_H +#define PEP384EXT_H + +#include "pep384impl.h" + +/// Returns the allocator slot of the PyTypeObject. +inline allocfunc PepExt_Type_GetAllocSlot(PyTypeObject *t) +{ + return reinterpret_cast<allocfunc>(PepType_GetSlot(t, Py_tp_alloc)); +} + +/// Invokes the allocator slot of the PyTypeObject. +template <class Type> +inline Type *PepExt_TypeCallAlloc(PyTypeObject *t, Py_ssize_t nitems) +{ + PyObject *result = PepExt_Type_GetAllocSlot(t)(t, nitems); + return reinterpret_cast<Type *>(result); +} + +/// Returns the getattro slot of the PyTypeObject. +inline getattrofunc PepExt_Type_GetGetAttroSlot(PyTypeObject *t) +{ + return reinterpret_cast<getattrofunc>(PepType_GetSlot(t, Py_tp_getattro)); +} + +/// Returns the setattro slot of the PyTypeObject. +inline setattrofunc PepExt_Type_GetSetAttroSlot(PyTypeObject *t) +{ + return reinterpret_cast<setattrofunc>(PepType_GetSlot(t, Py_tp_setattro)); +} + +/// Returns the descr_get slot of the PyTypeObject. +inline descrgetfunc PepExt_Type_GetDescrGetSlot(PyTypeObject *t) +{ + return reinterpret_cast<descrgetfunc>(PepType_GetSlot(t, Py_tp_descr_get)); +} + +/// Invokes the descr_get slot of the PyTypeObject. +inline PyObject *PepExt_Type_CallDescrGet(PyObject *self, PyObject *obj, PyObject *type) +{ + return PepExt_Type_GetDescrGetSlot(Py_TYPE(self))(self, obj, type); +} + +/// Returns the descr_set slot of the PyTypeObject. +inline descrsetfunc PepExt_Type_GetDescrSetSlot(PyTypeObject *t) +{ + return reinterpret_cast<descrsetfunc>(PepType_GetSlot(t, Py_tp_descr_set)); +} + +/// Returns the call slot of the PyTypeObject. +inline ternaryfunc PepExt_Type_GetCallSlot(PyTypeObject *t) +{ + return reinterpret_cast<ternaryfunc>(PepType_GetSlot(t, Py_tp_call)); +} + +/// Returns the new slot of the PyTypeObject. +inline newfunc PepExt_Type_GetNewSlot(PyTypeObject *t) +{ + return reinterpret_cast<newfunc>(PepType_GetSlot(t, Py_tp_new)); +} + +/// Returns the init slot of the PyTypeObject. +inline initproc PepExt_Type_GetInitSlot(PyTypeObject *t) +{ + return reinterpret_cast<initproc>(PepType_GetSlot(t, Py_tp_init)); +} + +/// Returns the free slot of the PyTypeObject. +inline freefunc PepExt_Type_GetFreeSlot(PyTypeObject *t) +{ + return reinterpret_cast<freefunc>(PepType_GetSlot(t, Py_tp_free)); +} + +/// Invokes the free slot of the PyTypeObject. +inline void PepExt_TypeCallFree(PyTypeObject *t, void *object) +{ + PepExt_Type_GetFreeSlot(t)(object); +} + +/// Invokes the free slot of the PyTypeObject. +inline void PepExt_TypeCallFree(PyObject *object) +{ + PepExt_Type_GetFreeSlot(Py_TYPE(object))(object); +} + +#endif // PEP384EXT_H diff --git a/sources/shiboken6/libshiboken/pep384impl.cpp b/sources/shiboken6/libshiboken/pep384impl.cpp index 7678fe661..4b3759456 100644 --- a/sources/shiboken6/libshiboken/pep384impl.cpp +++ b/sources/shiboken6/libshiboken/pep384impl.cpp @@ -1,41 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#define PEP384_INTERN #include "sbkpython.h" #include "autodecref.h" @@ -44,8 +10,7 @@ #include "basewrapper.h" #include "basewrapper_p.h" #include "sbkenum.h" -#include "sbkenum_p.h" -#include "sbkconverter.h" +#include "voidptr.h" #include <cstdlib> #include <cstring> @@ -54,7 +19,7 @@ extern "C" { /* - * The documentation is located in pep384impl_doc.rst + * The documentation is located in `sources/pyside6/doc/developer/limited_api.rst`. * Here is the verification code for PyTypeObject. * We create a type object and check if its fields @@ -72,16 +37,16 @@ dummy_func(PyObject * /* self */, PyObject * /* args */) } static struct PyMethodDef probe_methoddef[] = { - {"dummy", dummy_func, METH_NOARGS}, - {nullptr} + {"dummy", dummy_func, METH_NOARGS, nullptr}, + {nullptr, nullptr, 0, nullptr} }; static PyGetSetDef probe_getseters[] = { - {nullptr} /* Sentinel */ + {nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */ }; static PyMemberDef probe_members[] = { - {nullptr} /* Sentinel */ + {nullptr, 0, 0, 0, nullptr} /* Sentinel */ }; #define probe_tp_dealloc make_dummy(1) @@ -152,6 +117,8 @@ check_PyTypeObject_valid() PyObject *d = PyObject_GetAttr(obtype, Shiboken::PyMagicName::dictoffset()); long probe_tp_dictoffset = PyLong_AsLong(d); PyObject *probe_tp_mro = PyObject_GetAttr(obtype, Shiboken::PyMagicName::mro()); + Shiboken::AutoDecRef tpDict(PepType_GetDict(check)); + auto *checkDict = tpDict.object(); if (false || strcmp(probe_tp_name, check->tp_name) != 0 || probe_tp_basicsize != check->tp_basicsize @@ -168,8 +135,8 @@ check_PyTypeObject_valid() || probe_tp_methods != check->tp_methods || probe_tp_getset != check->tp_getset || probe_tp_base != typetype->tp_base - || !PyDict_Check(check->tp_dict) - || !PyDict_GetItemString(check->tp_dict, "dummy") + || !PyDict_Check(checkDict) + || !PyDict_GetItemString(checkDict, "dummy") || probe_tp_descr_get != check->tp_descr_get || probe_tp_descr_set != check->tp_descr_set || probe_tp_dictoffset != typetype->tp_dictoffset @@ -190,10 +157,6 @@ check_PyTypeObject_valid() Py_DECREF(probe_tp_mro); } -#if PY_VERSION_HEX < PY_ISSUE33738_SOLVED -#include "pep384_issue33738.cpp" -#endif - #endif // Py_LIMITED_API /***************************************************************************** @@ -215,7 +178,7 @@ static PyObject * find_name_in_mro(PyTypeObject *type, PyObject *name, int *error) { Py_ssize_t i, n; - PyObject *mro, *res, *base, *dict; + PyObject *mro, *res, *base; /* Look in tp_dict of types in MRO */ mro = type->tp_mro; @@ -229,9 +192,10 @@ find_name_in_mro(PyTypeObject *type, PyObject *name, int *error) for (i = 0; i < n; i++) { base = PyTuple_GET_ITEM(mro, i); assert(PyType_Check(base)); - dict = ((PyTypeObject *)base)->tp_dict; - assert(dict && PyDict_Check(dict)); - res = PyDict_GetItem(dict, name); + auto *type = reinterpret_cast<PyTypeObject *>(base); + Shiboken::AutoDecRef dict(PepType_GetDict(type)); + assert(!dict.isNull() && PyDict_Check(dict.object())); + res = PyDict_GetItem(dict.object(), name); if (res != nullptr) break; if (PyErr_Occurred()) { @@ -284,20 +248,173 @@ _PepType_Lookup(PyTypeObject *type, PyObject *name) */ #ifdef Py_LIMITED_API -char * -_PepUnicode_AsString(PyObject *str) +// structs and macros modelled after their equivalents in +// cpython/Include/cpython/unicodeobject.h + +struct PepASCIIObject // since 3.12 +{ + PyObject_HEAD + Py_ssize_t length; /* Number of code points in the string */ + Py_hash_t hash; /* Hash value; -1 if not set */ + struct { + unsigned int interned:2; + unsigned int kind:3; + unsigned int compact:1; + unsigned int ascii:1; + unsigned int ready:1; + unsigned int :24; + } state; +}; + +struct PepASCIIObject_311 : public PepASCIIObject +{ + wchar_t *wstr; /* wchar_t representation (null-terminated) */ +}; + +struct PepCompactUnicodeObject // since 3.12 +{ + PepASCIIObject _base; + Py_ssize_t utf8_length; + char *utf8; /* UTF-8 representation (null-terminated) */ +}; + +struct PepCompactUnicodeObject_311 // since 3.12 +{ + PepASCIIObject_311 _base; + Py_ssize_t utf8_length; + char *utf8; /* UTF-8 representation (null-terminated) */ + Py_ssize_t wstr_length; /* Number of code points in wstr */ +}; + +struct PepUnicodeObject // since 3.12 +{ + PepCompactUnicodeObject _base; + union { + void *any; + Py_UCS1 *latin1; + Py_UCS2 *ucs2; + Py_UCS4 *ucs4; + } data; /* Canonical, smallest-form Unicode buffer */ +}; + +struct PepUnicodeObject_311 +{ + PepCompactUnicodeObject_311 _base; + union { + void *any; + Py_UCS1 *latin1; + Py_UCS2 *ucs2; + Py_UCS4 *ucs4; + } data; /* Canonical, smallest-form Unicode buffer */ +}; + +int _PepUnicode_KIND(PyObject *str) +{ + return reinterpret_cast<PepASCIIObject *>(str)->state.kind; +} + +int _PepUnicode_IS_ASCII(PyObject *str) +{ + auto *asciiObj = reinterpret_cast<PepASCIIObject *>(str); + return asciiObj->state.ascii; +} + +int _PepUnicode_IS_COMPACT(PyObject *str) +{ + auto *asciiObj = reinterpret_cast<PepASCIIObject *>(str); + return asciiObj->state.compact; +} + +static void *_PepUnicode_ASCII_DATA(PyObject *str) +{ + if (_PepRuntimeVersion() < 0x030C00) { + auto *asciiObj_311 = reinterpret_cast<PepASCIIObject_311 *>(str); + return asciiObj_311 + 1; + } + auto *asciiObj = reinterpret_cast<PepASCIIObject *>(str); + return asciiObj + 1; +} + +static void *_PepUnicode_COMPACT_DATA(PyObject *str) +{ + if (_PepUnicode_IS_ASCII(str) != 0) + return _PepUnicode_ASCII_DATA(str); + if (_PepRuntimeVersion() < 0x030C00) { + auto *compactObj_311 = reinterpret_cast<PepCompactUnicodeObject_311 *>(str); + return compactObj_311 + 1; + } + auto *compactObj = reinterpret_cast<PepCompactUnicodeObject *>(str); + return compactObj + 1; +} + +static void *_PepUnicode_NONCOMPACT_DATA(PyObject *str) +{ + return _PepRuntimeVersion() < 0x030C00 + ? reinterpret_cast<PepUnicodeObject_311 *>(str)->data.any + : reinterpret_cast<PepUnicodeObject *>(str)->data.any; +} + +void *_PepUnicode_DATA(PyObject *str) +{ + return _PepUnicode_IS_COMPACT(str) + ? _PepUnicode_COMPACT_DATA(str) : _PepUnicode_NONCOMPACT_DATA(str); +} + +// Fast path accessing UTF8 data without doing a conversion similar +// to _PyUnicode_AsUTF8String +static const char *utf8FastPath_311(PyObject *str) +{ + if (PyUnicode_GetLength(str) == 0) + return ""; + auto *asciiObj = reinterpret_cast<PepASCIIObject_311 *>(str); + if (asciiObj->state.kind != PepUnicode_1BYTE_KIND || asciiObj->state.compact == 0) + return nullptr; // Empirical: PyCompactUnicodeObject.utf8 is only valid for 1 byte + if (asciiObj->state.ascii) { + auto *data = asciiObj + 1; + return reinterpret_cast<const char *>(data); + } + auto *compactObj = reinterpret_cast<PepCompactUnicodeObject_311 *>(str); + if (compactObj->utf8_length) + return compactObj->utf8; + return nullptr; +} + +static const char *utf8FastPath(PyObject *str) +{ + if (PyUnicode_GetLength(str) == 0) + return ""; + auto *asciiObj = reinterpret_cast<PepASCIIObject *>(str); + if (asciiObj->state.kind != PepUnicode_1BYTE_KIND || asciiObj->state.compact == 0) + return nullptr; // Empirical: PyCompactUnicodeObject.utf8 is only valid for 1 byte + if (asciiObj->state.ascii) { + auto *data = asciiObj + 1; + return reinterpret_cast<const char *>(data); + } + auto *compactObj = reinterpret_cast<PepCompactUnicodeObject *>(str); + if (compactObj->utf8_length) + return compactObj->utf8; + return nullptr; +} + +const char *_PepUnicode_AsString(PyObject *str) { /* - * We need to keep the string alive but cannot borrow the Python object. - * Ugly easy way out: We re-code as an interned bytes string. This - * produces a pseudo-leak as long as there are new strings. - * Typically, this function is used for name strings, and the dict size - * will not grow so much. + * This function is the surrogate for PyUnicode_AsUTF8, which keeps the data + * in the unicode object as long as that object exists. + * + * The function does too much if not optimized by utf8, because it keeps the + * string alive, unconditionally. + * We should not rely on this behavior and think of PyUnicode_AsUTF8, only. */ #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) #define AT __FILE__ ":" TOSTRING(__LINE__) + if (const auto *utf8 = _PepRuntimeVersion() < 0x030C00 + ? utf8FastPath_311(str) : utf8FastPath(str)) { + return utf8; + } + static PyObject *cstring_dict = nullptr; if (cstring_dict == nullptr) { cstring_dict = PyDict_New(); @@ -320,38 +437,6 @@ _PepUnicode_AsString(PyObject *str) /***************************************************************************** * - * Support for longobject.h - * - */ -#ifdef Py_LIMITED_API - -/* - * This is the original Python function _PyLong_AsInt() from longobject.c . - * We define it here because we are not allowed to use the function - * from Python with an underscore. - */ - -/* Get a C int from an int object or any object that has an __int__ - method. Return -1 and set an error if overflow occurs. */ - -int -_PepLong_AsInt(PyObject *obj) -{ - int overflow; - long result = PyLong_AsLongAndOverflow(obj, &overflow); - if (overflow || result > INT_MAX || result < INT_MIN) { - /* XXX: could be cute and give a different - message for overflow == -1 */ - PyErr_SetString(PyExc_OverflowError, - "Python int too large to convert to C int"); - return -1; - } - return int(result); -} -#endif // Py_LIMITED_API - -/***************************************************************************** - * * Support for pydebug.h * */ @@ -419,8 +504,24 @@ PepCode_Get(PepCodeObject *co, const char *name) } return ret; } + +int PepCode_Check(PyObject *o) +{ + return o != nullptr && std::strcmp(Py_TYPE(o)->tp_name, "code") == 0 ? 1 : 0; +} + #endif // Py_LIMITED_API +#if defined(Py_LIMITED_API) || defined(PYPY_VERSION) +PyObject *PepFunction_GetDefaults(PyObject *function) +{ + auto *ob_ret = PyObject_GetAttrString(function, "__defaults__"); + Py_XDECREF(ob_ret); // returns borrowed ref + return ob_ret != Py_None ? ob_ret : nullptr; +} + +#endif // defined(Py_LIMITED_API) || defined(PYPY_VERSION) + /***************************************************************************** * * Support for datetime.h @@ -648,6 +749,29 @@ PyStaticMethod_New(PyObject *callable) } #endif // Py_LIMITED_API +#ifdef PYPY_VERSION +PyTypeObject *PepBuiltinMethod_TypePtr = nullptr; + +static PyTypeObject * +getBuiltinMethodType(void) +{ + // PYSIDE-535: PyPy has a special builtin method that acts almost like PyCFunction. + // + // There is no public declaration for the "builtin method" type. + // We also cannot grep it with a Python script since the import is too early. + // Pick a demo "builtin method" by using the VoidPtr type. + // Create the equivalent of + // "from shiboken6.Shiboken import VoidPtr\n" + // "result = type(VoidPtr(0).toBytes)\n"; + auto *pyVoidP = reinterpret_cast<PyObject *>(SbkVoidPtr_TypeF()); + Shiboken::AutoDecRef arg(Py_BuildValue("i", 0)); + Shiboken::AutoDecRef inst(PyObject_CallFunctionObjArgs(pyVoidP, arg.object(), nullptr)); + Shiboken::AutoDecRef meth(PyObject_GetAttrString(inst, "toBytes")); + auto *result = reinterpret_cast<PyTypeObject *>(PyObject_Type(meth)); + return result; +} +#endif + /***************************************************************************** * * Common newly needed functions @@ -667,12 +791,37 @@ PepType_GetNameStr(PyTypeObject *type) return ret; } +// PYSIDE-2264: Find the _functools or functools module and retrieve the +// partial function. This can be tampered with, check carefully. +PyObject * +Pep_GetPartialFunction(void) +{ + static bool initialized = false; + static PyObject *result{}; + if (initialized) { + Py_INCREF(result); + return result; + } + auto *functools = PyImport_ImportModule("_functools"); + if (!functools) { + PyErr_Clear(); + functools = PyImport_ImportModule("functools"); + } + if (!functools) + Py_FatalError("functools cannot be found"); + result = PyObject_GetAttrString(functools, "partial"); + if (!result || !PyCallable_Check(result)) + Py_FatalError("partial not found or not a function"); + initialized = true; + return result; +} + /***************************************************************************** * * Newly introduced convenience functions * */ -#if PY_VERSION_HEX < 0x03070000 || defined(Py_LIMITED_API) +#ifdef Py_LIMITED_API PyObject * PyImport_GetModule(PyObject *name) @@ -698,7 +847,7 @@ PyImport_GetModule(PyObject *name) return m; } -#endif // PY_VERSION_HEX < 0x03070000 || defined(Py_LIMITED_API) +#endif // Py_LIMITED_API // 2020-06-16: For simplicity of creating arbitrary things, this function // is now made public. @@ -723,6 +872,12 @@ PepRun_GetResult(const char *command) return res; } +PyTypeObject *PepType_Type_tp_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) +{ + auto ret = PyType_Type.tp_new(metatype, args, kwds); + return reinterpret_cast<PyTypeObject *>(ret); +} + /***************************************************************************** * * Extra support for name mangling @@ -758,14 +913,14 @@ _Pep_PrivateMangle(PyObject *self, PyObject *name) } Shiboken::AutoDecRef privateobj(PyObject_GetAttr( reinterpret_cast<PyObject *>(Py_TYPE(self)), Shiboken::PyMagicName::name())); -#ifndef Py_LIMITED_API - return _Py_Mangle(privateobj, name); -#else - // For some reason, _Py_Mangle is not in the Limited API. Why? - size_t plen = PyUnicode_GET_LENGTH(privateobj); + + // PYSIDE-1436: _Py_Mangle is no longer exposed; implement it always. + // The rest of this function is our own implementation of _Py_Mangle. + // Please compare the original function in compile.c . + size_t plen = PyUnicode_GET_LENGTH(privateobj.object()); /* Strip leading underscores from class name */ size_t ipriv = 0; - while (PyUnicode_READ_CHAR(privateobj, ipriv) == '_') + while (PyUnicode_READ_CHAR(privateobj.object(), ipriv) == '_') ipriv++; if (ipriv == plen) { Py_INCREF(name); @@ -783,18 +938,17 @@ _Pep_PrivateMangle(PyObject *self, PyObject *name) wchar_t bigbuf[big_stack]; wchar_t *resbuf = amount <= big_stack ? bigbuf : (wchar_t *)malloc(sizeof(wchar_t) * amount); if (!resbuf) - return 0; + return nullptr; /* ident = "_" + priv[ipriv:] + ident # i.e. 1+plen+nlen bytes */ resbuf[0] = '_'; if (PyUnicode_AsWideChar(privateobj, resbuf + 1, ipriv + plen) < 0) - return 0; + return nullptr; if (PyUnicode_AsWideChar(name, resbuf + ipriv + plen + 1, nlen) < 0) - return 0; + return nullptr; PyObject *result = PyUnicode_FromWideChar(resbuf + ipriv, 1 + plen + nlen); if (amount > big_stack) free(resbuf); return result; -#endif // else Py_LIMITED_API } /***************************************************************************** @@ -816,6 +970,21 @@ init_PepRuntime() PepRuntime_38_flag = 1; } +static long _GetPepRuntimeVersion() +{ + auto *version = PySys_GetObject("version_info"); + const auto major = PyLong_AsLong(PyTuple_GetItem(version, 0)); + const auto minor = PyLong_AsLong(PyTuple_GetItem(version, 1)); + const auto micro = PyLong_AsLong(PyTuple_GetItem(version, 2)); + return major << 16 | minor << 8 | micro; +} + +long _PepRuntimeVersion() +{ + static const auto number = _GetPepRuntimeVersion(); + return number; +} + /***************************************************************************** * * PYSIDE-535: Support for PyPy @@ -825,33 +994,104 @@ init_PepRuntime() * */ +/////////////////////////////////////////////////////////////////////// +// +// PEP 697: Support for embedded type structures. +// +// According to `https://docs.python.org/3/c-api/object.html?highlight=pyobject_gettypedata#c.PyObject_GetTypeData` +// the function `PyObject_GetTypeData` should belong to the Stable API +// since version 3.12.0, but it does not. We use instead some copies +// from Python source code. + +#if !defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x030C0000 + +# define PepObject_GetTypeData PyObject_GetTypeData + +SbkObjectTypePrivate *PepType_SOTP(PyTypeObject *type) +{ + assert(SbkObjectType_Check(type)); + auto *obType = reinterpret_cast<PyObject *>(type); + void *data = PyObject_GetTypeData(obType, Py_TYPE(obType)); + return reinterpret_cast<SbkObjectTypePrivate *>(data); +} + +void PepType_SOTP_delete(PyTypeObject * /*type*/) +{ +} + +#else + +// The following comments are directly copied from Python 3.12 +// + +// Make sure we have maximum alignment, even if the current compiler +// does not support max_align_t. Note that: +// - Autoconf reports alignment of unknown types to 0. +// - 'long double' has maximum alignment on *most* platforms, +// looks like the best we can do for pre-C11 compilers. +// - The value is tested, see test_alignof_max_align_t +# if !defined(ALIGNOF_MAX_ALIGN_T) || ALIGNOF_MAX_ALIGN_T == 0 +# undef ALIGNOF_MAX_ALIGN_T +# define ALIGNOF_MAX_ALIGN_T alignof(long double) +# endif + +/* Align up to the nearest multiple of alignof(max_align_t) + * (like _Py_ALIGN_UP, but for a size rather than pointer) + */ +static Py_ssize_t _align_up(Py_ssize_t size) +{ + return (size + ALIGNOF_MAX_ALIGN_T - 1) & ~(ALIGNOF_MAX_ALIGN_T - 1); +} + +static void *PepObject_GetTypeData(PyObject *obj, PyTypeObject *cls) +{ + assert(PyObject_TypeCheck(obj, cls)); + return reinterpret_cast<char *>(obj) + _align_up(cls->tp_base->tp_basicsize); +} +// +/////////////////////////////////////////////////////////////////////// + /* - * SbkObjectType extender + * PyTypeObject extender */ -static std::unordered_map<SbkObjectType *, SbkObjectTypePrivate > SOTP_extender{}; -static thread_local SbkObjectType *SOTP_key{}; + +static std::unordered_map<PyTypeObject *, SbkObjectTypePrivate > SOTP_extender{}; +static thread_local PyTypeObject *SOTP_key{}; static thread_local SbkObjectTypePrivate *SOTP_value{}; -SbkObjectTypePrivate *PepType_SOTP(SbkObjectType *sbkType) +SbkObjectTypePrivate *PepType_SOTP(PyTypeObject *type) { - if (sbkType == SOTP_key) + static bool use_312 = _PepRuntimeVersion() >= 0x030C00; + assert(SbkObjectType_Check(type)); + if (use_312) { + auto *obType = reinterpret_cast<PyObject *>(type); + void *data = PepObject_GetTypeData(obType, Py_TYPE(obType)); + return reinterpret_cast<SbkObjectTypePrivate *>(data); + } + if (type == SOTP_key) return SOTP_value; - auto it = SOTP_extender.find(sbkType); + auto it = SOTP_extender.find(type); if (it == SOTP_extender.end()) { - it = SOTP_extender.insert({sbkType, {}}).first; + it = SOTP_extender.insert({type, {}}).first; memset(&it->second, 0, sizeof(SbkObjectTypePrivate)); } - SOTP_key = sbkType; + SOTP_key = type; SOTP_value = &it->second; return SOTP_value; } -void PepType_SOTP_delete(SbkObjectType *sbkType) +void PepType_SOTP_delete(PyTypeObject *type) { - SOTP_extender.erase(sbkType); + static bool use_312 = _PepRuntimeVersion() >= 0x030C00; + assert(SbkObjectType_Check(type)); + if (use_312) + return; + SOTP_extender.erase(type); SOTP_key = nullptr; } +#endif // !defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x030C0000 + /* * SbkEnumType extender */ @@ -861,6 +1101,7 @@ static thread_local SbkEnumTypePrivate *SETP_value{}; SbkEnumTypePrivate *PepType_SETP(SbkEnumType *enumType) { + // PYSIDE-2230: This makes no sense at all for Enum types. if (enumType == SETP_key) return SETP_value; auto it = SETP_extender.find(enumType); @@ -879,31 +1120,76 @@ void PepType_SETP_delete(SbkEnumType *enumType) SETP_key = nullptr; } -/* - * PySideQFlagsType extender - */ -static std::unordered_map<PySideQFlagsType *, PySideQFlagsTypePrivate> PFTP_extender{}; -static thread_local PySideQFlagsType *PFTP_key{}; -static thread_local PySideQFlagsTypePrivate *PFTP_value{}; - -PySideQFlagsTypePrivate *PepType_PFTP(PySideQFlagsType *flagsType) +#ifdef Py_LIMITED_API +static PyObject *emulatePyType_GetDict(PyTypeObject *type) { - if (flagsType == PFTP_key) - return PFTP_value; - auto it = PFTP_extender.find(flagsType); - if (it == PFTP_extender.end()) { - it = PFTP_extender.insert({flagsType, {}}).first; - memset(&it->second, 0, sizeof(PySideQFlagsTypePrivate)); + if (_PepRuntimeVersion() < 0x030C00 || type->tp_dict) { + auto *res = type->tp_dict; + Py_XINCREF(res); + return res; } - PFTP_key = flagsType; - PFTP_value = &it->second; - return PFTP_value; + // PYSIDE-2230: Here we are really cheating. We don't know how to + // access an internal dict, and so we simply pretend + // it were an empty dict. This works great for our types. + // This was an unexpectedly simple solution :D + return PyDict_New(); +} +#endif + +// PyType_GetDict: replacement for <static type>.tp_dict, which is +// zero for builtin types since 3.12. +PyObject *PepType_GetDict(PyTypeObject *type) +{ +#if !defined(Py_LIMITED_API) +# if PY_VERSION_HEX >= 0x030C0000 + return PyType_GetDict(type); +# else + // pre 3.12 fallback code, mimicking the addref-behavior. + Py_XINCREF(type->tp_dict); + return type->tp_dict; +# endif +#else + return emulatePyType_GetDict(type); +#endif // Py_LIMITED_API } -void PepType_PFTP_delete(PySideQFlagsType *flagsType) +int PepType_SetDict(PyTypeObject *type, PyObject *dict) { - PFTP_extender.erase(flagsType); - PFTP_key = nullptr; + type->tp_dict = dict; + return 0; +} + +// Pre 3.10, PyType_GetSlot() would only work for heap types. +// FIXME: PyType_GetSlot() can be used unconditionally when the +// minimum limited API version is >= 3.10. +void *PepType_GetSlot(PyTypeObject *type, int aSlot) +{ + static const bool is310 = _PepRuntimeVersion() >= 0x030A00; + if (is310 || (type->tp_flags & Py_TPFLAGS_HEAPTYPE) != 0) + return PyType_GetSlot(type, aSlot); + + switch (aSlot) { + case Py_tp_alloc: + return reinterpret_cast<void *>(type->tp_alloc); + case Py_tp_getattro: + return reinterpret_cast<void *>(type->tp_getattro); + case Py_tp_setattro: + return reinterpret_cast<void *>(type->tp_setattro); + case Py_tp_descr_get: + return reinterpret_cast<void *>(type->tp_descr_get); + case Py_tp_descr_set: + return reinterpret_cast<void *>(type->tp_descr_set); + case Py_tp_call: + return reinterpret_cast<void *>(type->tp_call); + case Py_tp_new: + return reinterpret_cast<void *>(type->tp_new); + case Py_tp_init: + return reinterpret_cast<void *>(type->tp_init); + case Py_tp_free: + return reinterpret_cast<void *>(type->tp_free); + } + assert(false); + return nullptr; } /*************************************************************************** @@ -937,16 +1223,16 @@ static inline void *PepType_ExTP(PyTypeObject *type, size_t size) static PyTypeObject *alias{}; const char *kind = size == sizeof(SbkObjectTypePrivate) ? "SOTP" : size == sizeof(SbkEnumTypePrivate) ? "SETP" : - size == sizeof(PySideQFlagsTypePrivate) ? "PFTP" : + size == sizeof(SbkQFlagsTypePrivate) ? "PFTP" : "unk."; fprintf(stderr, "%s:%d %p x %s s=%ld\n", __func__, __LINE__, type, kind, size); PyObject *kill{}; if (strlen(env_p) > 0) { - if (size == sizeof(PySideQFlagsTypePrivate)) { + if (size == sizeof(SbkQFlagsTypePrivate)) { if (alias == nullptr) alias = type; } - if (size != sizeof(PySideQFlagsTypePrivate)) { + if (size != sizeof(SbkQFlagsTypePrivate)) { if (type == alias) Py_INCREF(kill); } @@ -983,6 +1269,9 @@ Pep384_Init() PepFunction_TypePtr = getFunctionType(); PepStaticMethod_TypePtr = getStaticMethodType(); #endif // Py_LIMITED_API +#ifdef PYPY_VERSION + PepBuiltinMethod_TypePtr = getBuiltinMethodType(); +#endif } } // extern "C" diff --git a/sources/shiboken6/libshiboken/pep384impl.h b/sources/shiboken6/libshiboken/pep384impl.h index 6d297bc7d..ec58aac81 100644 --- a/sources/shiboken6/libshiboken/pep384impl.h +++ b/sources/shiboken6/libshiboken/pep384impl.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef PEP384IMPL_H #define PEP384IMPL_H @@ -86,42 +50,79 @@ typedef struct _typeobject { const char *tp_name; Py_ssize_t tp_basicsize; void *X03; // Py_ssize_t tp_itemsize; +#ifdef PEP384_INTERN destructor tp_dealloc; +#else + destructor X04; +#endif void *X05; // Py_ssize_t tp_vectorcall_offset; void *X06; // getattrfunc tp_getattr; void *X07; // setattrfunc tp_setattr; void *X08; // PyAsyncMethods *tp_as_async; +#ifdef PEP384_INTERN reprfunc tp_repr; +#else + reprfunc X09; +#endif void *X10; // PyNumberMethods *tp_as_number; void *X11; // PySequenceMethods *tp_as_sequence; void *X12; // PyMappingMethods *tp_as_mapping; void *X13; // hashfunc tp_hash; +#ifdef PEP384_INTERN ternaryfunc tp_call; - reprfunc tp_str; +#else + ternaryfunc X14; +#endif + reprfunc tp_str; // Only used for PEP384_INTERN and a shiboken test getattrofunc tp_getattro; setattrofunc tp_setattro; void *X18; // PyBufferProcs *tp_as_buffer; unsigned long tp_flags; void *X20; // const char *tp_doc; +#ifdef PEP384_INTERN traverseproc tp_traverse; inquiry tp_clear; +#else + traverseproc X21; + inquiry X22; +#endif void *X23; // richcmpfunc tp_richcompare; Py_ssize_t tp_weaklistoffset; void *X25; // getiterfunc tp_iter; +#ifdef PEP384_INTERN iternextfunc tp_iternext; +#else + iternextfunc X26; +#endif struct PyMethodDef *tp_methods; struct PyMemberDef *tp_members; struct PyGetSetDef *tp_getset; struct _typeobject *tp_base; +#ifdef PEP384_INTERN PyObject *tp_dict; descrgetfunc tp_descr_get; descrsetfunc tp_descr_set; +#else + void *X31; + descrgetfunc X32; + descrsetfunc X33; +#endif Py_ssize_t tp_dictoffset; +#ifdef PEP384_INTERN initproc tp_init; allocfunc tp_alloc; +#else + initproc X39; + allocfunc X40; +#endif newfunc tp_new; +#ifdef PEP384_INTERN freefunc tp_free; inquiry tp_is_gc; /* For PyObject_IS_GC */ +#else + freefunc X41; + inquiry X42; /* For PyObject_IS_GC */ +#endif PyObject *tp_bases; PyObject *tp_mro; /* method resolution order */ @@ -134,14 +135,6 @@ typedef struct _typeobject { && (Py_TYPE(o)->tp_is_gc == NULL || Py_TYPE(o)->tp_is_gc(o))) #endif -// This was a macro error in the limited API from the beginning. -// It was fixed in Python master, but did make it only in Python 3.8 . -#define PY_ISSUE33738_SOLVED 0x03080000 -#if PY_VERSION_HEX < PY_ISSUE33738_SOLVED -#undef PyIndex_Check -LIBSHIBOKEN_API int PyIndex_Check(PyObject *obj); -#endif - LIBSHIBOKEN_API PyObject *_PepType_Lookup(PyTypeObject *type, PyObject *name); #else // Py_LIMITED_API @@ -150,17 +143,18 @@ LIBSHIBOKEN_API PyObject *_PepType_Lookup(PyTypeObject *type, PyObject *name); #endif // Py_LIMITED_API +/// PYSIDE-939: We need the runtime version, given major << 16 + minor << 8 + micro +LIBSHIBOKEN_API long _PepRuntimeVersion(); /***************************************************************************** * * PYSIDE-535: Implement a clean type extension for PyPy * */ -struct SbkObjectType; struct SbkObjectTypePrivate; -LIBSHIBOKEN_API SbkObjectTypePrivate *PepType_SOTP(SbkObjectType *type); -LIBSHIBOKEN_API void PepType_SOTP_delete(SbkObjectType *type); +LIBSHIBOKEN_API SbkObjectTypePrivate *PepType_SOTP(PyTypeObject *type); +LIBSHIBOKEN_API void PepType_SOTP_delete(PyTypeObject *type); struct SbkEnumType; struct SbkEnumTypePrivate; @@ -169,26 +163,14 @@ LIBSHIBOKEN_API SbkEnumTypePrivate *PepType_SETP(SbkEnumType *type); LIBSHIBOKEN_API void PepType_SETP_delete(SbkEnumType *enumType); struct PySideQFlagsType; -struct PySideQFlagsTypePrivate; - -LIBSHIBOKEN_API PySideQFlagsTypePrivate *PepType_PFTP(PySideQFlagsType *type); -LIBSHIBOKEN_API void PepType_PFTP_delete(PySideQFlagsType *flagsType); +struct SbkQFlagsTypePrivate; /*****************************************************************************/ // functions used everywhere LIBSHIBOKEN_API const char *PepType_GetNameStr(PyTypeObject *type); -/***************************************************************************** - * - * RESOLVED: longobject.h - * - */ -#ifdef Py_LIMITED_API -LIBSHIBOKEN_API int _PepLong_AsInt(PyObject *); -#else -#define _PepLong_AsInt _PyLong_AsInt -#endif +LIBSHIBOKEN_API PyObject *Pep_GetPartialFunction(void); /***************************************************************************** * @@ -204,7 +186,6 @@ LIBSHIBOKEN_API int _PepLong_AsInt(PyObject *); */ LIBSHIBOKEN_API int Pep_GetFlag(const char *name); LIBSHIBOKEN_API int Pep_GetVerboseFlag(void); -#define Py_VerboseFlag Pep_GetVerboseFlag() #endif /***************************************************************************** @@ -234,12 +215,44 @@ LIBSHIBOKEN_API int Pep_GetVerboseFlag(void); // PyUnicode_GetSize is deprecated in favor of PyUnicode_GetLength. #define PepUnicode_GetLength(op) PyUnicode_GetLength((PyObject *)(op)) +// Unfortunately, we cannot ask this at runtime +// #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000 +// FIXME: Python 3.10: Replace _PepUnicode_AsString by PyUnicode_AsUTF8 #ifdef Py_LIMITED_API -LIBSHIBOKEN_API char *_PepUnicode_AsString(PyObject *); +LIBSHIBOKEN_API const char *_PepUnicode_AsString(PyObject *); + +enum PepUnicode_Kind { +#if PY_VERSION_HEX < 0x030C0000 + PepUnicode_WCHAR_KIND = 0, +#endif + PepUnicode_1BYTE_KIND = 1, + PepUnicode_2BYTE_KIND = 2, + PepUnicode_4BYTE_KIND = 4 +}; + +LIBSHIBOKEN_API int _PepUnicode_KIND(PyObject *); +LIBSHIBOKEN_API int _PepUnicode_IS_ASCII(PyObject *str); +LIBSHIBOKEN_API int _PepUnicode_IS_COMPACT(PyObject *str); + +LIBSHIBOKEN_API void *_PepUnicode_DATA(PyObject *str); #else + +enum PepUnicode_Kind { +#if PY_VERSION_HEX < 0x030C0000 + PepUnicode_WCHAR_KIND = PyUnicode_WCHAR_KIND, +#endif + PepUnicode_1BYTE_KIND = PyUnicode_1BYTE_KIND, + PepUnicode_2BYTE_KIND = PyUnicode_2BYTE_KIND, + PepUnicode_4BYTE_KIND = PyUnicode_4BYTE_KIND +}; + #define _PepUnicode_AsString PyUnicode_AsUTF8 +#define _PepUnicode_KIND PyUnicode_KIND +#define _PepUnicode_DATA PyUnicode_DATA +#define _PepUnicode_IS_COMPACT PyUnicode_IS_COMPACT +#define _PepUnicode_IS_ASCII PyUnicode_IS_ASCII #endif /***************************************************************************** @@ -291,7 +304,7 @@ LIBSHIBOKEN_API char *_PepUnicode_AsString(PyObject *); #ifdef Py_LIMITED_API -typedef struct _pycfunc PyCFunctionObject; +using PyCFunctionObject = struct _pycfunc; #define PyCFunction_GET_FUNCTION(func) PyCFunction_GetFunction((PyObject *)func) #define PyCFunction_GET_SELF(func) PyCFunction_GetSelf((PyObject *)func) #define PyCFunction_GET_FLAGS(func) PyCFunction_GetFlags((PyObject *)func) @@ -321,11 +334,8 @@ LIBSHIBOKEN_API PyObject *PyRun_String(const char *, int, PyObject *, PyObject * // buffer functions. // But this is no problem as we check it's validity for every version. -#define PYTHON_BUFFER_VERSION_COMPATIBLE (PY_VERSION_HEX >= 0x03030000 && \ - PY_VERSION_HEX < 0x0309FFFF) -#if !PYTHON_BUFFER_VERSION_COMPATIBLE -# error Please check the buffer compatibility for this python version! -#endif +// PYSIDE-1960 The buffer interface is since Python 3.11 part of the stable +// API and we do not need to check the compatibility by hand anymore. typedef struct { getbufferproc bf_getbuffer; @@ -409,10 +419,13 @@ LIBSHIBOKEN_API PyObject *PyMethod_Self(PyObject *); typedef struct _code PepCodeObject; LIBSHIBOKEN_API int PepCode_Get(PepCodeObject *co, const char *name); +LIBSHIBOKEN_API int PepCode_Check(PyObject *o); # define PepCode_GET_FLAGS(o) PepCode_Get(o, "co_flags") # define PepCode_GET_ARGCOUNT(o) PepCode_Get(o, "co_argcount") +LIBSHIBOKEN_API PyObject *PepFunction_GetDefaults(PyObject *function); + /* Masks for co_flags above */ # define CO_OPTIMIZED 0x0001 # define CO_NEWLOCALS 0x0002 @@ -426,7 +439,15 @@ LIBSHIBOKEN_API int PepCode_Get(PepCodeObject *co, const char *name); # define PepCodeObject PyCodeObject # define PepCode_GET_FLAGS(o) ((o)->co_flags) # define PepCode_GET_ARGCOUNT(o) ((o)->co_argcount) +# define PepCode_Check PyCode_Check +# ifdef PYPY_VERSION + +LIBSHIBOKEN_API PyObject *PepFunction_GetDefaults(PyObject *function); + +# else +# define PepFunction_GetDefaults PyFunction_GetDefaults +# endif #endif /***************************************************************************** @@ -514,6 +535,10 @@ LIBSHIBOKEN_API PyObject *PyStaticMethod_New(PyObject *callable); #define PepStaticMethod_TypePtr &PyStaticMethod_Type #endif +#ifdef PYPY_VERSION +extern LIBSHIBOKEN_API PyTypeObject *PepBuiltinMethod_TypePtr; +#endif + // Although not PEP specific, we resolve this similar issue, here: #define PepMethodDescr_TypePtr &PyMethodDescr_Type @@ -523,13 +548,18 @@ LIBSHIBOKEN_API PyObject *PyStaticMethod_New(PyObject *callable); * * This is not defined if Py_LIMITED_API is defined. */ -#if PY_VERSION_HEX < 0x03070000 || defined(Py_LIMITED_API) +#ifdef Py_LIMITED_API LIBSHIBOKEN_API PyObject *PyImport_GetModule(PyObject *name); -#endif // PY_VERSION_HEX < 0x03070000 || defined(Py_LIMITED_API) +#endif // Py_LIMITED_API // Evaluate a script and return the variable `result` LIBSHIBOKEN_API PyObject *PepRun_GetResult(const char *command); +// Call PyType_Type.tp_new returning a PyType object. +LIBSHIBOKEN_API PyTypeObject *PepType_Type_tp_new(PyTypeObject *metatype, + PyObject *args, + PyObject *kwds); + /***************************************************************************** * * Runtime support for Python 3.8 incompatibilities @@ -545,6 +575,20 @@ extern LIBSHIBOKEN_API int PepRuntime_38_flag; /***************************************************************************** * + * Runtime support for Python 3.12 incompatibility + * + */ + +LIBSHIBOKEN_API PyObject *PepType_GetDict(PyTypeObject *type); + +// This function does not exist as PyType_SetDict. But because tp_dict +// is no longer considered to be accessible, we treat it as such. +LIBSHIBOKEN_API int PepType_SetDict(PyTypeObject *type, PyObject *dict); + +LIBSHIBOKEN_API void *PepType_GetSlot(PyTypeObject *type, int aSlot); + +/***************************************************************************** + * * Module Initialization * */ diff --git a/sources/shiboken6/libshiboken/pep384impl_doc.rst b/sources/shiboken6/libshiboken/pep384impl_doc.rst deleted file mode 100644 index f8e29d321..000000000 --- a/sources/shiboken6/libshiboken/pep384impl_doc.rst +++ /dev/null @@ -1,720 +0,0 @@ -**************************************** -The Transition To The Limited Python API -**************************************** - - -Foreword -======== - -Python supports a limited API that restricts access to certain structures. -Besides eliminating whole modules and all functions and macros which names -start with an -underscore, the most drastic restriction is the removal of normal type object -declarations. - -For details about the eliminated modules and functions, please see the -`PEP 384`_ page for reference. - - -.. _`PEP 384`: https://www.python.org/dev/peps/pep-0384/ - - - -Changed Modules -=============== - -All changed module's include files are listed with the changed functions here. -As a general rule, it was tried to keep the changes to a minimum diff. -Macros which are not available were changed to functions with the same name -if possible. Completely removed names ``Py{name}`` were re-implemented as ``Pep{name}``. - - -memoryobject.h --------------- - -The buffer protocol was completely removed. We redefined all the structures -and methods, because PySide uses that. This is an exception to the limited API -that we have to check ourselves. The code is extracted in bufferprocs_py37.h . -This is related to the following: - - -abstract.h ----------- - -This belongs to the buffer protocol like memoryobject.h . -As replacement for ``Py_buffer`` we defined ``Pep_buffer`` and several other -internal macros. - -The version is checked by hand, and the version number must be updated only -if the implementation does not change. Otherwise, we need to write version -dependent code paths. - -It is questionable if it is worthwhile to continue using the buffer protocol -or if we should try to get rid of ``Pep_buffer``, completely. - - -longobject.h ------------- - -``_PyLong_AsInt`` is not available. We defined a ``_PepLong_AsInt`` function, instead. -Maybe this should be replaced by ``PyLong_AsLong``. - - -pydebug.h ---------- - -We have no direct access to ``Py_VerboseFlag`` because debugging is not -supported. We redefined it as macro ``Py_VerboseFlag`` which calls ``Pep_VerboseFlag``. - - -unicodeobject.h ---------------- - -The macro ``PyUnicode_GET_SIZE`` was removed and replaced by ``PepUnicode_GetLength`` -which evaluates to ``PyUnicode_GetSize`` for Python 2 and ``PyUnicode_GetLength`` for Python 3. -Since Python 3.3, ``PyUnicode_GetSize`` would have the bad side effect of requiring the GIL! - -Function ``_PyUnicode_AsString`` is unavailable and was replaced by a macro -that calls ``_PepUnicode_AsString``. The implementation was a bit involved, -and it would be better to change the code and replace this function. - - -bytesobject.h -------------- - -The macros ``PyBytes_AS_STRING`` and ``PyBytes_GET_SIZE`` were redefined to call -the according functions. - - -floatobject.h -------------- - -``PyFloat_AS_DOUBLE`` now calls ``PyFloat_AsDouble``. - - -tupleobject.h -------------- - -``PyTuple_GET_ITEM``, ``PyTuple_SET_ITEM`` and ``PyTuple_GET_SIZE`` were redefined as -function calls. - - -listobject.h ------------- - -``PyList_GET_ITEM``, ``PyList_SET_ITEM`` and ``PyList_GET_SIZE`` were redefined as -function calls. - - -dictobject.h ------------- - -``PyDict_GetItem`` also exists in a ``PyDict_GetItemWithError`` version that does -not suppress errors. This suppression has the side effect of touching global -structures. This function exists in Python 2 only since Python 2.7.12 and has -a different name. We simply implemented the function. -Needed to avoid the GIL when accessing dictionaries. - - -methodobject.h --------------- - -``PyCFunction_GET_FUNCTION``, ``PyCFunction_GET_SELF`` and ``PyCFunction_GET_FLAGS`` -were redefined as function calls. - -Direct access to the methoddef structure is not available, and we defined -``PepCFunction_GET_NAMESTR`` as accessor for name strings. - - -pythonrun.h ------------ - -The simple function ``PyRun_String`` is not available. It was re-implemented -in a simplified version for the signature module. - - -funcobject.h ------------- - -The definitions of funcobject.h are completely missing, although there -are extra ``#ifdef`` conditional defines inside, too. This suggests that the exclusion -was unintended. - -We therefore redefined ``PyFunctionObject`` as an opaque type. - -The missing macro ``PyFunction_Check`` was defined, and the macro -``PyFunction_GET_CODE`` calls the according function. - -There is no equivalent for function name access, therefore we introduced -``PepFunction_GetName`` either as a function or as a macro. - -*TODO: We should fix funcobject.h* - - -classobject.h -------------- - -Classobject is also completely not imported, instead of defining an opaque type. - -We defined the missing functions ``PyMethod_New``, ``PyMethod_Function`` and -``PyMethod_Self`` and also redefined ``PyMethod_GET_SELF`` and -``PyMethod_GET_FUNCTION`` as calls to these functions. - -*TODO: We should fix classobject.h* - - -code.h ------- - -The whole code.c code is gone, although it may make sense to -define some minimum accessibility. This will be clarified on -`Python-Dev`_. We needed access to code objects and defined the missing -PepCode_GET_FLAGS and PepCode_GET_ARGCOUNT either as function or macro. -We further added the missing flags, although few are used: - -``CO_OPTIMIZED`` ``CO_NEWLOCALS`` ``CO_VARARGS`` ``CO_VARKEYWORDS`` ``CO_NESTED`` -``CO_GENERATOR`` - -*TODO: We should maybe fix code.h* - -.. _`Python-Dev`: https://mail.python.org/mailman/listinfo/python-dev - -datetime.h ----------- - -The DateTime module is explicitly not included in the limited API. -We defined all the needed functions but called them via Python instead -of direct call macros. This has a slight performance impact. - -The performance could be easily improved by providing an interface -that fetches all attributes at once, instead of going through the object -protocol every time. - -The re-defined macros and methods are:: - - PyDateTime_GET_YEAR - PyDateTime_GET_MONTH - PyDateTime_GET_DAY - PyDateTime_DATE_GET_HOUR - PyDateTime_DATE_GET_MINUTE - PyDateTime_DATE_GET_SECOND - PyDateTime_DATE_GET_MICROSECOND - PyDateTime_DATE_GET_FOLD - PyDateTime_TIME_GET_HOUR - PyDateTime_TIME_GET_MINUTE - PyDateTime_TIME_GET_SECOND - PyDateTime_TIME_GET_MICROSECOND - PyDateTime_TIME_GET_FOLD - - PyDate_Check - PyDateTime_Check - PyTime_Check - - PyDate_FromDate - PyDateTime_FromDateAndTime - PyTime_FromTime - -*XXX: We should maybe provide an optimized interface to datetime* - - -object.h --------- - -The file object.h contains the ``PyTypeObject`` structure, which is supposed -to be completely opaque. All access to types should be done through -``PyType_GetSlot`` calls. Due to bugs and deficiencies in the limited API -implementation, it was not possible to do that. Instead, we have defined -a simplified structure for ``PyTypeObject`` that has only the fields that -are used in PySide. - -We will explain later why and how this was done. Here is the reduced -structure:: - - typedef struct _typeobject { - PyVarObject ob_base; - const char *tp_name; - Py_ssize_t tp_basicsize; - void *X03; // Py_ssize_t tp_itemsize; - void *X04; // destructor tp_dealloc; - void *X05; // printfunc tp_print; - void *X06; // getattrfunc tp_getattr; - void *X07; // setattrfunc tp_setattr; - void *X08; // PyAsyncMethods *tp_as_async; - void *X09; // reprfunc tp_repr; - void *X10; // PyNumberMethods *tp_as_number; - void *X11; // PySequenceMethods *tp_as_sequence; - void *X12; // PyMappingMethods *tp_as_mapping; - void *X13; // hashfunc tp_hash; - ternaryfunc tp_call; - reprfunc tp_str; - void *X16; // getattrofunc tp_getattro; - void *X17; // setattrofunc tp_setattro; - void *X18; // PyBufferProcs *tp_as_buffer; - void *X19; // unsigned long tp_flags; - void *X20; // const char *tp_doc; - traverseproc tp_traverse; - inquiry tp_clear; - void *X23; // richcmpfunc tp_richcompare; - Py_ssize_t tp_weaklistoffset; - void *X25; // getiterfunc tp_iter; - void *X26; // iternextfunc tp_iternext; - struct PyMethodDef *tp_methods; - void *X28; // struct PyMemberDef *tp_members; - void *X29; // struct PyGetSetDef *tp_getset; - struct _typeobject *tp_base; - PyObject *tp_dict; - descrgetfunc tp_descr_get; - void *X33; // descrsetfunc tp_descr_set; - Py_ssize_t tp_dictoffset; - initproc tp_init; - allocfunc tp_alloc; - newfunc tp_new; - freefunc tp_free; - inquiry tp_is_gc; /* For PyObject_IS_GC */ - PyObject *tp_bases; - PyObject *tp_mro; /* method resolution order */ - } PyTypeObject; - -Function ``PyIndex_Check`` had to be defined in an unwanted way due to -a Python issue. See file pep384_issue33738.cpp . - -There are extension structures which have been isolated as special macros that -dynamically compute the right offsets of the extended type structures: - -* ``PepType_SOTP`` for ``SbkObjectTypePrivate`` -* ``PepType_SETP`` for ``SbkEnumTypePrivate`` -* ``PepType_PFTP`` for ``PySideQFlagsTypePrivate`` - -How these extension structures are used can best be seen by searching -``PepType_{four}`` in the source. - -Due to the new heaptype interface, the names of certain types contain -now the module name in the ``tp_name`` field. To have a compatible way -to access simple type names as C string, ``PepType_GetNameStr`` has been -written that skips over dotted name parts. - -Finally, the function ``_PyObject_Dump`` was excluded from the limited API. -This is a useful debugging aid that we always want to have available, -so it is added back, again. Anyway, we did not reimplement it, and so -Windows is not supported. -Therefore, a forgotten debugging call of this functions will break COIN. :-) - - -Using The New Type API -====================== - -After converting everything but the object.h file, we were a little -bit shocked: it suddenly was clear that we would have no more -access to type objects, and even more scary that all types which we -use have to be heap types, only! - -For PySide with its intense use of heap type extensions in various -flavors, the situation looked quite unsolvable. In the end, it was -nicely solved, but it took almost 3.5 months to get that right. - -Before we see how this is done, we will explain the differences -between the APIs and their consequences. - - -The Interface -------------- - -The old type API of Python knows static types and heap types. -Static types are written down as a declaration of a ``PyTypeObject`` -structure with all its fields filled in. Here is for example -the definition of the Python type ``object`` (Python 3.6):: - - PyTypeObject PyBaseObject_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "object", /* tp_name */ - sizeof(PyObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - object_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_reserved */ - object_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - (hashfunc)_Py_HashPointer, /* tp_hash */ - 0, /* tp_call */ - object_str, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - PyObject_GenericSetAttr, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - PyDoc_STR("object()\n--\n\nThe most base type"), /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - object_richcompare, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - object_methods, /* tp_methods */ - 0, /* tp_members */ - object_getsets, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - object_init, /* tp_init */ - PyType_GenericAlloc, /* tp_alloc */ - object_new, /* tp_new */ - PyObject_Del, /* tp_free */ - }; - -We can write the same structure in form of a ``PyType_Spec`` structure, -and there is even an incomplete tool *abitype.py* that does this conversion -for us. With a few corrections, the result looks like this:: - - static PyType_Slot PyBaseObject_Type_slots[] = { - {Py_tp_dealloc, (void *)object_dealloc}, - {Py_tp_repr, (void *)object_repr}, - {Py_tp_hash, (void *)_Py_HashPointer}, - {Py_tp_str, (void *)object_str}, - {Py_tp_getattro, (void *)PyObject_GenericGetAttr}, - {Py_tp_setattro, (void *)PyObject_GenericSetAttr}, - {Py_tp_richcompare, (void *)object_richcompare}, - {Py_tp_methods, (void *)object_methods}, - {Py_tp_getset, (void *)object_getsets}, - {Py_tp_init, (void *)object_init}, - {Py_tp_alloc, (void *)PyType_GenericAlloc}, - {Py_tp_new, (void *)object_new}, - {Py_tp_free, (void *)PyObject_Del}, - {0, 0}, - }; - static PyType_Spec PyBaseObject_Type_spec = { - "object", - sizeof(PyObject), - 0, - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - PyBaseObject_Type_slots, - }; - -This new structure is almost compatible with the old one, but there -are some subtle differences. - -* The new types are generated in one step - -This seems to be no problem, but it was very much, due to the way the -types were built in PySide. Types were assembled piece by piece, and -finally the ``PyType_Ready`` function was called. - -With the new API, ``PyType_Ready`` is called already at the end of -``PyType_FromSpec``, and that meant that the logic of type creation became -completely turned upside down. - -* The new types are always heaptypes - -With the new type creation functions, it is no longer possible to -create "normal" types. Instead, they all have to be allocated on the -heap and garbage collected. The user should normally not recognize this. -But type creation is more constrained, and you cannot create a subtype -if the ``Py_TPFLAGS_BASETYPE`` is not set. This constraint was already -violated by PySide and needed a quite profound fix. - -* The new types always need a module - -While this is not a problem per se, the above new type spec will not create -a usable new type, but complain with:: - - DeprecationWarning: builtin type object has no __module__ attribute - -But there are more problems: - -* The new types have unexpected defaults - -When fields are empty, you would usually assume that they stay empty. -There are just a few corrections that ``PyType_Ready`` will do to a type. - -But there is the following clause in ``PyType_FromSpec`` that can give you -many headaches:: - - if (type->tp_dealloc == NULL) { - /* It's a heap type, so needs the heap types' dealloc. - subtype_dealloc will call the base type's tp_dealloc, if - necessary. */ - type->tp_dealloc = subtype_dealloc; - } - -In fact, before the move to the new API, the ``PyType_Ready`` function -filled empty ``tp_dealloc`` fields with ``object_dealloc``. And the code -that has been written with that in mind now becomes pretty wrong if suddenly -``subtype_dealloc`` is used. - -The way out was to explicitly provide an ``object_dealloc`` function. -This would then again impose a problem, because ``object_dealloc`` is not -public. Writing our own version is easy, but it again needs access to -type objects. But fortunately, we have broken this rule, already... - - -* The new types are only partially allocated - -The structures used in ``PyType_FromSpec`` are almost all allocated, -only the name field is static. This is no problem for types which are -statically created once. But if you want to parameterize things and -create multiple types with a single slots and spec definition, the name -field that is used for tp_name must be allocated dynamically. -This is misleading, since all the slots already are copies. - -* The new types don't support special offsets - -The special fields ``tp_weaklistoffset`` and ``tp_dictoffset`` are not supported -by ``PyType_FromSpec``. Unfortunately the documentation does not tell you -if you are allowed to set these fields manually after creating the type or not. -We finally did it and it worked, but we are not sure about correctness. - -See basewrapper.cpp function ``SbkObject_TypeF()`` as the only reference to -these fields in PySide. This single reference is absolutely necessary and -very important, since all derived types invisibly inherit these two fields. - - -Future Versions Of The Limited API -================================== - -As we have seen, the current version of the limited API does a bit of -cheating, because it uses parts of the data structure that should be -an opaque type. At the moment, this works fine because the data is -still way more compatible as it could be. - -But what if this is changed in the future? - -We know that the data structures are stable until Python 3.8 comes out. -Until then, the small bugs and omissions will hopefully all be solved. -Then it will be possible to replace the current small tricks by calls -to ``PyType_GetSlot`` in the way things should be. - -At the very moment when the current assumptions about the data structure -are no longer true, we will rewrite the direct attribute access with -calls to ``PyType_GetSlot``. After that, no more changes will be necessary. - - -Appendix A: The Transition To Simpler Types -=========================================== - -After all code had been converted to the limited API, there was a -remaining problem with the ``PyHeapTypeObject``. - -Why a problem? Well, all the type structures in shiboken use -special extra fields at the end of the heap type object. This -currently enforces extra knowledge at compile time about how large the -heap type object is. In a clean implementation, we would only use -the ``PyTypeObject`` itself and access the fields *behind* the type -by a pointer that is computed at runtime. - - -Restricted PyTypeObject ------------------------ - -Before we are going into details, let us motivate the existence of -the restricted ``PyTypeObject``: - -Originally, we wanted to use ``PyTypeObject`` as an opaque type and -restrict ourselves to only use the access function ``PyType_GetSlot``. -This function allows access to all fields which are supported by -the limited API. - -But this is a restriction, because we get no access to ``tp_dict``, -which we need to support the signature extension. But we can work -around that. - -The real restriction is that ``PyType_GetSlot`` only works for heap -types. This makes the function quite useless, because we have -no access to ``PyType_Type``, which is the most important type ``type`` -in Python. We need that for instance to compute the size of -``PyHeapTypeObject`` dynamically. - -With much effort, it is possible to clone ``PyType_Type`` as a heap -type. But due to a bug in the Pep 384 support, we need -access to the ``nb_index`` field of a normal type. Cloning does not -help because ``PyNumberMethods`` fields are *not* inherited. - -After we realized this dead end, we changed concept and did not -use ``PyType_GetSlot`` at all (except in function ``copyNumberMethods``), -but created a restricted ``PyTypeObject`` with only those fields -defined that are needed in PySide. - -Is this breakage of the limited API? I don't think so. A special -function runs on program startup that checks the correct position -of the fields of ``PyTypeObject``, although a change in those fields is -more than unlikely. -The really crucial thing is to no longer use ``PyHeapTypeObject`` -explicitly because that *does* change its layout over time. - - -Diversification ---------------- - -There were multiple ``Sbk{something}`` structures which all used a "d" field -for their private data. This made it not easy to find the right -fields when switching between objects and types:: - - struct LIBSHIBOKEN_API SbkObject - { - PyObject_HEAD - PyObject *ob_dict; - PyObject *weakreflist; - SbkObjectPrivate *d; - }; - - struct LIBSHIBOKEN_API SbkObjectType - { - PyHeapTypeObject super; - SbkObjectTypePrivate *d; - }; - -The first step was to rename the SbkObjectTypePrivate part from "d" to -"sotp". It was chosen to be short but easy to remember as abbreviation -of "SbkObjectTypePrivate", leading to:: - - struct LIBSHIBOKEN_API SbkObjectType - { - PyHeapTypeObject super; - SbkObjectTypePrivate *sotp; - }; - -After renaming, it was easier to do the following transformations. - - -Abstraction ------------ - -After renaming the type extension pointers to ``sotp``, I replaced -them by function-like macros which did the special access *behind* -the types, instead of those explicit fields. For instance, the -expression:: - - type->sotp->converter - -became:: - - PepType_SOTP(type)->converter - -The macro expansion can be seen here:: - - #define PepHeapType_SIZE \ - (reinterpret_cast<PyTypeObject *>(&PyType_Type)->tp_basicsize) - - #define _genericTypeExtender(etype) \ - (reinterpret_cast<char *>(etype) + PepHeapType_SIZE) - - #define PepType_SOTP(etype) \ - (*reinterpret_cast<SbkObjectTypePrivate **>(_genericTypeExtender(etype))) - -This looks complicated, but in the end there is only a single new -indirection via ``PyType_Type``, which happens at runtime. This is the -key to fulfil what Pep 384 wants to achieve: *No more version-dependent fields*. - - -Simplification --------------- - -After all type extension fields were replaced by macro calls, we -could remove the following version dependent re-definition of ``PyHeapTypeObject`` -:: - - typedef struct _pyheaptypeobject { - union { - PyTypeObject ht_type; - void *opaque[PY_HEAPTYPE_SIZE]; - }; - } PyHeapTypeObject; - -, and the version dependent structure:: - - struct LIBSHIBOKEN_API SbkObjectType - { - PyHeapTypeObject super; - SbkObjectTypePrivate *sotp; - }; - -could be replaced by the simplified:: - - struct LIBSHIBOKEN_API SbkObjectType - { - PyTypeObject type; - }; - -which is no longer version-dependent. -Note that we tried to replace the above struct directly by ``PyTypeObject``, -but that was too much. The distinction between ``SbkObjectType`` and -``PyTypeObject`` is still needed. - - -Appendix B: Verification Of PyTypeObject -======================================== - -We have introduced a limited PyTypeObject in the same place -as the original PyTypeObject, and now we need to prove that -we are allowed to do so. - -When using the limited API as intended, then types are completely -opaque, and access is only through ``PyType_FromSpec`` and (from -version 3.5 upwards) through ``PyType_GetSlot``. - -Python then uses all the slot definitions in the type description -and produces a regular heap type object. - - -Unused Information ------------------- - -We know many things about types that are not explicitly said, -but they are inherently clear: - -(a) The basic structure of a type is always the same, regardless - if it is a static type or a heap type. - -(b) types are evolving very slowly, and a field is never replaced - by another field with different semantics. - -Inherent rule (a) gives us the following information: If we calculate -the offsets of the basic fields, then this info is also usable for non-heap -types. - -The validation checks if rule (b) is still valid. - - -How it Works ------------- - -The basic idea of the validation is to produce a new type using -``PyType_FromSpec`` and to see where in the type structure these fields -show up. So we build a ``PyType_Slot`` structure with all the fields we -are using and make sure that these values are all unique in the -type. - -Most fields are not interrogated by ``PyType_FromSpec``, and so we -simply used some numeric value. Some fields are interpreted, like -``tp_members``. This field must really be a ``PyMemberDef``. And there are -``tp_base`` and ``tp_bases`` which have to be type objects and lists -thereof. It was easiest to not produce these fields from scratch -but use them from the ``type`` object ``PyType_Type``. - -Then one would think to write a function that searches the known -values in the opaque type structure. - -But we can do better and use optimistically the observation (b): -We simply use the restricted ``PyTypeObject`` structure and assume that -every field lands exactly where we are awaiting it. - -And that is the whole proof: If we find all the disjoint values at -the places where we expect them, then verification is done. - - -About ``tp_dict`` ------------------ - -One word about the ``tp_dict`` field: This field is a bit special in -the proof, since it does not appear in the spec and cannot easily -be checked by ``type.__dict__`` because that creates a *dictproxy* -object. So how do we prove that is really the right dict? - -We have to create that ``PyMethodDef`` structure anyway, and instead of -leaving it empty, we insert a dummy function. Then we ask the -``tp_dict`` field if it has the awaited object in it, and that's it! - -#EOT diff --git a/sources/shiboken6/libshiboken/pyobjectholder.h b/sources/shiboken6/libshiboken/pyobjectholder.h new file mode 100644 index 000000000..857748c2f --- /dev/null +++ b/sources/shiboken6/libshiboken/pyobjectholder.h @@ -0,0 +1,86 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef PYOBJECTHOLDER_H +#define PYOBJECTHOLDER_H + +#include "sbkpython.h" + +#include <cassert> +#include <utility> + +namespace Shiboken +{ + +/// PyObjectHolder holds a PyObject pointer, keeping a reference decrementing +/// its reference counter when destroyed. It makes sure to hold the GIL when +/// releasing. It implements copy/move semantics and is mainly intended as a +/// base class for functors holding a callable which can be passed around and +/// stored in containers or moved from freely. +/// For one-shot functors, release() can be invoked after the call. +class PyObjectHolder +{ +public: + PyObjectHolder() noexcept = default; + + /// PyObjectHolder constructor. + /// \param pyobj A reference to a Python object + explicit PyObjectHolder(PyObject *pyObj) noexcept : m_pyObj(pyObj) + { + assert(pyObj != nullptr); + Py_INCREF(m_pyObj); + } + + PyObjectHolder(const PyObjectHolder &o) noexcept : m_pyObj(o.m_pyObj) + { + Py_XINCREF(m_pyObj); + } + + PyObjectHolder &operator=(const PyObjectHolder &o) noexcept + { + if (this != &o) { + m_pyObj = o.m_pyObj; + Py_XINCREF(m_pyObj); + } + return *this; + } + + PyObjectHolder(PyObjectHolder &&o) noexcept : m_pyObj{std::exchange(o.m_pyObj, nullptr)} {} + + PyObjectHolder &operator=(PyObjectHolder &&o) noexcept + { + m_pyObj = std::exchange(o.m_pyObj, nullptr); + return *this; + } + + /// Decref the python reference + ~PyObjectHolder() { release(); } + + [[nodiscard]] bool isNull() const { return m_pyObj == nullptr; } + [[nodiscard]] operator bool() const { return m_pyObj != nullptr; } + + /// Returns the pointer of the Python object being held. + [[nodiscard]] PyObject *object() const { return m_pyObj; } + [[nodiscard]] operator PyObject *() const { return m_pyObj; } + + [[nodiscard]] PyObject *operator->() { return m_pyObj; } + +protected: + void release() + { + if (m_pyObj != nullptr) { + assert(Py_IsInitialized()); + auto gstate = PyGILState_Ensure(); + Py_DECREF(m_pyObj); + PyGILState_Release(gstate); + m_pyObj = nullptr; + } + } + +private: + PyObject *m_pyObj = nullptr; +}; + +} // namespace Shiboken + +#endif // PYOBJECTHOLDER_H diff --git a/sources/shiboken6/libshiboken/sbkarrayconverter.cpp b/sources/shiboken6/libshiboken/sbkarrayconverter.cpp index 58d58d25c..bcc3fb767 100644 --- a/sources/shiboken6/libshiboken/sbkarrayconverter.cpp +++ b/sources/shiboken6/libshiboken/sbkarrayconverter.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "sbkarrayconverter.h" #include "sbkarrayconverter_p.h" @@ -50,8 +14,7 @@ static SbkArrayConverter *ArrayTypeConverters[Shiboken::Conversions::SBK_ARRAY_IDX_SIZE] [2] = {}; -namespace Shiboken { -namespace Conversions { +namespace Shiboken::Conversions { // Check whether Predicate is true for all elements of a sequence template <class Predicate> @@ -157,7 +120,7 @@ static void sequenceToCppIntArray(PyObject *pyIn, void *cppOut) { auto *handle = reinterpret_cast<ArrayHandle<int> *>(cppOut); handle->allocate(PySequence_Size(pyIn)); - convertPySequence(pyIn, _PepLong_AsInt, handle->data()); + convertPySequence(pyIn, PyLong_AsLong, handle->data()); } static PythonToCppFunc sequenceToCppIntArrayCheck(PyObject *pyIn, int dim1, int /* dim2 */) @@ -280,5 +243,4 @@ void setArrayTypeConverter(int index, int dimension, SbkArrayConverter *c) ArrayTypeConverters[index][dimension - 1] = c; } -} // namespace Conversions -} // namespace Shiboken +} // namespace Shiboken::Conversions diff --git a/sources/shiboken6/libshiboken/sbkarrayconverter.h b/sources/shiboken6/libshiboken/sbkarrayconverter.h index 32bbd8284..f07cb1d70 100644 --- a/sources/shiboken6/libshiboken/sbkarrayconverter.h +++ b/sources/shiboken6/libshiboken/sbkarrayconverter.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef SBKARRAYCONVERTERS_H #define SBKARRAYCONVERTERS_H @@ -47,8 +11,7 @@ extern "C" { struct SbkArrayConverter; } -namespace Shiboken { -namespace Conversions { +namespace Shiboken::Conversions { enum : int { SBK_UNIMPLEMENTED_ARRAY_IDX, @@ -168,7 +131,6 @@ void ArrayHandle<T>::destroy() m_owned = false; } -} // namespace Conversions -} // namespace Shiboken +} // namespace Shiboken::Conversions #endif // SBKARRAYCONVERTERS_H diff --git a/sources/shiboken6/libshiboken/sbkarrayconverter_p.h b/sources/shiboken6/libshiboken/sbkarrayconverter_p.h index a7b46702b..63d03fb12 100644 --- a/sources/shiboken6/libshiboken/sbkarrayconverter_p.h +++ b/sources/shiboken6/libshiboken/sbkarrayconverter_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef SBKARRAYCONVERTER_P_H #define SBKARRAYCONVERTER_P_H @@ -46,7 +10,7 @@ extern "C" { -typedef PythonToCppFunc (*IsArrayConvertibleToCppFunc)(PyObject *, int dim1, int dim2); +using IsArrayConvertibleToCppFunc = PythonToCppFunc (*)(PyObject *, int dim1, int dim2); /** * \internal * Private structure of SbkArrayConverter. diff --git a/sources/shiboken6/libshiboken/sbkcontainer.cpp b/sources/shiboken6/libshiboken/sbkcontainer.cpp new file mode 100644 index 000000000..7de1f03e6 --- /dev/null +++ b/sources/shiboken6/libshiboken/sbkcontainer.cpp @@ -0,0 +1,19 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "sbkcontainer.h" +#include "sbkstaticstrings.h" +#include "autodecref.h" + +namespace Shiboken +{ +bool isOpaqueContainer(PyObject *o) +{ + if (!o) + return false; + Shiboken::AutoDecRef tpDict(PepType_GetDict(o->ob_type)); + return o != nullptr && o != Py_None + && PyDict_Contains(tpDict.object(), Shiboken::PyMagicName::opaque_container()) == 1; + +} +} // Shiboken diff --git a/sources/shiboken6/libshiboken/sbkcontainer.h b/sources/shiboken6/libshiboken/sbkcontainer.h new file mode 100644 index 000000000..240c772a9 --- /dev/null +++ b/sources/shiboken6/libshiboken/sbkcontainer.h @@ -0,0 +1,281 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef SBK_CONTAINER_H +#define SBK_CONTAINER_H + +#include "sbkpython.h" +#include "shibokenmacros.h" +#include "shibokenbuffer.h" + +#include <algorithm> +#include <iterator> +#include <optional> +#include <utility> + +extern "C" +{ +struct LIBSHIBOKEN_API ShibokenContainer +{ + PyObject_HEAD + void *d; +}; + +} // extern "C" + +// Conversion helper traits for container values (Keep it out of namespace as +// otherwise clashes occur). +template <class Value> +struct ShibokenContainerValueConverter +{ + static bool checkValue(PyObject *pyArg); + static PyObject *convertValueToPython(Value v); + static std::optional<Value> convertValueToCpp(PyObject pyArg); +}; + +// SFINAE test for the presence of reserve() in a sequence container (std::vector/QList) +template <typename T> +class ShibokenContainerHasReserve +{ +private: + using YesType = char[1]; + using NoType = char[2]; + + template <typename C> static YesType& test( decltype(&C::reserve) ) ; + template <typename C> static NoType& test(...); + +public: + enum { value = sizeof(test<T>(nullptr)) == sizeof(YesType) }; +}; + +template <class SequenceContainer> +class ShibokenSequenceContainerPrivate // Helper for sequence type containers +{ +public: + using value_type = typename SequenceContainer::value_type; + using OptionalValue = typename std::optional<value_type>; + + SequenceContainer *m_list{}; + bool m_ownsList = false; + bool m_const = false; + static constexpr const char *msgModifyConstContainer = + "Attempt to modify a constant container."; + + static PyObject *tpNew(PyTypeObject *subtype, PyObject * /* args */, PyObject * /* kwds */) + { + allocfunc allocFunc = reinterpret_cast<allocfunc>(PepType_GetSlot(subtype, Py_tp_alloc)); + auto *me = reinterpret_cast<ShibokenContainer *>(allocFunc(subtype, 0)); + auto *d = new ShibokenSequenceContainerPrivate; + d->m_list = new SequenceContainer; + d->m_ownsList = true; + me->d = d; + return reinterpret_cast<PyObject *>(me); + } + + static PyObject *tpNewInvalid(PyTypeObject * /* subtype */, PyObject * /* args */, PyObject * /* kwds */) + { + PyErr_Format(PyExc_NotImplementedError, + "Opaque containers of type '%s' cannot be instantiated.", + typeid(SequenceContainer).name()); + return nullptr; + } + + static int tpInit(PyObject * /* self */, PyObject * /* args */, PyObject * /* kwds */) + { + return 0; + } + + static void tpFree(void *self) + { + auto *pySelf = reinterpret_cast<PyObject *>(self); + auto *d = get(pySelf); + if (d->m_ownsList) + delete d->m_list; + delete d; + auto freeFunc = reinterpret_cast<freefunc>(PepType_GetSlot(Py_TYPE(pySelf)->tp_base, + Py_tp_free)); + freeFunc(self); + } + + static Py_ssize_t sqLen(PyObject *self) + { + return get(self)->m_list->size(); + } + + static PyObject *sqGetItem(PyObject *self, Py_ssize_t i) + { + auto *d = get(self); + if (i < 0 || i >= Py_ssize_t(d->m_list->size())) { + PyErr_SetString(PyExc_IndexError, "index out of bounds"); + return nullptr; + } + auto it = std::cbegin(*d->m_list); + std::advance(it, i); + return ShibokenContainerValueConverter<value_type>::convertValueToPython(*it); + } + + static int sqSetItem(PyObject *self, Py_ssize_t i, PyObject *pyArg) + { + auto *d = get(self); + if (i < 0 || i >= Py_ssize_t(d->m_list->size())) { + PyErr_SetString(PyExc_IndexError, "index out of bounds"); + return -1; + } + auto it = std::begin(*d->m_list); + std::advance(it, i); + OptionalValue value = ShibokenContainerValueConverter<value_type>::convertValueToCpp(pyArg); + if (!value.has_value()) + return -1; + *it = value.value(); + return 0; + } + + static PyObject *push_back(PyObject *self, PyObject *pyArg) + { + auto *d = get(self); + if (!ShibokenContainerValueConverter<value_type>::checkValue(pyArg)) { + PyErr_SetString(PyExc_TypeError, "wrong type passed to append."); + return nullptr; + } + if (d->m_const) { + PyErr_SetString(PyExc_TypeError, msgModifyConstContainer); + return nullptr; + } + + OptionalValue value = ShibokenContainerValueConverter<value_type>::convertValueToCpp(pyArg); + if (!value.has_value()) + return nullptr; + d->m_list->push_back(value.value()); + Py_RETURN_NONE; + } + + static PyObject *push_front(PyObject *self, PyObject *pyArg) + { + auto *d = get(self); + if (!ShibokenContainerValueConverter<value_type>::checkValue(pyArg)) { + PyErr_SetString(PyExc_TypeError, "wrong type passed to append."); + return nullptr; + } + if (d->m_const) { + PyErr_SetString(PyExc_TypeError, msgModifyConstContainer); + return nullptr; + } + + OptionalValue value = ShibokenContainerValueConverter<value_type>::convertValueToCpp(pyArg); + if (!value.has_value()) + return nullptr; + d->m_list->push_front(value.value()); + Py_RETURN_NONE; + } + + static PyObject *clear(PyObject *self) + { + auto *d = get(self); + if (d->m_const) { + PyErr_SetString(PyExc_TypeError, msgModifyConstContainer); + return nullptr; + } + + d->m_list->clear(); + Py_RETURN_NONE; + } + + static PyObject *pop_back(PyObject *self) + { + auto *d = get(self); + if (d->m_const) { + PyErr_SetString(PyExc_TypeError, msgModifyConstContainer); + return nullptr; + } + + d->m_list->pop_back(); + Py_RETURN_NONE; + } + + static PyObject *pop_front(PyObject *self) + { + auto *d = get(self); + if (d->m_const) { + PyErr_SetString(PyExc_TypeError, msgModifyConstContainer); + return nullptr; + } + + d->m_list->pop_front(); + Py_RETURN_NONE; + } + + // Support for containers with reserve/capacity + static PyObject *reserve(PyObject *self, PyObject *pyArg) + { + auto *d = get(self); + if (PyLong_Check(pyArg) == 0) { + PyErr_SetString(PyExc_TypeError, "wrong type passed to reserve()."); + return nullptr; + } + if (d->m_const) { + PyErr_SetString(PyExc_TypeError, msgModifyConstContainer); + return nullptr; + } + + if constexpr (ShibokenContainerHasReserve<SequenceContainer>::value) { + const Py_ssize_t size = PyLong_AsSsize_t(pyArg); + d->m_list->reserve(size); + } else { + PyErr_SetString(PyExc_TypeError, "Container does not support reserve()."); + return nullptr; + } + + Py_RETURN_NONE; + } + + static PyObject *capacity(PyObject *self) + { + Py_ssize_t result = -1; + if constexpr (ShibokenContainerHasReserve<SequenceContainer>::value) { + const auto *d = get(self); + result = d->m_list->capacity(); + } + return PyLong_FromSsize_t(result); + } + + static PyObject *data(PyObject *self) + { + PyObject *result = nullptr; + if constexpr (ShibokenContainerHasReserve<SequenceContainer>::value) { + const auto *d = get(self); + auto *data = d->m_list->data(); + const Py_ssize_t size = sizeof(value_type) * d->m_list->size(); + result = Shiboken::Buffer::newObject(data, size, Shiboken::Buffer::ReadWrite); + } else { + PyErr_SetString(PyExc_TypeError, "Container does not support data()."); + } + return result; + } + + static PyObject *constData(PyObject *self) + { + PyObject *result = nullptr; + if constexpr (ShibokenContainerHasReserve<SequenceContainer>::value) { + const auto *d = get(self); + const auto *data = std::as_const(d->m_list)->data(); + const Py_ssize_t size = sizeof(value_type) * d->m_list->size(); + result = Shiboken::Buffer::newObject(data, size); + } else { + PyErr_SetString(PyExc_TypeError, "Container does not support constData()."); + } + return result; + } + + static ShibokenSequenceContainerPrivate *get(PyObject *self) + { + auto *data = reinterpret_cast<ShibokenContainer *>(self); + return reinterpret_cast<ShibokenSequenceContainerPrivate *>(data->d); + } +}; + +namespace Shiboken +{ +LIBSHIBOKEN_API bool isOpaqueContainer(PyObject *o); +} + +#endif // SBK_CONTAINER_H diff --git a/sources/shiboken6/libshiboken/sbkconverter.cpp b/sources/shiboken6/libshiboken/sbkconverter.cpp index 87b4bda63..358827aa8 100644 --- a/sources/shiboken6/libshiboken/sbkconverter.cpp +++ b/sources/shiboken6/libshiboken/sbkconverter.cpp @@ -1,45 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "sbkconverter.h" #include "sbkconverter_p.h" #include "sbkarrayconverter_p.h" +#include "sbkmodule.h" #include "basewrapper_p.h" #include "bindingmanager.h" #include "autodecref.h" @@ -54,8 +19,7 @@ static SbkConverter **PrimitiveTypeConverters; using ConvertersMap = std::unordered_map<std::string, SbkConverter *>; static ConvertersMap converters; -namespace Shiboken { -namespace Conversions { +namespace Shiboken::Conversions { void initArrayConverters(); @@ -73,6 +37,7 @@ void init() Primitive<short>::createConverter(), Primitive<signed char>::createConverter(), Primitive<std::string>::createConverter(), + Primitive<std::wstring>::createConverter(), Primitive<unsigned PY_LONG_LONG>::createConverter(), Primitive<unsigned char>::createConverter(), Primitive<unsigned int>::createConverter(), @@ -95,6 +60,7 @@ void init() converters["short"] = primitiveTypeConverters[SBK_SHORT_IDX]; converters["signed char"] = primitiveTypeConverters[SBK_SIGNEDCHAR_IDX]; converters["std::string"] = primitiveTypeConverters[SBK_STD_STRING_IDX]; + converters["std::wstring"] = primitiveTypeConverters[SBK_STD_WSTRING_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]; @@ -127,14 +93,14 @@ SbkConverter *createConverterObject(PyTypeObject *type, return converter; } -SbkConverter *createConverter(SbkObjectType *type, +SbkConverter *createConverter(PyTypeObject *type, PythonToCppFunc toCppPointerConvFunc, IsConvertibleToCppFunc toCppPointerCheckFunc, CppToPythonFunc pointerToPythonFunc, CppToPythonFunc copyToPythonFunc) { SbkConverter *converter = - createConverterObject(reinterpret_cast<PyTypeObject *>(type), + createConverterObject(type, toCppPointerConvFunc, toCppPointerCheckFunc, pointerToPythonFunc, copyToPythonFunc); PepType_SOTP(type)->converter = converter; @@ -173,7 +139,7 @@ void addPythonToCppValueConversion(SbkConverter *converter, converter->toCppConversions.push_back(std::make_pair(isConvertibleToCppFunc, pythonToCppFunc)); } -void addPythonToCppValueConversion(SbkObjectType *type, +void addPythonToCppValueConversion(PyTypeObject *type, PythonToCppFunc pythonToCppFunc, IsConvertibleToCppFunc isConvertibleToCppFunc) { @@ -181,7 +147,14 @@ void addPythonToCppValueConversion(SbkObjectType *type, addPythonToCppValueConversion(sotp->converter, pythonToCppFunc, isConvertibleToCppFunc); } -PyObject *pointerToPython(SbkObjectType *type, const void *cppIn) +void addPythonToCppValueConversion(Shiboken::Module::TypeInitStruct typeStruct, + PythonToCppFunc pythonToCppFunc, + IsConvertibleToCppFunc isConvertibleToCppFunc) +{ + addPythonToCppValueConversion(typeStruct.type, pythonToCppFunc, isConvertibleToCppFunc); +} + +PyObject *pointerToPython(PyTypeObject *type, const void *cppIn) { auto *sotp = PepType_SOTP(type); return pointerToPython(sotp->converter, cppIn); @@ -200,7 +173,7 @@ PyObject *pointerToPython(const SbkConverter *converter, const void *cppIn) return converter->pointerToPython(cppIn); } -PyObject *referenceToPython(SbkObjectType *type, const void *cppIn) +PyObject *referenceToPython(PyTypeObject *type, const void *cppIn) { auto *sotp = PepType_SOTP(type); return referenceToPython(sotp->converter, cppIn); @@ -235,7 +208,7 @@ static inline PyObject *CopyCppToPython(const SbkConverter *converter, const voi return converter->copyToPython(cppIn); } -PyObject *copyToPython(SbkObjectType *type, const void *cppIn) +PyObject *copyToPython(PyTypeObject *type, const void *cppIn) { auto *sotp = PepType_SOTP(type); return CopyCppToPython(sotp->converter, cppIn); @@ -246,13 +219,27 @@ PyObject *copyToPython(const SbkConverter *converter, const void *cppIn) return CopyCppToPython(converter, cppIn); } -PythonToCppFunc isPythonToCppPointerConvertible(SbkObjectType *type, PyObject *pyIn) +PythonToCppFunc isPythonToCppPointerConvertible(PyTypeObject *type, PyObject *pyIn) { assert(pyIn); auto *sotp = PepType_SOTP(type); return sotp->converter->toCppPointerConversion.first(pyIn); } +PythonToCppConversion pythonToCppPointerConversion(PyTypeObject *type, PyObject *pyIn) +{ + if (pyIn == nullptr) + return {}; + if (PythonToCppFunc toCppPtr = isPythonToCppPointerConvertible(type, pyIn)) + return {toCppPtr, PythonToCppConversion::Pointer}; + return {}; +} + +PythonToCppConversion pythonToCppPointerConversion(Module::TypeInitStruct typeStruct, PyObject *pyIn) +{ + return pythonToCppPointerConversion(typeStruct.type, pyIn); +} + static inline PythonToCppFunc IsPythonToCppConvertible(const SbkConverter *converter, PyObject *pyIn) { assert(pyIn); @@ -263,17 +250,46 @@ static inline PythonToCppFunc IsPythonToCppConvertible(const SbkConverter *conve return nullptr; } -PythonToCppFunc isPythonToCppValueConvertible(SbkObjectType *type, PyObject *pyIn) +PythonToCppFunc isPythonToCppValueConvertible(PyTypeObject *type, PyObject *pyIn) { auto *sotp = PepType_SOTP(type); return IsPythonToCppConvertible(sotp->converter, pyIn); } +PythonToCppConversion pythonToCppValueConversion(PyTypeObject *type, PyObject *pyIn) +{ + if (pyIn == nullptr) + return {}; + if (PythonToCppFunc toCppVal = isPythonToCppValueConvertible(type, pyIn)) + return {toCppVal, PythonToCppConversion::Value}; + return {}; +} + PythonToCppFunc isPythonToCppConvertible(const SbkConverter *converter, PyObject *pyIn) { return IsPythonToCppConvertible(converter, pyIn); } +PythonToCppConversion pythonToCppReferenceConversion(const SbkConverter *converter, PyObject *pyIn) +{ + if (converter->toCppPointerConversion.first) { + if (auto toCppPtr = converter->toCppPointerConversion.first(pyIn)) + return {toCppPtr, PythonToCppConversion::Pointer}; + } + for (const ToCppConversion &c : converter->toCppConversions) { + if (PythonToCppFunc toCppFunc = c.first(pyIn)) + return {toCppFunc, PythonToCppConversion::Value}; + } + return {}; +} + +PythonToCppConversion pythonToCppConversion(const SbkConverter *converter, PyObject *pyIn) +{ + if (auto func = IsPythonToCppConvertible(converter, pyIn)) + return {func, PythonToCppConversion::Value}; + return {}; +} + PythonToCppFunc isPythonToCppConvertible(const SbkArrayConverter *converter, int dim1, int dim2, PyObject *pyIn) { @@ -285,7 +301,15 @@ PythonToCppFunc isPythonToCppConvertible(const SbkArrayConverter *converter, return nullptr; } -PythonToCppFunc isPythonToCppReferenceConvertible(SbkObjectType *type, PyObject *pyIn) +LIBSHIBOKEN_API PythonToCppConversion pythonToCppConversion(const SbkArrayConverter *converter, + int dim1, int dim2, PyObject *pyIn) +{ + if (auto func = isPythonToCppConvertible(converter, dim1, dim2, pyIn)) + return {func, PythonToCppConversion::Value}; + return {}; +} + +PythonToCppFunc isPythonToCppReferenceConvertible(PyTypeObject *type, PyObject *pyIn) { if (pyIn != Py_None) { PythonToCppFunc toCpp = isPythonToCppPointerConvertible(type, pyIn); @@ -295,6 +319,19 @@ PythonToCppFunc isPythonToCppReferenceConvertible(SbkObjectType *type, PyObject return isPythonToCppValueConvertible(type, pyIn); } +PythonToCppConversion pythonToCppReferenceConversion(PyTypeObject *type, PyObject *pyIn) +{ + if (pyIn == nullptr) + return {}; + if (pyIn != Py_None) { + if (PythonToCppFunc toCppPtr = isPythonToCppPointerConvertible(type, pyIn)) + return {toCppPtr, PythonToCppConversion::Pointer}; + } + if (PythonToCppFunc toCppVal = isPythonToCppValueConvertible(type, pyIn)) + return {toCppVal, PythonToCppConversion::Value}; + return {}; +} + void nonePythonToCppNullPtr(PyObject *, void *cppOut) { assert(cppOut); @@ -306,20 +343,20 @@ void *cppPointer(PyTypeObject *desiredType, SbkObject *pyIn) assert(pyIn); if (!ObjectType::checkType(desiredType)) return pyIn; - auto *inType = reinterpret_cast<SbkObjectType *>(Py_TYPE(pyIn)); + auto *inType = 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) +void pythonToCppPointer(PyTypeObject *type, PyObject *pyIn, void *cppOut) { assert(type); assert(pyIn); assert(cppOut); *reinterpret_cast<void **>(cppOut) = pyIn == Py_None ? nullptr - : cppPointer(reinterpret_cast<PyTypeObject *>(type), reinterpret_cast<SbkObject *>(pyIn)); + : cppPointer(type, reinterpret_cast<SbkObject *>(pyIn)); } void pythonToCppPointer(const SbkConverter *converter, PyObject *pyIn, void *cppOut) @@ -329,7 +366,7 @@ void pythonToCppPointer(const SbkConverter *converter, PyObject *pyIn, void *cpp assert(cppOut); *reinterpret_cast<void **>(cppOut) = pyIn == Py_None ? nullptr - : cppPointer(reinterpret_cast<PyTypeObject *>(converter->pythonType), reinterpret_cast<SbkObject *>(pyIn)); + : cppPointer(converter->pythonType, reinterpret_cast<SbkObject *>(pyIn)); } static void _pythonToCppCopy(const SbkConverter *converter, PyObject *pyIn, void *cppOut) @@ -342,7 +379,7 @@ static void _pythonToCppCopy(const SbkConverter *converter, PyObject *pyIn, void toCpp(pyIn, cppOut); } -void pythonToCppCopy(SbkObjectType *type, PyObject *pyIn, void *cppOut) +void pythonToCppCopy(PyTypeObject *type, PyObject *pyIn, void *cppOut) { assert(type); auto *sotp = PepType_SOTP(type); @@ -354,7 +391,7 @@ void pythonToCppCopy(const SbkConverter *converter, PyObject *pyIn, void *cppOut _pythonToCppCopy(converter, pyIn, cppOut); } -bool isImplicitConversion(SbkObjectType *type, PythonToCppFunc toCppFunc) +bool isImplicitConversion(PyTypeObject *type, PythonToCppFunc toCppFunc) { auto *sotp = PepType_SOTP(type); // This is the Object Type or Value Type conversion that only @@ -378,19 +415,34 @@ bool isImplicitConversion(SbkObjectType *type, PythonToCppFunc toCppFunc) return toCppFunc != (*conv).second; } -void registerConverterName(SbkConverter *converter , const char *typeName) +void registerConverterName(SbkConverter *converter, const char *typeName) { auto iter = converters.find(typeName); if (iter == converters.end()) converters.insert(std::make_pair(typeName, converter)); } +static std::string getRealTypeName(const char *name) +{ + std::string typeName(name); + auto size = typeName.size(); + if (std::isalnum(typeName[size - 1]) == 0) + return typeName.substr(0, size - 1); + return typeName; +} + SbkConverter *getConverter(const char *typeName) { - ConvertersMap::const_iterator it = converters.find(typeName); + auto it = converters.find(typeName); if (it != converters.end()) return it->second; - if (Py_VerboseFlag > 0) { + // PYSIDE-2404: Did not find the name. Load the lazy classes + // which have this name and try again. + Shiboken::Module::loadLazyClassesWithName(getRealTypeName(typeName).c_str()); + it = converters.find(typeName); + if (it != converters.end()) + return it->second; + if (Shiboken::pyVerbose() > 0) { const std::string message = std::string("Can't find type resolver for type '") + typeName + "'."; PyErr_WarnEx(PyExc_RuntimeWarning, message.c_str(), 0); @@ -403,6 +455,27 @@ SbkConverter *primitiveTypeConverter(int index) return PrimitiveTypeConverters[index]; } +bool checkIterableTypes(PyTypeObject *type, PyObject *pyIn) +{ + Shiboken::AutoDecRef it(PyObject_GetIter(pyIn)); + if (it.isNull()) { + PyErr_Clear(); + return false; + } + + while (true) { + Shiboken::AutoDecRef pyItem(PyIter_Next(it.object())); + if (pyItem.isNull()) { + if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_StopIteration)) + PyErr_Clear(); + break; + } + if (!PyObject_TypeCheck(pyItem, type)) + return false; + } + return true; +} + bool checkSequenceTypes(PyTypeObject *type, PyObject *pyIn) { assert(type); @@ -419,6 +492,28 @@ bool checkSequenceTypes(PyTypeObject *type, PyObject *pyIn) } return true; } + +bool convertibleIterableTypes(const SbkConverter *converter, PyObject *pyIn) +{ + Shiboken::AutoDecRef it(PyObject_GetIter(pyIn)); + if (it.isNull()) { + PyErr_Clear(); + return false; + } + + while (true) { + Shiboken::AutoDecRef pyItem(PyIter_Next(it.object())); + if (pyItem.isNull()) { + if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_StopIteration)) + PyErr_Clear(); + break; + } + if (!isPythonToCppConvertible(converter, pyItem)) + return false; + } + return true; +} + bool convertibleSequenceTypes(const SbkConverter *converter, PyObject *pyIn) { assert(converter); @@ -432,13 +527,20 @@ bool convertibleSequenceTypes(const SbkConverter *converter, PyObject *pyIn) } return true; } -bool convertibleSequenceTypes(SbkObjectType *type, PyObject *pyIn) +bool convertibleSequenceTypes(PyTypeObject *type, PyObject *pyIn) { assert(type); auto *sotp = PepType_SOTP(type); return convertibleSequenceTypes(sotp->converter, pyIn); } +bool convertibleIterableTypes(PyTypeObject *type, PyObject *pyIn) +{ + assert(type); + auto *sotp = PepType_SOTP(type); + return convertibleIterableTypes(sotp->converter, pyIn); +} + bool checkPairTypes(PyTypeObject *firstType, PyTypeObject *secondType, PyObject *pyIn) { assert(firstType); @@ -602,7 +704,14 @@ PyTypeObject *getPythonTypeObject(const SbkConverter *converter) PyTypeObject *getPythonTypeObject(const char *typeName) { - return getPythonTypeObject(getConverter(typeName)); + auto *type = getPythonTypeObject(getConverter(typeName)); + if (type == nullptr) { + // PYSIDE-2404: Did not find the name. Load the lazy classes + // which have this name and try again. + Shiboken::Module::loadLazyClassesWithName(getRealTypeName(typeName).c_str()); + type = getPythonTypeObject(getConverter(typeName)); + } + return type; } bool pythonTypeIsValueType(const SbkConverter *converter) @@ -673,4 +782,4 @@ void SpecificConverter::toCpp(PyObject *pyIn, void *cppOut) } } -} } // namespace Shiboken::Conversions +} // namespace Shiboken::Conversions diff --git a/sources/shiboken6/libshiboken/sbkconverter.h b/sources/shiboken6/libshiboken/sbkconverter.h index d7803a42a..0d68f3faf 100644 --- a/sources/shiboken6/libshiboken/sbkconverter.h +++ b/sources/shiboken6/libshiboken/sbkconverter.h @@ -1,56 +1,19 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef SBK_CONVERTER_H #define SBK_CONVERTER_H #include "sbkpython.h" +#include "sbkmodule.h" #include "shibokenmacros.h" #include "sbkenum.h" -#include "sbkenum_p.h" #include "basewrapper_p.h" #include <limits> #include <string> struct SbkObject; -struct SbkObjectType; /** * This is a convenience macro identical to Python's PyObject_TypeCheck, @@ -80,7 +43,7 @@ struct SbkArrayConverter; * * C++ -> Python */ -typedef PyObject *(*CppToPythonFunc)(const void *); +using CppToPythonFunc = PyObject *(*)(const void *); /** * This function converts a Python object to a C++ value, it may be @@ -93,7 +56,7 @@ typedef PyObject *(*CppToPythonFunc)(const void *); * * Python -> C++ */ -typedef void (*PythonToCppFunc)(PyObject *,void *); +using PythonToCppFunc = void (*)(PyObject *,void *); /** * Checks if the Python object passed in the argument is convertible to a @@ -104,7 +67,7 @@ typedef void (*PythonToCppFunc)(PyObject *,void *); * * Python -> C++ ? */ -typedef PythonToCppFunc (*IsConvertibleToCppFunc)(PyObject *); +using IsConvertibleToCppFunc = PythonToCppFunc (*)(PyObject *); } // extern "C" @@ -150,7 +113,7 @@ private: * \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, +LIBSHIBOKEN_API SbkConverter *createConverter(PyTypeObject *type, PythonToCppFunc toCppPointerConvFunc, IsConvertibleToCppFunc toCppPointerCheckFunc, CppToPythonFunc pointerToPythonFunc, @@ -181,7 +144,10 @@ LIBSHIBOKEN_API void setPythonToCppPointerFunctions(SbkConverter *converter, LIBSHIBOKEN_API void addPythonToCppValueConversion(SbkConverter *converter, PythonToCppFunc pythonToCppFunc, IsConvertibleToCppFunc isConvertibleToCppFunc); -LIBSHIBOKEN_API void addPythonToCppValueConversion(SbkObjectType *type, +LIBSHIBOKEN_API void addPythonToCppValueConversion(PyTypeObject *type, + PythonToCppFunc pythonToCppFunc, + IsConvertibleToCppFunc isConvertibleToCppFunc); +LIBSHIBOKEN_API void addPythonToCppValueConversion(Shiboken::Module::TypeInitStruct typeStruct, PythonToCppFunc pythonToCppFunc, IsConvertibleToCppFunc isConvertibleToCppFunc); @@ -194,7 +160,7 @@ LIBSHIBOKEN_API void addPythonToCppValueConversion(SbkObjectType *type, * TYPE *var; * PyObject *pyVar = pointerToPython(SBKTYPE, &var); */ -LIBSHIBOKEN_API PyObject *pointerToPython(SbkObjectType *type, const void *cppIn); +LIBSHIBOKEN_API PyObject *pointerToPython(PyTypeObject *type, const void *cppIn); LIBSHIBOKEN_API PyObject *pointerToPython(const SbkConverter *converter, const void *cppIn); /** @@ -206,7 +172,7 @@ LIBSHIBOKEN_API PyObject *pointerToPython(const SbkConverter *converter, const v * TYPE &var = SOMETHING; * PyObject *pyVar = referenceToPython(SBKTYPE, &var); */ -LIBSHIBOKEN_API PyObject *referenceToPython(SbkObjectType *type, const void *cppIn); +LIBSHIBOKEN_API PyObject *referenceToPython(PyTypeObject *type, const void *cppIn); LIBSHIBOKEN_API PyObject *referenceToPython(const SbkConverter *converter, const void *cppIn); /** @@ -216,16 +182,32 @@ LIBSHIBOKEN_API PyObject *referenceToPython(const SbkConverter *converter, const * TYPE var; * PyObject *pyVar = copyToPython(SBKTYPE, &var); */ -LIBSHIBOKEN_API PyObject *copyToPython(SbkObjectType *type, const void *cppIn); +LIBSHIBOKEN_API PyObject *copyToPython(PyTypeObject *type, const void *cppIn); LIBSHIBOKEN_API PyObject *copyToPython(const SbkConverter *converter, const void *cppIn); // Python -> C++ --------------------------------------------------------------------------- +struct PythonToCppConversion +{ + enum Type {Invalid, Pointer, Value}; + + operator bool() const { return type != Invalid; } + + void operator()(PyObject *po,void *cpp) const { function(po, cpp); } + + bool isValue() const { return type == Value; } + + PythonToCppFunc function = nullptr; + Type type = Invalid; +}; + /** * 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(SbkObjectType *type, PyObject *pyIn); +LIBSHIBOKEN_API PythonToCppFunc isPythonToCppPointerConvertible(PyTypeObject *type, PyObject *pyIn); +LIBSHIBOKEN_API PythonToCppConversion pythonToCppPointerConversion(PyTypeObject *type, PyObject *pyIn); +LIBSHIBOKEN_API PythonToCppConversion pythonToCppPointerConversion(Module::TypeInitStruct typeStruct, PyObject *pyIn); /** * Returns a Python to C++ conversion function if the Python object is convertible to a C++ value. @@ -233,7 +215,8 @@ LIBSHIBOKEN_API PythonToCppFunc isPythonToCppPointerConvertible(SbkObjectType *t * 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(SbkObjectType *type, PyObject *pyIn); +LIBSHIBOKEN_API PythonToCppFunc isPythonToCppValueConvertible(PyTypeObject *type, PyObject *pyIn); +LIBSHIBOKEN_API PythonToCppConversion pythonToCppValueConversion(PyTypeObject *type, PyObject *pyIn); /** * Returns a Python to C++ conversion function if the Python object is convertible to a C++ reference. @@ -241,12 +224,18 @@ LIBSHIBOKEN_API PythonToCppFunc isPythonToCppValueConvertible(SbkObjectType *typ * 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(SbkObjectType *type, PyObject *pyIn); +LIBSHIBOKEN_API PythonToCppFunc isPythonToCppReferenceConvertible(PyTypeObject *type, PyObject *pyIn); +LIBSHIBOKEN_API PythonToCppConversion pythonToCppReferenceConversion(PyTypeObject *type, PyObject *pyIn); /// This is the same as isPythonToCppValueConvertible function. LIBSHIBOKEN_API PythonToCppFunc isPythonToCppConvertible(const SbkConverter *converter, PyObject *pyIn); +LIBSHIBOKEN_API PythonToCppConversion pythonToCppReferenceConversion(const SbkConverter *converter, PyObject *pyIn); + +LIBSHIBOKEN_API PythonToCppConversion pythonToCppConversion(const SbkConverter *converter, PyObject *pyIn); LIBSHIBOKEN_API PythonToCppFunc isPythonToCppConvertible(const SbkArrayConverter *converter, int dim1, int dim2, PyObject *pyIn); +LIBSHIBOKEN_API PythonToCppConversion pythonToCppConversion(const SbkArrayConverter *converter, + int dim1, int dim2, PyObject *pyIn); /** * Returns the C++ pointer for the \p pyIn object cast to the type passed via \p desiredType. @@ -256,11 +245,11 @@ LIBSHIBOKEN_API PythonToCppFunc isPythonToCppConvertible(const SbkArrayConverter 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(PyTypeObject *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(SbkObjectType *type, PyObject *pyIn, void *cppOut); +LIBSHIBOKEN_API void pythonToCppCopy(PyTypeObject *type, PyObject *pyIn, void *cppOut); LIBSHIBOKEN_API void pythonToCppCopy(const SbkConverter *converter, PyObject *pyIn, void *cppOut); /** @@ -274,7 +263,7 @@ LIBSHIBOKEN_API void nonePythonToCppNullPtr(PyObject *, void *cppOut); * It is used when C++ expects a reference argument, so it may be the same object received * from Python, or another created through implicit conversion. */ -LIBSHIBOKEN_API bool isImplicitConversion(SbkObjectType *type, PythonToCppFunc toCpp); +[[deprecated]] LIBSHIBOKEN_API bool isImplicitConversion(PyTypeObject *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); @@ -288,11 +277,23 @@ 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 type is iterable and comprised of objects of the +/// given \p type. +LIBSHIBOKEN_API bool checkIterableTypes(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(SbkObjectType *type, PyObject *pyIn); +LIBSHIBOKEN_API bool convertibleSequenceTypes(PyTypeObject *type, PyObject *pyIn); + +/// Returns true if a Python type is iterable and comprised of objects of a +/// type convertible to the one represented by the given \p converter. +LIBSHIBOKEN_API bool convertibleIterableTypes(const SbkConverter *converter, PyObject *pyIn); + +/// Returns true if a Python type is iterable and comprised of objects of a +/// type convertible to \p type. +LIBSHIBOKEN_API bool convertibleIterableTypes(PyTypeObject *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); @@ -351,13 +352,14 @@ LIBSHIBOKEN_API bool pythonTypeIsWrapperType(const SbkConverter *converter); #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 -#define SBK_NULLPTR_T_IDX 17 +#define SBK_STD_WSTRING_IDX 11 +#define SBK_UNSIGNEDPY_LONG_LONG_IDX 12 +#define SBK_UNSIGNEDCHAR_IDX 13 +#define SBK_UNSIGNEDINT_IDX 14 +#define SBK_UNSIGNEDLONG_IDX 15 +#define SBK_UNSIGNEDSHORT_IDX 16 +#define SBK_VOIDPTR_IDX 17 +#define SBK_NULLPTR_T_IDX 18 template<typename T> SbkConverter *PrimitiveTypeConverter() { return nullptr; } template<> inline SbkConverter *PrimitiveTypeConverter<PY_LONG_LONG>() { return primitiveTypeConverter(SBK_PY_LONG_LONG_IDX); } @@ -371,6 +373,7 @@ template<> inline SbkConverter *PrimitiveTypeConverter<long>() { return primitiv template<> inline SbkConverter *PrimitiveTypeConverter<short>() { return primitiveTypeConverter(SBK_SHORT_IDX); } template<> inline SbkConverter *PrimitiveTypeConverter<signed char>() { return primitiveTypeConverter(SBK_SIGNEDCHAR_IDX); } template<> inline SbkConverter *PrimitiveTypeConverter<std::string>() { return primitiveTypeConverter(SBK_STD_STRING_IDX); } +template<> inline SbkConverter *PrimitiveTypeConverter<std::wstring>() { return primitiveTypeConverter(SBK_STD_WSTRING_IDX); } template<> inline SbkConverter *PrimitiveTypeConverter<unsigned PY_LONG_LONG>() { return primitiveTypeConverter(SBK_UNSIGNEDPY_LONG_LONG_IDX); } template<> inline SbkConverter *PrimitiveTypeConverter<unsigned char>() { return primitiveTypeConverter(SBK_UNSIGNEDCHAR_IDX); } template<> inline SbkConverter *PrimitiveTypeConverter<unsigned int>() { return primitiveTypeConverter(SBK_UNSIGNEDINT_IDX); } @@ -392,29 +395,26 @@ template<typename T> PyTypeObject *SbkType() { return nullptr; } // Below are the template specializations for C++ primitive types. template<> inline PyTypeObject *SbkType<PY_LONG_LONG>() { return &PyLong_Type; } template<> inline PyTypeObject *SbkType<bool>() { return &PyBool_Type; } -template<> inline PyTypeObject *SbkType<char>() { return &PyInt_Type; } +template<> inline PyTypeObject *SbkType<char>() { return &PyLong_Type; } template<> inline PyTypeObject *SbkType<double>() { return &PyFloat_Type; } template<> inline PyTypeObject *SbkType<float>() { return &PyFloat_Type; } -template<> inline PyTypeObject *SbkType<int>() { return &PyInt_Type; } +template<> inline PyTypeObject *SbkType<int>() { return &PyLong_Type; } template<> inline PyTypeObject *SbkType<long>() { return &PyLong_Type; } -template<> inline PyTypeObject *SbkType<short>() { return &PyInt_Type; } -template<> inline PyTypeObject *SbkType<signed char>() { return &PyInt_Type; } +template<> inline PyTypeObject *SbkType<short>() { return &PyLong_Type; } +template<> inline PyTypeObject *SbkType<signed char>() { return &PyLong_Type; } template<> inline PyTypeObject *SbkType<unsigned PY_LONG_LONG>() { return &PyLong_Type; } -template<> inline PyTypeObject *SbkType<unsigned char>() { return &PyInt_Type; } +template<> inline PyTypeObject *SbkType<unsigned char>() { return &PyLong_Type; } template<> inline PyTypeObject *SbkType<unsigned int>() { return &PyLong_Type; } template<> inline PyTypeObject *SbkType<unsigned long>() { return &PyLong_Type; } -template<> inline PyTypeObject *SbkType<unsigned short>() { return &PyInt_Type; } +template<> inline PyTypeObject *SbkType<unsigned short>() { return &PyLong_Type; } template<> inline PyTypeObject *SbkType<std::nullptr_t>() { return Py_TYPE(&_Py_NoneStruct); } } // namespace Shiboken -// 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)) +#define SbkChar_Check(X) (PyNumber_Check(X) || Shiboken::String::checkChar(X)) struct PySideQFlagsType; -struct PySideQFlagsTypePrivate +struct SbkQFlagsTypePrivate { SbkConverter *converter; }; diff --git a/sources/shiboken6/libshiboken/sbkconverter_p.h b/sources/shiboken6/libshiboken/sbkconverter_p.h index 9e23fa54c..c886c9b9f 100644 --- a/sources/shiboken6/libshiboken/sbkconverter_p.h +++ b/sources/shiboken6/libshiboken/sbkconverter_p.h @@ -1,47 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef SBK_CONVERTER_P_H #define SBK_CONVERTER_P_H #include "sbkpython.h" #include "sbkconverter.h" +#include "sbkcppstring.h" #include "sbkstring.h" #include <limits> #include <typeinfo> @@ -252,7 +217,7 @@ struct IntPrimitive : TwoPrimitive<INT> { static PyObject *toPython(const void *cppIn) { - return PyInt_FromLong(*reinterpret_cast<const INT *>(cppIn)); + return PyLong_FromLong(*reinterpret_cast<const INT *>(cppIn)); } static void toCpp(PyObject *pyIn, void *cppOut) { @@ -277,7 +242,7 @@ struct IntPrimitive : TwoPrimitive<INT> } static PythonToCppFunc isOtherConvertible(PyObject *pyIn) { - if (SbkNumber_Check(pyIn)) + if (PyNumber_Check(pyIn)) return otherToCpp; return nullptr; } @@ -313,12 +278,12 @@ struct Primitive<PY_LONG_LONG> : OnePrimitive<PY_LONG_LONG> { PY_LONG_LONG result = PyLong_AsLongLong(pyIn); if (OverFlowChecker<PY_LONG_LONG>::check(result, pyIn)) - PyErr_SetObject(PyExc_OverflowError, 0); + PyErr_SetObject(PyExc_OverflowError, nullptr); *reinterpret_cast<PY_LONG_LONG * >(cppOut) = result; } static PythonToCppFunc isConvertible(PyObject *pyIn) { - if (SbkNumber_Check(pyIn)) + if (PyNumber_Check(pyIn)) return toCpp; return nullptr; } @@ -345,7 +310,7 @@ struct Primitive<unsigned PY_LONG_LONG> : OnePrimitive<unsigned PY_LONG_LONG> } static PythonToCppFunc isConvertible(PyObject *pyIn) { - if (SbkNumber_Check(pyIn)) + if (PyNumber_Check(pyIn)) return toCpp; return nullptr; } @@ -362,11 +327,11 @@ struct FloatPrimitive : TwoPrimitive<FLOAT> } static void toCpp(PyObject *pyIn, void *cppOut) { - *reinterpret_cast<FLOAT *>(cppOut) = FLOAT(PyLong_AsLong(pyIn)); + *reinterpret_cast<FLOAT *>(cppOut) = FLOAT(PyLong_AsDouble(pyIn)); } static PythonToCppFunc isConvertible(PyObject *pyIn) { - if (PyInt_Check(pyIn) || PyLong_Check(pyIn)) + if (PyLong_Check(pyIn) || PyLong_Check(pyIn)) return toCpp; return nullptr; } @@ -376,7 +341,7 @@ struct FloatPrimitive : TwoPrimitive<FLOAT> } static PythonToCppFunc isOtherConvertible(PyObject *pyIn) { - if (SbkNumber_Check(pyIn)) + if (PyNumber_Check(pyIn)) return otherToCpp; return nullptr; } @@ -395,13 +360,13 @@ struct Primitive<bool> : OnePrimitive<bool> } static PythonToCppFunc isConvertible(PyObject *pyIn) { - if (SbkNumber_Check(pyIn)) + if (PyNumber_Check(pyIn)) return toCpp; return nullptr; } static void toCpp(PyObject *pyIn, void *cppOut) { - *reinterpret_cast<bool *>(cppOut) = PyInt_AS_LONG(pyIn) != 0; + *reinterpret_cast<bool *>(cppOut) = PyLong_AS_LONG(pyIn) != 0; } }; @@ -429,7 +394,7 @@ struct CharPrimitive : IntPrimitive<CHAR> } static PythonToCppFunc isOtherConvertible(PyObject *pyIn) { - if (SbkNumber_Check(pyIn)) + if (PyNumber_Check(pyIn)) return otherToCpp; return nullptr; } @@ -465,7 +430,7 @@ struct Primitive<const char *> : TwoPrimitive<const char *> } static void toCpp(PyObject *, void *cppOut) { - *((const char **)cppOut) = nullptr; + *reinterpret_cast<const char **>(cppOut) = nullptr; } static PythonToCppFunc isConvertible(PyObject *pyIn) { @@ -490,7 +455,7 @@ struct Primitive<std::string> : TwoPrimitive<std::string> { static PyObject *toPython(const void *cppIn) { - return Shiboken::String::fromCString(reinterpret_cast<const std::string *>(cppIn)->c_str()); + return Shiboken::String::fromCppString(*reinterpret_cast<const std::string *>(cppIn)); } static void toCpp(PyObject *, void *cppOut) { @@ -504,7 +469,7 @@ struct Primitive<std::string> : TwoPrimitive<std::string> } static void otherToCpp(PyObject *pyIn, void *cppOut) { - reinterpret_cast<std::string *>(cppOut)->assign(Shiboken::String::toCString(pyIn)); + Shiboken::String::toCppString(pyIn, reinterpret_cast<std::string *>(cppOut)); } static PythonToCppFunc isOtherConvertible(PyObject *pyIn) { @@ -514,9 +479,34 @@ struct Primitive<std::string> : TwoPrimitive<std::string> } }; +template <> +struct Primitive<std::wstring> : TwoPrimitive<std::wstring> +{ + static PyObject *toPython(const void *cppIn) + { + return Shiboken::String::fromCppWString(*reinterpret_cast<const std::wstring *>(cppIn)); + } + static void toCpp(PyObject *, void *cppOut) + { + reinterpret_cast<std::wstring *>(cppOut)->clear(); + } + static PythonToCppFunc isConvertible(PyObject *pyIn) + { + return pyIn == Py_None ? toCpp : nullptr; + } + static void otherToCpp(PyObject *pyIn, void *cppOut) + { + Shiboken::String::toCppWString(pyIn, reinterpret_cast<std::wstring *>(cppOut)); + } + static PythonToCppFunc isOtherConvertible(PyObject *pyIn) + { + return PyUnicode_Check(pyIn) ? otherToCpp : nullptr; + } +}; + // nullptr_t template <> -struct Primitive<std::nullptr_t> : TwoPrimitive<std::nullptr_t> +struct Primitive<std::nullptr_t> : OnePrimitive<std::nullptr_t> { static PyObject *toPython(const void * /* cppIn */) { @@ -532,25 +522,15 @@ struct Primitive<std::nullptr_t> : TwoPrimitive<std::nullptr_t> return toCpp; return nullptr; } - static void otherToCpp(PyObject * /* pyIn */, void *cppOut) - { - *reinterpret_cast<std::nullptr_t *>(cppOut) = nullptr; - } - static PythonToCppFunc isOtherConvertible(PyObject *pyIn) - { - if (pyIn == nullptr) - return otherToCpp; - return nullptr; - } }; -namespace Shiboken { -namespace Conversions { +namespace Shiboken::Conversions { + SbkConverter *createConverterObject(PyTypeObject *type, PythonToCppFunc toCppPointerConvFunc, IsConvertibleToCppFunc toCppPointerCheckFunc, CppToPythonFunc pointerToPythonFunc, CppToPythonFunc copyToPythonFunc); -} // namespace Conversions -} // namespace Shiboken +} // namespace Shiboken::Conversions + #endif // SBK_CONVERTER_P_H diff --git a/sources/shiboken6/libshiboken/sbkcppstring.cpp b/sources/shiboken6/libshiboken/sbkcppstring.cpp new file mode 100644 index 000000000..8e8324f5e --- /dev/null +++ b/sources/shiboken6/libshiboken/sbkcppstring.cpp @@ -0,0 +1,54 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "sbkcppstring.h" +#include "autodecref.h" + +namespace Shiboken::String +{ + +PyObject *fromCppString(const std::string &value) +{ + return PyUnicode_FromStringAndSize(value.data(), value.size()); +} + +PyObject *fromCppStringView(std::string_view value) +{ + return PyUnicode_FromStringAndSize(value.data(), value.size()); +} + +PyObject *fromCppWString(const std::wstring &value) +{ + return PyUnicode_FromWideChar(value.data(), value.size()); +} + +void toCppString(PyObject *str, std::string *value) +{ + value->clear(); + + if (str == Py_None) + return; + + if (PyUnicode_Check(str)) { + if (PyUnicode_GetLength(str) > 0) + value->assign(_PepUnicode_AsString(str)); + return; + } + + if (PyBytes_Check(str)) + value->assign(PyBytes_AS_STRING(str)); +} + +void toCppWString(PyObject *str, std::wstring *value) +{ + value->clear(); + + if (str == Py_None || PyUnicode_Check(str) == 0 || PyUnicode_GetLength(str) == 0) + return; + + wchar_t *w = PyUnicode_AsWideCharString(str, nullptr); + value->assign(w); + PyMem_Free(w); +} + +} // namespace Shiboken::String diff --git a/sources/shiboken6/libshiboken/sbkcppstring.h b/sources/shiboken6/libshiboken/sbkcppstring.h new file mode 100644 index 000000000..7ffe11c75 --- /dev/null +++ b/sources/shiboken6/libshiboken/sbkcppstring.h @@ -0,0 +1,22 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef SBKCPPSTRING_H +#define SBKCPPSTRING_H + +#include "sbkpython.h" +#include "shibokenmacros.h" + +#include <string> +#include <string_view> + +namespace Shiboken::String +{ + LIBSHIBOKEN_API PyObject *fromCppString(const std::string &value); + LIBSHIBOKEN_API PyObject *fromCppStringView(std::string_view value); + LIBSHIBOKEN_API PyObject *fromCppWString(const std::wstring &value); + LIBSHIBOKEN_API void toCppString(PyObject *str, std::string *value); + LIBSHIBOKEN_API void toCppWString(PyObject *str, std::wstring *value); +} // namespace Shiboken::String + +#endif // SBKCPPSTRING_H diff --git a/sources/shiboken6/libshiboken/sbkcpptonumpy.cpp b/sources/shiboken6/libshiboken/sbkcpptonumpy.cpp new file mode 100644 index 000000000..44e900f01 --- /dev/null +++ b/sources/shiboken6/libshiboken/sbkcpptonumpy.cpp @@ -0,0 +1,67 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// included by sbknumpy.cpp + +namespace Shiboken::Numpy { + +#ifdef HAVE_NUMPY + +// Helper to create a 1-dimensional numpy array +template <class Type> +static PyObject *_createArray1(Py_ssize_t size, int numpyType, const Type *data) +{ + const npy_intp dims[1] = {size}; + PyObject *result = PyArray_EMPTY(1, dims, numpyType, 0); + auto *array = reinterpret_cast<PyArrayObject *>(result); + auto *rawTargetData = PyArray_DATA(array); + auto *targetData = reinterpret_cast<Type *>(rawTargetData); + std::copy(data, data + size, targetData); + return result; +} + +PyObject *createByteArray1(Py_ssize_t size, const uint8_t *data) +{ + return _createArray1(size, NPY_BYTE, data); +} + +PyObject *createDoubleArray1(Py_ssize_t size, const double *data) +{ + return _createArray1(size, NPY_DOUBLE, data); +} + +PyObject *createFloatArray1(Py_ssize_t size, const float *data) +{ + return _createArray1(size, NPY_FLOAT, data); +} + +PyObject *createIntArray1(Py_ssize_t size, const int *data) +{ + return _createArray1(size, NPY_INT, data); +} + +#else // HAVE_NUMPY + +PyObject *createByteArray1(Py_ssize_t, const uint8_t *) +{ + return Py_None; +} + +PyObject *createDoubleArray1(Py_ssize_t, const double *) +{ + return Py_None; +} + +PyObject *createFloatArray1(Py_ssize_t, const float *) +{ + return Py_None; +} + +PyObject *createIntArray1(Py_ssize_t, const int *) +{ + return Py_None; +} + +#endif // !HAVE_NUMPY + +} //namespace Shiboken::Numpy diff --git a/sources/shiboken6/libshiboken/sbkcpptonumpy.h b/sources/shiboken6/libshiboken/sbkcpptonumpy.h new file mode 100644 index 000000000..8b9b0cfd2 --- /dev/null +++ b/sources/shiboken6/libshiboken/sbkcpptonumpy.h @@ -0,0 +1,41 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef SBKCPPTONUMPY_H +#define SBKCPPTONUMPY_H + +#include <sbkpython.h> +#include <shibokenmacros.h> + +#include <cstdint> + +namespace Shiboken::Numpy +{ + +/// Create a one-dimensional numpy array of type uint8/NPY_BYTE +/// \param size Size +/// \param data Data +/// \return PyArrayObject +LIBSHIBOKEN_API PyObject *createByteArray1(Py_ssize_t size, const uint8_t *data); + +/// Create a one-dimensional numpy array of type double/NPY_DOUBLE +/// \param size Size +/// \param data Data +/// \return PyArrayObject +LIBSHIBOKEN_API PyObject *createDoubleArray1(Py_ssize_t size, const double *data); + +/// Create a one-dimensional numpy array of type float/NPY_FLOAT +/// \param size Size +/// \param data Data +/// \return PyArrayObject +LIBSHIBOKEN_API PyObject *createFloatArray1(Py_ssize_t size, const float *data); + +/// Create a one-dimensional numpy array of type int/NPY_INT +/// \param size Size +/// \param data Data +/// \return PyArrayObject +LIBSHIBOKEN_API PyObject *createIntArray1(Py_ssize_t size, const int *data); + +} //namespace Shiboken::Numpy + +#endif // SBKCPPTONUMPY_H diff --git a/sources/shiboken6/libshiboken/sbkenum.cpp b/sources/shiboken6/libshiboken/sbkenum.cpp index b7478cb78..d39369979 100644 --- a/sources/shiboken6/libshiboken/sbkenum.cpp +++ b/sources/shiboken6/libshiboken/sbkenum.cpp @@ -1,44 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "sbkenum.h" -#include "sbkenum_p.h" #include "sbkstring.h" #include "sbkstaticstrings.h" #include "sbkstaticstrings_p.h" @@ -50,11 +13,9 @@ #include <cstring> #include <vector> +#include <sstream> -#define SbkEnumType_Check(o) (Py_TYPE(Py_TYPE(o)) == SbkEnumType_TypeF()) -using enum_func = PyObject *(*)(PyObject *, PyObject *); - -static void cleanupEnumTypes(); +using namespace Shiboken; extern "C" { @@ -64,705 +25,428 @@ struct SbkEnumType PyTypeObject type; }; -struct SbkEnumObject -{ - PyObject_HEAD - long ob_value; - PyObject *ob_name; -}; - -static PyTypeObject *SbkEnum_TypeF(); // forward - -static PyObject *SbkEnumObject_repr(PyObject *self) +// Initialization +static bool _init_enum() { - const SbkEnumObject *enumObj = reinterpret_cast<SbkEnumObject *>(self); - auto name = Py_TYPE(self)->tp_name; - if (enumObj->ob_name) { - return Shiboken::String::fromFormat("%s.%s", name, - PyBytes_AS_STRING(enumObj->ob_name)); + AutoDecRef shibo(PyImport_ImportModule("shiboken6.Shiboken")); + return !shibo.isNull(); +} + +static PyObject *PyEnumModule{}; +static PyObject *PyEnumMeta{}; +static PyObject *PyEnum{}; +static PyObject *PyIntEnum{}; +static PyObject *PyFlag{}; +static PyObject *PyIntFlag{}; +static PyObject *PyFlag_KEEP{}; + +bool PyEnumMeta_Check(PyObject *ob) +{ + return Py_TYPE(ob) == reinterpret_cast<PyTypeObject *>(PyEnumMeta); +} + +PyTypeObject *getPyEnumMeta() +{ + if (PyEnumMeta) + return reinterpret_cast<PyTypeObject *>(PyEnumMeta); + + static auto *mod = PyImport_ImportModule("enum"); + if (mod) { + PyEnumModule = mod; + PyEnumMeta = PyObject_GetAttrString(mod, "EnumMeta"); + if (PyEnumMeta && PyType_Check(PyEnumMeta)) + PyEnum = PyObject_GetAttrString(mod, "Enum"); + if (PyEnum && PyType_Check(PyEnum)) + PyIntEnum = PyObject_GetAttrString(mod, "IntEnum"); + if (PyIntEnum && PyType_Check(PyIntEnum)) + PyFlag = PyObject_GetAttrString(mod, "Flag"); + if (PyFlag && PyType_Check(PyFlag)) + PyIntFlag = PyObject_GetAttrString(mod, "IntFlag"); + if (PyIntFlag && PyType_Check(PyIntFlag)) { + // KEEP is defined from Python 3.11 on. + PyFlag_KEEP = PyObject_GetAttrString(mod, "KEEP"); + PyErr_Clear(); + return reinterpret_cast<PyTypeObject *>(PyEnumMeta); + } } - return Shiboken::String::fromFormat("%s(%ld)", name, enumObj->ob_value); -} - -static PyObject *SbkEnumObject_name(PyObject *self, void *) -{ - auto *enum_self = reinterpret_cast<SbkEnumObject *>(self); - - if (enum_self->ob_name == nullptr) - Py_RETURN_NONE; - - Py_INCREF(enum_self->ob_name); - return enum_self->ob_name; + Py_FatalError("Python module 'enum' not found"); + return nullptr; } -static PyObject *SbkEnum_tp_new(PyTypeObject *type, PyObject *args, PyObject *) +void init_enum() { - long itemValue = 0; - if (!PyArg_ParseTuple(args, "|l:__new__", &itemValue)) - return nullptr; - - if (type == SbkEnum_TypeF()) { - PyErr_Format(PyExc_TypeError, "You cannot use %s directly", type->tp_name); - return nullptr; + static bool isInitialized = false; + if (isInitialized) + return; + if (!(isInitialized || _init_enum())) + Py_FatalError("could not init enum"); + + // PYSIDE-1735: Determine whether we should use the old or the new enum implementation. + static PyObject *option = PySys_GetObject("pyside6_option_python_enum"); + if (!option || !PyLong_Check(option)) { + PyErr_Clear(); + option = PyLong_FromLong(1); } - - SbkEnumObject *self = PyObject_New(SbkEnumObject, type); - if (!self) - return nullptr; - self->ob_value = itemValue; - Shiboken::AutoDecRef item(Shiboken::Enum::getEnumItemFromValue(type, itemValue)); - self->ob_name = item.object() ? SbkEnumObject_name(item, nullptr) : nullptr; - return reinterpret_cast<PyObject *>(self); + int ignoreOver{}; + Enum::enumOption = PyLong_AsLongAndOverflow(option, &ignoreOver); + getPyEnumMeta(); + isInitialized = true; } -static const char *SbkEnum_SignatureStrings[] = { - "Shiboken.Enum(self,itemValue:int=0)", - nullptr}; // Sentinel - -void enum_object_dealloc(PyObject *ob) +// PYSIDE-1735: Helper function supporting QEnum +int enumIsFlag(PyObject *ob_type) { - auto *self = reinterpret_cast<SbkEnumObject *>(ob); - Py_XDECREF(self->ob_name); - Sbk_object_dealloc(ob); -} - -static PyObject *_enum_op(enum_func f, PyObject *a, PyObject *b) { - PyObject *valA = a; - PyObject *valB = b; - PyObject *result = nullptr; - bool enumA = false; - bool enumB = false; - - // We are not allowing floats - if (!PyFloat_Check(valA) && !PyFloat_Check(valB)) { - // Check if both variables are SbkEnumObject - if (SbkEnumType_Check(valA)) { - valA = PyLong_FromLong(reinterpret_cast<SbkEnumObject *>(valA)->ob_value); - enumA = true; - } - if (SbkEnumType_Check(valB)) { - valB = PyLong_FromLong(reinterpret_cast<SbkEnumObject *>(valB)->ob_value); - enumB = true; - } - } + init_enum(); - // Without an enum we are not supporting the operation - if (!(enumA || enumB)) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; + auto *metatype = Py_TYPE(ob_type); + if (metatype != reinterpret_cast<PyTypeObject *>(PyEnumMeta)) + return -1; + auto *mro = reinterpret_cast<PyTypeObject *>(ob_type)->tp_mro; + const Py_ssize_t n = PyTuple_GET_SIZE(mro); + for (Py_ssize_t idx = 0; idx < n; ++idx) { + auto *sub_type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx)); + if (sub_type == reinterpret_cast<PyTypeObject *>(PyFlag)) + return 1; } - - result = f(valA, valB); - - // Decreasing the reference of the used variables a and b. - if (enumA) - Py_DECREF(valA); - if (enumB) - Py_DECREF(valB); - return result; -} - -/* 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(reinterpret_cast<SbkEnumObject *>(v)->ob_value); -} - -static PyObject *enum_and(PyObject *self, PyObject *b) -{ - return _enum_op(PyNumber_And, self, b); -} - -static PyObject *enum_or(PyObject *self, PyObject *b) -{ - return _enum_op(PyNumber_Or, self, b); -} - -static PyObject *enum_xor(PyObject *self, PyObject *b) -{ - return _enum_op(PyNumber_Xor, self, b); + return 0; } -static int enum_bool(PyObject *v) -{ - return (reinterpret_cast<SbkEnumObject *>(v)->ob_value > 0); -} - -static PyObject *enum_add(PyObject *self, PyObject *v) -{ - return _enum_op(PyNumber_Add, self, v); -} - -static PyObject *enum_subtract(PyObject *self, PyObject *v) -{ - return _enum_op(PyNumber_Subtract, self, v); -} +/////////////////////////////////////////////////////////////////////// +// +// Support for Missing Values +// ========================== +// +// Qt enums sometimes use undefined values in enums. +// The enum module handles this by the option "KEEP" for Flag and +// IntFlag. The handling of missing enum values is still strict. +// +// We changed that (also for compatibility with some competitor) +// and provide a `_missing_` function that creates the missing value. +// +// The idea: +// --------- +// We cannot modify the already created class. +// But we can create a one-element class with the new value and +// pretend that this is the already existing class. +// +// We create each constant only once and keep the result in a dict +// "_sbk_missing_". This is similar to a competitor's "_sip_missing_". +// -static PyObject *enum_multiply(PyObject *self, PyObject *v) +static PyObject *missing_func(PyObject * /* self */ , PyObject *args) { - return _enum_op(PyNumber_Multiply, self, v); -} + // In order to relax matters to be more compatible with C++, we need + // to create a pseudo-member with that value. + static auto *const _sbk_missing = Shiboken::String::createStaticString("_sbk_missing_"); + static auto *const _name = Shiboken::String::createStaticString("__name__"); + static auto *const _mro = Shiboken::String::createStaticString("__mro__"); + static auto *const _class = Shiboken::String::createStaticString("__class__"); -static PyObject *enum_richcompare(PyObject *self, PyObject *other, int op) -{ - PyObject *valA = self; - PyObject *valB = other; - PyObject *result = nullptr; - bool enumA = false; - bool enumB = false; - - // We are not allowing floats - if (!PyFloat_Check(valA) && !PyFloat_Check(valB)) { - - // Check if both variables are SbkEnumObject - if (SbkEnumType_Check(valA)) { - valA = PyLong_FromLong(reinterpret_cast<SbkEnumObject *>(valA)->ob_value); - enumA = true; - } - if (SbkEnumType_Check(valB)) { - valB = PyLong_FromLong(reinterpret_cast<SbkEnumObject *>(valB)->ob_value); - enumB =true; - } + PyObject *klass{}, *value{}; + if (!PyArg_UnpackTuple(args, "missing", 2, 2, &klass, &value)) + Py_RETURN_NONE; + if (!PyLong_Check(value)) + Py_RETURN_NONE; + auto *type = reinterpret_cast<PyTypeObject *>(klass); + AutoDecRef tpDict(PepType_GetDict(type)); + auto *sbk_missing = PyDict_GetItem(tpDict.object(), _sbk_missing); + if (!sbk_missing) { + sbk_missing = PyDict_New(); + PyDict_SetItem(tpDict.object(), _sbk_missing, sbk_missing); } - - // Without an enum we are not supporting the operation - if (!(enumA || enumB)) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; + // See if the value is already in the dict. + AutoDecRef val_str(PyObject_CallMethod(value, "__str__", nullptr)); + auto *ret = PyDict_GetItem(sbk_missing, val_str); + if (ret) { + Py_INCREF(ret); + return ret; } - result = PyObject_RichCompare(valA, valB, op); - - // Decreasing the reference of the used variables a and b. - if (enumA) - Py_DECREF(valA); - if (enumB) - Py_DECREF(valB); - - return result; -} - -static Py_hash_t enum_hash(PyObject *pyObj) -{ - Py_hash_t val = reinterpret_cast<SbkEnumObject *>(pyObj)->ob_value; - if (val == -1) - val = -2; - return val; -} - -static PyGetSetDef SbkEnumGetSetList[] = { - {const_cast<char *>("name"), SbkEnumObject_name, nullptr, nullptr, nullptr}, - {nullptr, nullptr, nullptr, nullptr, nullptr} // Sentinel + // No, we must create a new object and insert it into the dict. + AutoDecRef cls_name(PyObject_GetAttr(klass, _name)); + AutoDecRef mro(PyObject_GetAttr(klass, _mro)); + auto *baseClass(PyTuple_GetItem(mro, 1)); + AutoDecRef param(PyDict_New()); + PyDict_SetItem(param, val_str, value); + AutoDecRef fake(PyObject_CallFunctionObjArgs(baseClass, cls_name.object(), param.object(), + nullptr)); + ret = PyObject_GetAttr(fake, val_str); + PyDict_SetItem(sbk_missing, val_str, ret); + // Now the real fake: Pretend that the type is our original type! + PyObject_SetAttr(ret, _class, klass); + return ret; +} + +static struct PyMethodDef dummy_methods[] = { + {"_missing_", reinterpret_cast<PyCFunction>(missing_func), METH_VARARGS|METH_STATIC, nullptr}, + {nullptr, nullptr, 0, nullptr} }; -static void SbkEnumTypeDealloc(PyObject *pyObj); -static PyObject *SbkEnumTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *kwds); - -static PyType_Slot SbkEnumType_Type_slots[] = { - {Py_tp_dealloc, reinterpret_cast<void *>(SbkEnumTypeDealloc)}, +static PyType_Slot dummy_slots[] = { {Py_tp_base, reinterpret_cast<void *>(&PyType_Type)}, - {Py_tp_alloc, reinterpret_cast<void *>(PyType_GenericAlloc)}, - {Py_tp_new, reinterpret_cast<void *>(SbkEnumTypeTpNew)}, - {Py_tp_free, reinterpret_cast<void *>(PyObject_GC_Del)}, + {Py_tp_methods, reinterpret_cast<void *>(dummy_methods)}, {0, nullptr} }; -static PyType_Spec SbkEnumType_Type_spec = { - "1:Shiboken.EnumMeta", + +static PyType_Spec dummy_spec = { + "1:builtins.EnumType", 0, - sizeof(PyMemberDef), - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, - SbkEnumType_Type_slots, + 0, + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + dummy_slots, }; -PyTypeObject *SbkEnumType_TypeF(void) +static PyObject *create_missing_func(PyObject *klass) { - static PyObject *obType = SbkType_FromSpec(&SbkEnumType_Type_spec); - return reinterpret_cast<PyTypeObject *>(obType); -} - -void SbkEnumTypeDealloc(PyObject *pyObj) -{ - auto *enumType = reinterpret_cast<SbkEnumType *>(pyObj); - auto *setp = PepType_SETP(enumType); - - PyObject_GC_UnTrack(pyObj); -#ifndef Py_LIMITED_API - Py_TRASHCAN_SAFE_BEGIN(pyObj); -#endif - if (setp->converter) - Shiboken::Conversions::deleteConverter(setp->converter); - PepType_SETP_delete(enumType); -#ifndef Py_LIMITED_API - Py_TRASHCAN_SAFE_END(pyObj); -#endif - if (PepRuntime_38_flag) { - // PYSIDE-939: Handling references correctly. - // This was not needed before Python 3.8 (Python issue 35810) - Py_DECREF(Py_TYPE(pyObj)); - } -} - -PyObject *SbkEnumTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *kwds) -{ - auto type_new = reinterpret_cast<newfunc>(PyType_GetSlot(&PyType_Type, Py_tp_new)); - auto newType = reinterpret_cast<SbkEnumType *>(type_new(metatype, args, kwds)); - if (!newType) - return nullptr; - return reinterpret_cast<PyObject *>(newType); + // When creating the class, memorize it in the missing function by + // a partial function argument. + static auto *const type = SbkType_FromSpec(&dummy_spec); + static auto *const obType = reinterpret_cast<PyObject *>(type); + static auto *const _missing = Shiboken::String::createStaticString("_missing_"); + static auto *const func = PyObject_GetAttr(obType, _missing); + static auto *const partial = Pep_GetPartialFunction(); + return PyObject_CallFunctionObjArgs(partial, func, klass, nullptr); } +// +//////////////////////////////////////////////////////////////////////// } // extern "C" -/////////////////////////////////////////////////////////////// -// -// PYSIDE-15: Pickling Support for Qt Enum objects -// This works very well and fixes the issue. -// -extern "C" { +namespace Shiboken::Enum { -static PyObject *enum_unpickler = nullptr; +int enumOption{}; -// Pickling: reduce the Qt Enum object -static PyObject *enum___reduce__(PyObject *obj) +bool check(PyObject *pyObj) { init_enum(); - return Py_BuildValue("O(Ni)", - enum_unpickler, - Py_BuildValue("s", Py_TYPE(obj)->tp_name), - PyInt_AS_LONG(obj)); -} - -} // extern "C" -namespace Shiboken { namespace Enum { + static PyTypeObject *meta = getPyEnumMeta(); + return Py_TYPE(Py_TYPE(pyObj)) == reinterpret_cast<PyTypeObject *>(meta); +} -// Unpickling: rebuild the Qt Enum object -PyObject *unpickleEnum(PyObject *enum_class_name, PyObject *value) +PyObject *getEnumItemFromValue(PyTypeObject *enumType, EnumValueType itemValue) { - Shiboken::AutoDecRef parts(PyObject_CallMethod(enum_class_name, - "split", "s", ".")); - if (parts.isNull()) - return nullptr; - PyObject *top_name = PyList_GetItem(parts, 0); // borrowed ref - if (top_name == nullptr) - return nullptr; - PyObject *module = PyImport_GetModule(top_name); - if (module == nullptr) { - PyErr_Format(PyExc_ImportError, "could not import module %.200s", - Shiboken::String::toCString(top_name)); + init_enum(); + + auto *obEnumType = reinterpret_cast<PyObject *>(enumType); + AutoDecRef val2members(PyObject_GetAttrString(obEnumType, "_value2member_map_")); + if (val2members.isNull()) { + PyErr_Clear(); return nullptr; } - Shiboken::AutoDecRef cur_thing(module); - int len = PyList_Size(parts); - for (int idx = 1; idx < len; ++idx) { - PyObject *name = PyList_GetItem(parts, idx); // borrowed ref - PyObject *thing = PyObject_GetAttr(cur_thing, name); - if (thing == nullptr) { - PyErr_Format(PyExc_ImportError, "could not import Qt Enum type %.200s", - Shiboken::String::toCString(enum_class_name)); - return nullptr; - } - cur_thing.reset(thing); - } - PyObject *klass = cur_thing; - return PyObject_CallFunctionObjArgs(klass, value, nullptr); -} - -} // namespace Enum -} // namespace Shiboken - -extern "C" { - -// Initialization -static bool _init_enum() -{ - Shiboken::AutoDecRef shibo(PyImport_ImportModule("shiboken6.Shiboken")); - auto mod = shibo.object(); - // publish Shiboken.Enum so that the signature gets initialized - if (PyObject_SetAttrString(mod, "Enum", reinterpret_cast<PyObject *>(SbkEnum_TypeF())) < 0) - return false; - if (InitSignatureStrings(SbkEnum_TypeF(), SbkEnum_SignatureStrings) < 0) - return false; - enum_unpickler = PyObject_GetAttrString(mod, "_unpickle_enum"); - if (enum_unpickler == nullptr) - return false; - return true; + AutoDecRef ob_value(PyLong_FromLongLong(itemValue)); + auto *result = PyDict_GetItem(val2members, ob_value); + Py_XINCREF(result); + return result; } -void init_enum() +PyObject *newItem(PyTypeObject *enumType, EnumValueType itemValue, + const char *itemName) { - static bool is_initialized = false; - if (!(is_initialized || enum_unpickler || _init_enum())) - Py_FatalError("could not load enum pickling helper function"); - Py_AtExit(cleanupEnumTypes); - is_initialized = true; -} - -static PyMethodDef SbkEnumObject_Methods[] = { - {"__reduce__", reinterpret_cast<PyCFunction>(enum___reduce__), - METH_NOARGS, nullptr}, - {nullptr, nullptr, 0, nullptr} // Sentinel -}; - -} // extern "C" + init_enum(); -// -/////////////////////////////////////////////////////////////// + auto *obEnumType = reinterpret_cast<PyObject *>(enumType); + if (!itemName) + return PyObject_CallFunction(obEnumType, "L", itemValue); -namespace Shiboken { + static PyObject *const _member_map_ = String::createStaticString("_member_map_"); + AutoDecRef tpDict(PepType_GetDict(enumType)); + auto *member_map = PyDict_GetItem(tpDict.object(), _member_map_); + if (!(member_map && PyDict_Check(member_map))) + return nullptr; + auto *result = PyDict_GetItemString(member_map, itemName); + Py_XINCREF(result); + return result; +} -class DeclaredEnumTypes +EnumValueType getValue(PyObject *enumItem) { -public: - struct EnumEntry - { - char *name; // full name as allocated. type->tp_name might be a substring. - PyTypeObject *type; - }; - - DeclaredEnumTypes(const DeclaredEnumTypes &) = delete; - DeclaredEnumTypes(DeclaredEnumTypes &&) = delete; - DeclaredEnumTypes &operator=(const DeclaredEnumTypes &) = delete; - DeclaredEnumTypes &operator=(DeclaredEnumTypes &&) = delete; - - DeclaredEnumTypes(); - ~DeclaredEnumTypes(); - static DeclaredEnumTypes &instance(); - void addEnumType(const EnumEntry &e) { m_enumTypes.push_back(e); } - - void cleanup(); - -private: - std::vector<EnumEntry> m_enumTypes; -}; + init_enum(); -namespace Enum { + assert(Enum::check(enumItem)); -bool check(PyObject *pyObj) -{ - return Py_TYPE(Py_TYPE(pyObj)) == SbkEnumType_TypeF(); + AutoDecRef pyValue(PyObject_GetAttrString(enumItem, "value")); + return PyLong_AsLongLong(pyValue); } -PyObject *getEnumItemFromValue(PyTypeObject *enumType, long itemValue) +void setTypeConverter(PyTypeObject *type, SbkConverter *converter) { - PyObject *key, *value; - Py_ssize_t pos = 0; - PyObject *values = PyDict_GetItem(enumType->tp_dict, Shiboken::PyName::values()); - - while (PyDict_Next(values, &pos, &key, &value)) { - auto *obj = reinterpret_cast<SbkEnumObject *>(value); - if (obj->ob_value == itemValue) { - Py_INCREF(value); - return value; - } - } - return nullptr; + auto *enumType = reinterpret_cast<SbkEnumType *>(type); + PepType_SETP(enumType)->converter = converter; } -static PyTypeObject *createEnum(const char *fullName, const char *cppName, - PyTypeObject *flagsType) +static PyTypeObject *createEnumForPython(PyObject *scopeOrModule, + const char *fullName, + PyObject *pyEnumItems) { - PyTypeObject *enumType = newTypeWithName(fullName, cppName, flagsType); - if (PyType_Ready(enumType) < 0) { - Py_XDECREF(enumType); - return nullptr; - } - return enumType; -} + const char *colon = strchr(fullName, ':'); + assert(colon); + int package_level = atoi(fullName); + const char *mod = colon + 1; -PyTypeObject *createGlobalEnum(PyObject *module, const char *name, const char *fullName, const char *cppName, PyTypeObject *flagsType) -{ - PyTypeObject *enumType = createEnum(fullName, cppName, flagsType); - if (enumType && PyModule_AddObject(module, name, reinterpret_cast<PyObject *>(enumType)) < 0) { - Py_DECREF(enumType); - return nullptr; + const char *qual = mod; + for (int idx = package_level; idx > 0; --idx) { + const char *dot = strchr(qual, '.'); + if (!dot) + break; + qual = dot + 1; } - if (flagsType && PyModule_AddObject(module, PepType_GetNameStr(flagsType), - reinterpret_cast<PyObject *>(flagsType)) < 0) { - Py_DECREF(enumType); - return nullptr; + int mlen = qual - mod - 1; + AutoDecRef module(Shiboken::String::fromCString(mod, mlen)); + AutoDecRef qualname(Shiboken::String::fromCString(qual)); + const char *dot = strrchr(qual, '.'); + AutoDecRef name(Shiboken::String::fromCString(dot ? dot + 1 : qual)); + + static PyObject *enumName = String::createStaticString("IntEnum"); + if (PyType_Check(scopeOrModule)) { + // For global objects, we have no good solution, yet where to put the int info. + auto type = reinterpret_cast<PyTypeObject *>(scopeOrModule); + auto *sotp = PepType_SOTP(type); + if (!sotp->enumFlagsDict) + initEnumFlagsDict(type); + enumName = PyDict_GetItem(sotp->enumTypeDict, name); } - return enumType; -} -PyTypeObject *createScopedEnum(SbkObjectType *scope, const char *name, const char *fullName, const char *cppName, PyTypeObject *flagsType) -{ - PyTypeObject *enumType = createEnum(fullName, cppName, flagsType); - if (enumType && PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(scope)->tp_dict, name, - reinterpret_cast<PyObject *>(enumType)) < 0) { - Py_DECREF(enumType); - return nullptr; - } - if (flagsType && PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(scope)->tp_dict, - PepType_GetNameStr(flagsType), - reinterpret_cast<PyObject *>(flagsType)) < 0) { - Py_DECREF(enumType); - return nullptr; - } - return enumType; -} + AutoDecRef PyEnumType(PyObject_GetAttr(PyEnumModule, enumName)); + assert(PyEnumType.object()); + bool isFlag = PyObject_IsSubclass(PyEnumType, PyFlag); -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) { - Py_DECREF(enumItem); - return nullptr; + // See if we should use the Int versions of the types, again + bool useIntInheritance = Enum::enumOption & Enum::ENOPT_INHERIT_INT; + if (useIntInheritance) { + auto *surrogate = PyObject_IsSubclass(PyEnumType, PyFlag) ? PyIntFlag : PyIntEnum; + Py_INCREF(surrogate); + PyEnumType.reset(surrogate); } - return enumItem; -} -bool createGlobalEnumItem(PyTypeObject *enumType, PyObject *module, const char *itemName, long itemValue) -{ - PyObject *enumItem = createEnumItem(enumType, itemName, itemValue); - if (!enumItem) - return false; - int ok = PyModule_AddObject(module, itemName, enumItem); - Py_DECREF(enumItem); - return ok >= 0; -} - -bool createScopedEnumItem(PyTypeObject *enumType, PyTypeObject *scope, - const char *itemName, long itemValue) -{ - PyObject *enumItem = createEnumItem(enumType, itemName, itemValue); - if (!enumItem) - return false; - int ok = PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(scope)->tp_dict, itemName, enumItem); - Py_DECREF(enumItem); - return ok >= 0; -} - -bool createScopedEnumItem(PyTypeObject *enumType, SbkObjectType *scope, const char *itemName, long itemValue) -{ - return createScopedEnumItem(enumType, reinterpret_cast<PyTypeObject *>(scope), itemName, itemValue); -} + // Walk the enumItemStrings and create a Python enum type. + auto *pyName = name.object(); + + // We now create the new type. Since Python 3.11, we need to pass in + // `boundary=KEEP` because the default STRICT crashes on us. + // See QDir.Filter.Drives | QDir.Filter.Files + AutoDecRef callArgs(Py_BuildValue("(OO)", pyName, pyEnumItems)); + AutoDecRef callDict(PyDict_New()); + static PyObject *boundary = String::createStaticString("boundary"); + if (PyFlag_KEEP) + PyDict_SetItem(callDict, boundary, PyFlag_KEEP); + auto *obNewType = PyObject_Call(PyEnumType, callArgs, callDict); + if (!obNewType || PyObject_SetAttr(scopeOrModule, pyName, obNewType) < 0) + return nullptr; -PyObject * -newItem(PyTypeObject *enumType, long itemValue, const char *itemName) -{ - bool newValue = true; - SbkEnumObject *enumObj; - if (!itemName) { - enumObj = reinterpret_cast<SbkEnumObject *>( - getEnumItemFromValue(enumType, itemValue)); - if (enumObj) - return reinterpret_cast<PyObject *>(enumObj); - - newValue = false; + // For compatibility with Qt enums, provide a permissive missing method for (Int)?Enum. + if (!isFlag) { + bool supportMissing = !(Enum::enumOption & Enum::ENOPT_NO_MISSING); + if (supportMissing) { + AutoDecRef enum_missing(create_missing_func(obNewType)); + PyObject_SetAttrString(obNewType, "_missing_", enum_missing); + } } - enumObj = PyObject_New(SbkEnumObject, enumType); - if (!enumObj) - return nullptr; - - enumObj->ob_name = itemName ? PyBytes_FromString(itemName) : nullptr; - enumObj->ob_value = itemValue; - - if (newValue) { - auto dict = enumType->tp_dict; // Note: 'values' is borrowed - PyObject *values = PyDict_GetItemWithError(dict, Shiboken::PyName::values()); - if (values == nullptr) { - if (PyErr_Occurred()) - return nullptr; - Shiboken::AutoDecRef new_values(values = PyDict_New()); - if (values == nullptr) - return nullptr; - if (PyDict_SetItem(dict, Shiboken::PyName::values(), values) < 0) - return nullptr; + auto *newType = reinterpret_cast<PyTypeObject *>(obNewType); + PyObject_SetAttr(obNewType, PyMagicName::qualname(), qualname); + PyObject_SetAttr(obNewType, PyMagicName::module(), module); + + // See if we should re-introduce shortcuts in the enclosing object. + const bool useGlobalShortcut = (Enum::enumOption & Enum::ENOPT_GLOBAL_SHORTCUT) != 0; + const bool useScopedShortcut = (Enum::enumOption & Enum::ENOPT_SCOPED_SHORTCUT) != 0; + if (useGlobalShortcut || useScopedShortcut) { + // We have to use the iterator protokol because the values dict is a mappingproxy. + AutoDecRef values(PyObject_GetAttr(obNewType, PyMagicName::members())); + AutoDecRef mapIterator(PyObject_GetIter(values)); + AutoDecRef mapKey{}; + bool isModule = PyModule_Check(scopeOrModule); + while ((mapKey.reset(PyIter_Next(mapIterator))), mapKey.object()) { + if ((useGlobalShortcut && isModule) || (useScopedShortcut && !isModule)) { + AutoDecRef value(PyObject_GetItem(values, mapKey)); + if (PyObject_SetAttr(scopeOrModule, mapKey, value) < 0) + return nullptr; + } } - PyDict_SetItemString(values, itemName, reinterpret_cast<PyObject *>(enumObj)); } - return reinterpret_cast<PyObject *>(enumObj); + return newType; } -} // namespace Shiboken -} // namespace Enum - -static PyType_Slot SbkNewEnum_slots[] = { - {Py_tp_repr, reinterpret_cast<void *>(SbkEnumObject_repr)}, - {Py_tp_str, reinterpret_cast<void *>(SbkEnumObject_repr)}, - {Py_tp_getset, reinterpret_cast<void *>(SbkEnumGetSetList)}, - {Py_tp_methods, reinterpret_cast<void *>(SbkEnumObject_Methods)}, - {Py_tp_new, reinterpret_cast<void *>(SbkEnum_tp_new)}, - {Py_nb_add, reinterpret_cast<void *>(enum_add)}, - {Py_nb_subtract, reinterpret_cast<void *>(enum_subtract)}, - {Py_nb_multiply, reinterpret_cast<void *>(enum_multiply)}, - {Py_nb_positive, reinterpret_cast<void *>(enum_int)}, - {Py_nb_bool, reinterpret_cast<void *>(enum_bool)}, - {Py_nb_and, reinterpret_cast<void *>(enum_and)}, - {Py_nb_xor, reinterpret_cast<void *>(enum_xor)}, - {Py_nb_or, reinterpret_cast<void *>(enum_or)}, - {Py_nb_int, reinterpret_cast<void *>(enum_int)}, - {Py_nb_index, reinterpret_cast<void *>(enum_int)}, - {Py_tp_richcompare, reinterpret_cast<void *>(enum_richcompare)}, - {Py_tp_hash, reinterpret_cast<void *>(enum_hash)}, - {Py_tp_dealloc, reinterpret_cast<void *>(enum_object_dealloc)}, - {0, nullptr} -}; -static PyType_Spec SbkNewEnum_spec = { - "1:Shiboken.Enum", - sizeof(SbkEnumObject), - 0, - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, - SbkNewEnum_slots, -}; - -static PyTypeObject *SbkEnum_TypeF() +template <typename IntT> +static PyObject *toPyObject(IntT v) { - static auto type = SbkType_FromSpec(&SbkNewEnum_spec); - return reinterpret_cast<PyTypeObject *>(type); + if constexpr (sizeof(IntT) == 8) { + if constexpr (std::is_unsigned_v<IntT>) + return PyLong_FromUnsignedLongLong(v); + return PyLong_FromLongLong(v); + } + if constexpr (std::is_unsigned_v<IntT>) + return PyLong_FromUnsignedLong(v); + return PyLong_FromLong(v); +} + +template <typename IntT> +static PyTypeObject *createPythonEnumHelper(PyObject *module, + const char *fullName, const char *enumItemStrings[], const IntT enumValues[]) +{ + AutoDecRef args(PyList_New(0)); + auto *pyEnumItems = args.object(); + for (size_t idx = 0; enumItemStrings[idx] != nullptr; ++idx) { + const char *kv = enumItemStrings[idx]; + auto *key = PyUnicode_FromString(kv); + auto *value = toPyObject(enumValues[idx]); + auto *key_value = PyTuple_New(2); + PyTuple_SET_ITEM(key_value, 0, key); + PyTuple_SET_ITEM(key_value, 1, value); + PyList_Append(pyEnumItems, key_value); + } + return createEnumForPython(module, fullName, pyEnumItems); } -namespace Shiboken { namespace Enum { +// Now we have to concretize these functions explicitly, +// otherwise templates will not work across modules. -static void -copyNumberMethods(PyTypeObject *flagsType, - PyType_Slot number_slots[], - int *pidx) +PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], const int64_t enumValues[]) { - int idx = *pidx; -#define PUT_SLOT(name) \ - number_slots[idx].slot = (name); \ - number_slots[idx].pfunc = PyType_GetSlot(flagsType, (name)); \ - ++idx; - - PUT_SLOT(Py_nb_absolute); - PUT_SLOT(Py_nb_add); - PUT_SLOT(Py_nb_and); - PUT_SLOT(Py_nb_bool); - PUT_SLOT(Py_nb_divmod); - PUT_SLOT(Py_nb_float); - PUT_SLOT(Py_nb_floor_divide); - PUT_SLOT(Py_nb_index); - PUT_SLOT(Py_nb_inplace_add); - PUT_SLOT(Py_nb_inplace_and); - PUT_SLOT(Py_nb_inplace_floor_divide); - PUT_SLOT(Py_nb_inplace_lshift); - PUT_SLOT(Py_nb_inplace_multiply); - PUT_SLOT(Py_nb_inplace_or); - PUT_SLOT(Py_nb_inplace_power); - PUT_SLOT(Py_nb_inplace_remainder); - PUT_SLOT(Py_nb_inplace_rshift); - PUT_SLOT(Py_nb_inplace_subtract); - PUT_SLOT(Py_nb_inplace_true_divide); - PUT_SLOT(Py_nb_inplace_xor); - PUT_SLOT(Py_nb_int); - PUT_SLOT(Py_nb_invert); - PUT_SLOT(Py_nb_lshift); - PUT_SLOT(Py_nb_multiply); - PUT_SLOT(Py_nb_negative); - PUT_SLOT(Py_nb_or); - PUT_SLOT(Py_nb_positive); - PUT_SLOT(Py_nb_power); - PUT_SLOT(Py_nb_remainder); - PUT_SLOT(Py_nb_rshift); - PUT_SLOT(Py_nb_subtract); - PUT_SLOT(Py_nb_true_divide); - PUT_SLOT(Py_nb_xor); -#undef PUT_SLOT - *pidx = idx; + return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues); } -PyTypeObject * -newTypeWithName(const char *name, - const char *cppName, - PyTypeObject *numbers_fromFlag) +PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], const uint64_t enumValues[]) { - // Careful: SbkType_FromSpec does not allocate the string. - PyType_Slot newslots[99] = {}; // enough but not too big for the stack - PyType_Spec newspec; - DeclaredEnumTypes::EnumEntry entry{strdup(name), nullptr}; - newspec.name = entry.name; // Note that SbkType_FromSpecWithBases might use a substring. - newspec.basicsize = SbkNewEnum_spec.basicsize; - newspec.itemsize = SbkNewEnum_spec.itemsize; - newspec.flags = SbkNewEnum_spec.flags; - // we must append all the number methods, so rebuild everything: - int idx = 0; - while (SbkNewEnum_slots[idx].slot) { - newslots[idx].slot = SbkNewEnum_slots[idx].slot; - newslots[idx].pfunc = SbkNewEnum_slots[idx].pfunc; - ++idx; - } - if (numbers_fromFlag) - copyNumberMethods(numbers_fromFlag, newslots, &idx); - newspec.slots = newslots; - Shiboken::AutoDecRef bases(PyTuple_New(1)); - static auto basetype = reinterpret_cast<PyObject *>(SbkEnum_TypeF()); - Py_INCREF(basetype); - PyTuple_SetItem(bases, 0, basetype); - auto *type = SbkType_FromSpecWithBases(&newspec, bases); - entry.type = reinterpret_cast<PyTypeObject *>(type); - Py_TYPE(type) = SbkEnumType_TypeF(); - - auto *enumType = reinterpret_cast<SbkEnumType *>(type); - auto *setp = PepType_SETP(enumType); - setp->cppName = cppName; - DeclaredEnumTypes::instance().addEnumType(entry); - return entry.type; + return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues); } -const char *getCppName(PyTypeObject *enumType) +PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], const int32_t enumValues[]) { - assert(Py_TYPE(enumType) == SbkEnumType_TypeF()); - auto *type = reinterpret_cast<SbkEnumType *>(enumType); - auto *setp = PepType_SETP(type); - return setp->cppName; + return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues); } -long int getValue(PyObject *enumItem) +PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], const uint32_t enumValues[]) { - assert(Shiboken::Enum::check(enumItem)); - return reinterpret_cast<SbkEnumObject *>(enumItem)->ob_value; -} - -void setTypeConverter(PyTypeObject *type, SbkConverter *converter, bool isFlag) -{ - if (isFlag) { - auto *flagsType = reinterpret_cast<PySideQFlagsType *>(type); - PepType_PFTP(flagsType)->converter = converter; - } - else { - auto *enumType = reinterpret_cast<SbkEnumType *>(type); - PepType_SETP(enumType)->converter = converter; - } + return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues); } -} // namespace Enum - -DeclaredEnumTypes &DeclaredEnumTypes::instance() +PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], const int16_t enumValues[]) { - static DeclaredEnumTypes me; - return me; + return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues); } -DeclaredEnumTypes::DeclaredEnumTypes() = default; - -DeclaredEnumTypes::~DeclaredEnumTypes() +PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], const uint16_t enumValues[]) { - cleanup(); + return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues); } -void DeclaredEnumTypes::cleanup() +PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], const int8_t enumValues[]) { - for (const auto &e : m_enumTypes) { - std::free(e.name); - Py_DECREF(e.type); - } - m_enumTypes.clear(); + return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues); } -} // namespace Shiboken - -static void cleanupEnumTypes() +PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], const uint8_t enumValues[]) { - Shiboken::DeclaredEnumTypes::instance().cleanup(); + return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues); } +} // namespace Shiboken::Enum diff --git a/sources/shiboken6/libshiboken/sbkenum.h b/sources/shiboken6/libshiboken/sbkenum.h index fd0fcb904..e19ca4b4c 100644 --- a/sources/shiboken6/libshiboken/sbkenum.h +++ b/sources/shiboken6/libshiboken/sbkenum.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef SBKENUM_H #define SBKENUM_H @@ -46,79 +10,93 @@ extern "C" { +LIBSHIBOKEN_API bool PyEnumMeta_Check(PyObject *ob); + /// exposed for the signature module LIBSHIBOKEN_API void init_enum(); -extern LIBSHIBOKEN_API PyTypeObject *SbkEnumType_TypeF(void); -struct SbkObjectType; struct SbkConverter; struct SbkEnumType; -struct SbkEnumTypePrivate; - -} // extern "C" -namespace Shiboken +struct SbkEnumTypePrivate { + SbkConverter *converter; +}; + +/// PYSIDE-1735: Pass on the Python enum/flag information. +LIBSHIBOKEN_API void initEnumFlagsDict(PyTypeObject *type); + +/// PYSIDE-1735: Make sure that we can import the Python enum implementation. +LIBSHIBOKEN_API PyTypeObject *getPyEnumMeta(); +/// PYSIDE-1735: Helper function supporting QEnum +LIBSHIBOKEN_API int enumIsFlag(PyObject *ob_enum); -inline bool isShibokenEnum(PyObject *pyObj) -{ - return Py_TYPE(Py_TYPE(pyObj)) == SbkEnumType_TypeF(); } -namespace Enum +namespace Shiboken::Enum { + +enum : int { + ENOPT_OLD_ENUM = 0x00, // PySide 6.6: no longer supported + ENOPT_NEW_ENUM = 0x01, + ENOPT_INHERIT_INT = 0x02, + ENOPT_GLOBAL_SHORTCUT = 0x04, + ENOPT_SCOPED_SHORTCUT = 0x08, + ENOPT_NO_FAKESHORTCUT = 0x10, + ENOPT_NO_FAKERENAMES = 0x20, + ENOPT_NO_ZERODEFAULT = 0x40, + ENOPT_NO_MISSING = 0x80, +}; + +LIBSHIBOKEN_API extern int enumOption; + +using EnumValueType = long long; + +LIBSHIBOKEN_API bool check(PyObject *obj); + +LIBSHIBOKEN_API PyObject *newItem(PyTypeObject *enumType, EnumValueType itemValue, + const char *itemName = nullptr); + +LIBSHIBOKEN_API EnumValueType getValue(PyObject *enumItem); +LIBSHIBOKEN_API PyObject *getEnumItemFromValue(PyTypeObject *enumType, + EnumValueType itemValue); + +/// Sets the enum/flag's type converter. +LIBSHIBOKEN_API void setTypeConverter(PyTypeObject *type, SbkConverter *converter); + +/// Creating Python enums for different types. +LIBSHIBOKEN_API PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], const int64_t enumValues[]); + +LIBSHIBOKEN_API PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], const uint64_t enumValues[]); + +LIBSHIBOKEN_API PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], const int32_t enumValues[]); + +LIBSHIBOKEN_API PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], const uint32_t enumValues[]); + +LIBSHIBOKEN_API PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], const int16_t enumValues[]); + +LIBSHIBOKEN_API PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], const uint16_t enumValues[]); + +LIBSHIBOKEN_API PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], const int8_t enumValues[]); + +LIBSHIBOKEN_API PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], const uint8_t enumValues[]); + +/// This template removes duplication by inlining necessary type casts. +template <typename IntT> +inline PyTypeObject *createPythonEnum(PyTypeObject *scope, + const char *fullName, const char *enumItemStrings[], const IntT enumValues[]) { - 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 = nullptr); - /// 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 = nullptr); - - /** - * 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, PyTypeObject *scope, - const char *itemName, long itemValue); - LIBSHIBOKEN_API bool createScopedEnumItem(PyTypeObject *enumType, SbkObjectType *scope, const char *itemName, long itemValue); - - LIBSHIBOKEN_API PyObject *newItem(PyTypeObject *enumType, long itemValue, const char *itemName = nullptr); - - LIBSHIBOKEN_API PyTypeObject *newTypeWithName(const char *name, const char *cppName, - PyTypeObject *numbers_fromFlag=nullptr); - LIBSHIBOKEN_API const char *getCppName(PyTypeObject *type); - - LIBSHIBOKEN_API long getValue(PyObject *enumItem); - LIBSHIBOKEN_API PyObject *getEnumItemFromValue(PyTypeObject *enumType, long itemValue); - - /// Sets the enum/flag's type converter. - LIBSHIBOKEN_API void setTypeConverter(PyTypeObject *type, SbkConverter *converter, bool isFlag); - - LIBSHIBOKEN_API PyObject *unpickleEnum(PyObject *, PyObject *); + auto *obScope = reinterpret_cast<PyObject *>(scope); + return createPythonEnum(obScope, fullName, enumItemStrings, enumValues); } -} // namespace Shiboken +} // namespace Shiboken::Enum #endif // SKB_PYENUM_H diff --git a/sources/shiboken6/libshiboken/sbkenum_p.h b/sources/shiboken6/libshiboken/sbkenum_p.h deleted file mode 100644 index 895c1ddcb..000000000 --- a/sources/shiboken6/libshiboken/sbkenum_p.h +++ /dev/null @@ -1,52 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef SBKENUM_P_H -#define SBKENUM_P_H - -#include "sbkpython.h" -#include "shibokenmacros.h" - -struct SbkEnumTypePrivate -{ - SbkConverter *converter; - const char *cppName; -}; - -#endif // SKB_PYENUM_P_H diff --git a/sources/shiboken6/libshiboken/sbkerrors.cpp b/sources/shiboken6/libshiboken/sbkerrors.cpp new file mode 100644 index 000000000..1832624d5 --- /dev/null +++ b/sources/shiboken6/libshiboken/sbkerrors.cpp @@ -0,0 +1,170 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "sbkerrors.h" +#include "sbkstring.h" +#include "helper.h" +#include "gilstate.h" + +namespace Shiboken +{ + +// PYSIDE-2335: Track down if we can reach a Python error handler. +// _pythonContextStack has always the current state of handler status +// in its lowest bit. +// Blocking calls like exec or run need to use `setBlocking`. +static thread_local std::size_t _pythonContextStack{}; + +PythonContextMarker::PythonContextMarker() +{ + // Shift history up and set lowest bit. + _pythonContextStack = (_pythonContextStack * 2) + 1; +} + +PythonContextMarker::~PythonContextMarker() +{ + // Shift history down. + _pythonContextStack /= 2; +} + +void PythonContextMarker::setBlocking() +{ + // Clear lowest bit. + _pythonContextStack = _pythonContextStack / 2 * 2; +} + +namespace Errors +{ + +void setInstantiateAbstractClass(const char *name) +{ + PyErr_Format(PyExc_NotImplementedError, + "'%s' represents a C++ abstract class and cannot be instantiated", name); +} + +void setInstantiateAbstractClassDisabledWrapper(const char *name) +{ + PyErr_Format(PyExc_NotImplementedError, + "Abstract class '%s' cannot be instantiated since the wrapper has been disabled.", + name); +} + +void setInvalidTypeDeletion(const char *name) +{ + PyErr_Format(PyExc_TypeError, "'%s' may not be deleted", name); +} + +void setOperatorNotImplemented() +{ + PyErr_SetString(PyExc_NotImplementedError, "operator not implemented."); +} + +void setPureVirtualMethodError(const char *name) +{ + PyErr_Format(PyExc_NotImplementedError, "pure virtual method '%s' not implemented.", name); +} + +void setPrivateMethod(const char *name) +{ + PyErr_Format(PyExc_TypeError, "%s is a private method.\", ", name); +} + +void setReverseOperatorNotImplemented() +{ + PyErr_SetString(PyExc_NotImplementedError, "reverse operator not implemented."); +} + +void setSequenceTypeError(const char *expectedType) +{ + PyErr_Format(PyExc_TypeError, + "attributed value with wrong type, '%s' or other convertible type expected", + expectedType); +} + +void setSetterTypeError(const char *name, const char *expectedType) +{ + PyErr_Format(PyExc_TypeError, + "wrong type attributed to '%s', '%s' or convertible type expected", + name, expectedType); +} + +void setWrongContainerType() +{ + PyErr_SetString(PyExc_TypeError, "Wrong type passed to container conversion."); +} + +struct ErrorStore { + PyObject *type; + PyObject *exc; + PyObject *traceback; +}; + +static thread_local ErrorStore savedError{}; + +void storeErrorOrPrint() +{ + // This error happened in a function with no way to return an error state. + // Therefore, we handle the error when we are error checking, anyway. + // But we do that only when we know that an error handler can pick it up. + if (_pythonContextStack & 1) + PyErr_Fetch(&savedError.type, &savedError.exc, &savedError.traceback); + else + PyErr_Print(); +} + +PyObject *occurred() +{ + if (savedError.type) { + PyErr_Restore(savedError.type, savedError.exc, savedError.traceback); + savedError.type = nullptr; + } + return PyErr_Occurred(); +} + +} // namespace Errors + +namespace Warnings +{ +void warnInvalidReturnValue(const char *className, const char *functionName, + const char *expectedType, const char *actualType) +{ + Shiboken::warning(PyExc_RuntimeWarning, 2, + "Invalid return value in function '%s.%s', expected %s, got %s.", + className, functionName, expectedType, actualType); +} + +void warnDeprecated(const char *functionName) +{ + Shiboken::warning(PyExc_DeprecationWarning, 1, + "Function: '%s' is marked as deprecated, please check " + "the documentation for more information.", + functionName); +} + +void warnDeprecated(const char *className, const char *functionName) +{ + Shiboken::warning(PyExc_DeprecationWarning, 1, + "Function: '%s.%s' is marked as deprecated, please check " + "the documentation for more information.", + className, functionName); +} + +void warnDeprecatedEnum(const char *enumName) +{ + Shiboken::warning(PyExc_DeprecationWarning, 1, + "Enum: '%s' is marked as deprecated, please check " + "the documentation for more information.", + enumName); +} + +void warnDeprecatedEnumValue(const char *enumName, const char *valueName) +{ + Shiboken::warning(PyExc_DeprecationWarning, 1, + "Enum value '%s.%s' is marked as deprecated, please check " + "the documentation for more information.", + enumName, valueName); + +} + +} // namespace Warnings +} // namespace Shiboken diff --git a/sources/shiboken6/libshiboken/sbkerrors.h b/sources/shiboken6/libshiboken/sbkerrors.h new file mode 100644 index 000000000..6ff85f8e1 --- /dev/null +++ b/sources/shiboken6/libshiboken/sbkerrors.h @@ -0,0 +1,73 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef SBKERRORS_H +#define SBKERRORS_H + +#include "sbkpython.h" +#include "shibokenmacros.h" + +/// Craving for C++20 and std::source_location::current() +#if defined(_MSC_VER) +# define SBK_FUNC_INFO __FUNCSIG__ +#elif defined(__GNUC__) +# define SBK_FUNC_INFO __PRETTY_FUNCTION__ +#else +# define SBK_FUNC_INFO __FUNCTION__ +#endif + +namespace Shiboken +{ + +struct LIBSHIBOKEN_API PythonContextMarker +{ +public: + PythonContextMarker(const PythonContextMarker &) = delete; + PythonContextMarker(PythonContextMarker &&) = delete; + PythonContextMarker &operator=(const PythonContextMarker &) = delete; + PythonContextMarker &operator=(PythonContextMarker &&) = delete; + + explicit PythonContextMarker(); + ~PythonContextMarker(); + void setBlocking(); +}; + +namespace Errors +{ + +LIBSHIBOKEN_API void setInstantiateAbstractClass(const char *name); +LIBSHIBOKEN_API void setInstantiateAbstractClassDisabledWrapper(const char *name); +LIBSHIBOKEN_API void setInvalidTypeDeletion(const char *name); +LIBSHIBOKEN_API void setOperatorNotImplemented(); +LIBSHIBOKEN_API void setPureVirtualMethodError(const char *name); +LIBSHIBOKEN_API void setPrivateMethod(const char *name); +LIBSHIBOKEN_API void setReverseOperatorNotImplemented(); +LIBSHIBOKEN_API void setSequenceTypeError(const char *expectedType); +LIBSHIBOKEN_API void setSetterTypeError(const char *name, const char *expectedType); +LIBSHIBOKEN_API void setWrongContainerType(); + +/// Report an error ASAP: Instead of printing, store for later re-raise. +/// This replaces `PyErr_Print`, which cannot report errors as exception. +/// To be used in contexts where raising errors is impossible. +LIBSHIBOKEN_API void storeErrorOrPrint(); +/// Handle an error as in PyErr_Occurred(), but also check for errors which +/// were captured by `storeErrorOrPrint`. +/// To be used in normal error checks. +LIBSHIBOKEN_API PyObject *occurred(); + +} // namespace Errors + +namespace Warnings +{ +/// Warn about invalid return value of overwritten virtual +LIBSHIBOKEN_API void warnInvalidReturnValue(const char *className, const char *functionName, + const char *expectedType, const char *actualType); +LIBSHIBOKEN_API void warnDeprecated(const char *functionName); +LIBSHIBOKEN_API void warnDeprecated(const char *className, const char *functionName); +LIBSHIBOKEN_API void warnDeprecatedEnum(const char *enumName); +LIBSHIBOKEN_API void warnDeprecatedEnumValue(const char *enumName, const char *valueName); +} // namespace Warnings + +} // namespace Shiboken + +#endif // SBKERRORS_H diff --git a/sources/shiboken6/libshiboken/sbkfeature_base.cpp b/sources/shiboken6/libshiboken/sbkfeature_base.cpp new file mode 100644 index 000000000..f31b8f4f7 --- /dev/null +++ b/sources/shiboken6/libshiboken/sbkfeature_base.cpp @@ -0,0 +1,424 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "basewrapper.h" +#include "basewrapper_p.h" +#include "autodecref.h" +#include "pep384ext.h" +#include "sbkenum.h" +#include "sbkstring.h" +#include "sbkstaticstrings.h" +#include "sbkstaticstrings_p.h" +#include "signature.h" +#include "sbkfeature_base.h" +#include "gilstate.h" + +#include <cctype> + +using namespace Shiboken; + +extern "C" +{ + +//////////////////////////////////////////////////////////////////////////// +// +// Minimal __feature__ support in Shiboken +// +int currentSelectId(PyTypeObject *type) +{ + AutoDecRef tpDict(PepType_GetDict(type)); + PyObject *PyId = PyObject_GetAttr(tpDict.object(), PyName::select_id()); + if (PyId == nullptr) { + PyErr_Clear(); + return 0x00; + } + int sel = PyLong_AsLong(PyId); + Py_DECREF(PyId); + return sel; +} + +static SelectableFeatureHook SelectFeatureSet = nullptr; +static SelectableFeatureCallback featureCb = nullptr; + +void setSelectableFeatureCallback(SelectableFeatureCallback func) +{ + featureCb = func; +} + +SelectableFeatureHook initSelectableFeature(SelectableFeatureHook func) +{ + auto ret = SelectFeatureSet; + SelectFeatureSet = func; + if (featureCb) + featureCb(SelectFeatureSet != nullptr); + return ret; +} +// +//////////////////////////////////////////////////////////////////////////// + +// This useful function is for debugging +void disassembleFrame(const char *marker) +{ + Shiboken::GilState gil; + PyObject *error_type, *error_value, *error_traceback; + PyErr_Fetch(&error_type, &error_value, &error_traceback); + static PyObject *dismodule = PyImport_ImportModule("dis"); + static PyObject *disco = PyObject_GetAttrString(dismodule, "disco"); + static PyObject *const _f_lasti = Shiboken::String::createStaticString("f_lasti"); + static PyObject *const _f_lineno = Shiboken::String::createStaticString("f_lineno"); + static PyObject *const _f_code = Shiboken::String::createStaticString("f_code"); + static PyObject *const _co_filename = Shiboken::String::createStaticString("co_filename"); + AutoDecRef ignore{}; + auto *frame = reinterpret_cast<PyObject *>(PyEval_GetFrame()); + if (frame == nullptr) { + fprintf(stdout, "\n%s BEGIN no frame END\n\n", marker); + } else { + AutoDecRef f_lasti(PyObject_GetAttr(frame, _f_lasti)); + AutoDecRef f_lineno(PyObject_GetAttr(frame, _f_lineno)); + AutoDecRef f_code(PyObject_GetAttr(frame, _f_code)); + AutoDecRef co_filename(PyObject_GetAttr(f_code, _co_filename)); + long line = PyLong_AsLong(f_lineno); + const char *fname = String::toCString(co_filename); + fprintf(stdout, "\n%s BEGIN line=%ld %s\n", marker, line, fname); + ignore.reset(PyObject_CallFunctionObjArgs(disco, f_code.object(), f_lasti.object(), nullptr)); + fprintf(stdout, "%s END line=%ld %s\n\n", marker, line, fname); + } +#if PY_VERSION_HEX >= 0x030C0000 && !Py_LIMITED_API + if (error_type) + PyErr_DisplayException(error_value); +#endif + static PyObject *stdout_file = PySys_GetObject("stdout"); + ignore.reset(PyObject_CallMethod(stdout_file, "flush", nullptr)); + PyErr_Restore(error_type, error_value, error_traceback); +} + +// python 3.12 +static int const CALL = 171; +// Python 3.11 +static int const PRECALL = 166; +// we have "big instructions" with gaps after them +static int const LOAD_METHOD_GAP_311 = 10 * 2; +static int const LOAD_ATTR_GAP_311 = 4 * 2; +static int const LOAD_ATTR_GAP = 9 * 2; +// Python 3.7 - 3.10 +static int const LOAD_METHOD = 160; +static int const CALL_METHOD = 161; +// Python 3.6 +static int const CALL_FUNCTION = 131; +static int const LOAD_ATTR = 106; +// NoGil (how long will this exist in this form?) +static int const LOAD_METHOD_NOGIL = 55; +static int const CALL_METHOD_NOGIL = 72; + +static bool currentOpcode_Is_CallMethNoArgs() +{ + // PYSIDE-2221: Special case for the NoGil version: + // Find out if we have such a version. + // We could also ask the variable `Py_NOGIL`. + static PyObject *flags = PySys_GetObject("flags"); + static bool isNoGil = PyObject_HasAttrString(flags, "nogil"); + // We look into the currently active operation if we are going to call + // a method with zero arguments. + auto *frame = PyEval_GetFrame(); +#if !Py_LIMITED_API && !defined(PYPY_VERSION) + auto *f_code = PyFrame_GetCode(frame); +#else + static PyObject *const _f_code = Shiboken::String::createStaticString("f_code"); + AutoDecRef dec_f_code(PyObject_GetAttr(reinterpret_cast<PyObject *>(frame), _f_code)); + auto *f_code = dec_f_code.object(); +#endif +#if PY_VERSION_HEX >= 0x030B0000 && !Py_LIMITED_API + AutoDecRef dec_co_code(PyCode_GetCode(f_code)); + Py_ssize_t f_lasti = PyFrame_GetLasti(frame); +#else + static PyObject *const _f_lasti = Shiboken::String::createStaticString("f_lasti"); + static PyObject *const _co_code = Shiboken::String::createStaticString("co_code"); + AutoDecRef dec_co_code(PyObject_GetAttr(reinterpret_cast<PyObject *>(f_code), _co_code)); + AutoDecRef dec_f_lasti(PyObject_GetAttr(reinterpret_cast<PyObject *>(frame), _f_lasti)); + Py_ssize_t f_lasti = PyLong_AsSsize_t(dec_f_lasti); +#endif + Py_ssize_t code_len; + char *co_code{}; + PyBytes_AsStringAndSize(dec_co_code, &co_code, &code_len); + uint8_t opcode1 = co_code[f_lasti]; + if (isNoGil) { + uint8_t opcode2 = co_code[f_lasti + 4]; + uint8_t oparg2 = co_code[f_lasti + 6]; + return opcode1 == LOAD_METHOD_NOGIL && opcode2 == CALL_METHOD_NOGIL && oparg2 == 1; + } + uint8_t opcode2 = co_code[f_lasti + 2]; + uint8_t oparg2 = co_code[f_lasti + 3]; + static auto number = _PepRuntimeVersion(); + if (number < 0x030B00) + return opcode1 == LOAD_METHOD && opcode2 == CALL_METHOD && oparg2 == 0; + + if (number < 0x030C00) { + // With Python 3.11, the opcodes get bigger and change a bit. + // Note: The new adaptive opcodes are elegantly hidden and we + // don't need to take care of them. + if (opcode1 == LOAD_METHOD) + f_lasti += LOAD_METHOD_GAP_311; + else if (opcode1 == LOAD_ATTR) + f_lasti += LOAD_ATTR_GAP_311; + else + return false; + + opcode2 = co_code[f_lasti + 2]; + oparg2 = co_code[f_lasti + 3]; + + return opcode2 == PRECALL && oparg2 == 0; + } + // With Python 3.12, the opcodes get again bigger and change a bit. + // Note: The new adaptive opcodes are elegantly hidden and we + // don't need to take care of them. + if (opcode1 == LOAD_ATTR) + f_lasti += LOAD_ATTR_GAP; + else + return false; + + opcode2 = co_code[f_lasti + 2]; + oparg2 = co_code[f_lasti + 3]; + + return opcode2 == CALL && oparg2 == 0; +} + +void initEnumFlagsDict(PyTypeObject *type) +{ + // We create a dict for all flag enums that holds the original C++ name + // and a dict that gives every enum/flag type name. + static PyObject *const split = Shiboken::String::createStaticString("split"); + static PyObject *const colon = Shiboken::String::createStaticString(":"); + auto sotp = PepType_SOTP(type); + auto **enumFlagInfo = sotp->enumFlagInfo; + auto *dict = PyDict_New(); + auto *typeDict = PyDict_New(); + for (; *enumFlagInfo; ++enumFlagInfo) { + AutoDecRef line(PyUnicode_FromString(*enumFlagInfo)); + AutoDecRef parts(PyObject_CallMethodObjArgs(line, split, colon, nullptr)); + auto *name = PyList_GetItem(parts, 0); + if (PyList_Size(parts) == 3) { + auto *key = PyList_GetItem(parts, 2); + auto *value = name; + PyDict_SetItem(dict, key, value); + } + auto *typeName = PyList_GetItem(parts, 1); + PyDict_SetItem(typeDict, name, typeName); + } + sotp->enumFlagsDict = dict; + sotp->enumTypeDict = typeDict; +} + +static PyObject *replaceNoArgWithZero(PyObject *callable) +{ + static auto *partial = Pep_GetPartialFunction(); + static auto *zero = PyLong_FromLong(0); + return PyObject_CallFunctionObjArgs(partial, callable, zero, nullptr); +} + +static PyObject *lookupUnqualifiedOrOldEnum(PyTypeObject *type, PyObject *name) +{ + // MRO has been observed to be 0 in case of errors with QML decorators + if (type == nullptr || type->tp_mro == nullptr) + return nullptr; + // Quick Check: Avoid "__..", "_slots", etc. + if (std::isalpha(Shiboken::String::toCString(name)[0]) == 0) + return nullptr; + static PyTypeObject *const EnumMeta = getPyEnumMeta(); + static PyObject *const _member_map_ = String::createStaticString("_member_map_"); + // This is similar to `find_name_in_mro`, but instead of looking directly into + // tp_dict, we also search for the attribute in local classes of that dict (Part 2). + PyObject *mro = type->tp_mro; + PyObject *result{}; + assert(PyTuple_Check(mro)); + Py_ssize_t idx, n = PyTuple_GET_SIZE(mro); + for (idx = 0; idx < n; ++idx) { + auto *base = PyTuple_GET_ITEM(mro, idx); + auto *type_base = reinterpret_cast<PyTypeObject *>(base); + if (!SbkObjectType_Check(type_base)) + continue; + auto sotp = PepType_SOTP(type_base); + // The EnumFlagInfo structure tells us if there are Enums at all. + const char **enumFlagInfo = sotp->enumFlagInfo; + if (!(enumFlagInfo)) + continue; + if (!sotp->enumFlagsDict) + initEnumFlagsDict(type_base); + bool useFakeRenames = !(Enum::enumOption & Enum::ENOPT_NO_FAKERENAMES); + if (useFakeRenames) { + auto *rename = PyDict_GetItem(sotp->enumFlagsDict, name); + if (rename) { + /* + * Part 1: Look into the enumFlagsDict if we have an old flags name. + * ------------------------------------------------------------- + * We need to replace the parameterless + + QtCore.Qt.Alignment() + + * by the one-parameter call + + QtCore.Qt.AlignmentFlag(0) + + * That means: We need to bind the zero as default into a wrapper and + * return that to be called. + * + * Addendum: + * --------- + * We first need to look into the current opcode of the bytecode to find + * out if we have a call like above or just a type lookup. + */ + AutoDecRef tpDict(PepType_GetDict(type_base)); + auto *flagType = PyDict_GetItem(tpDict.object(), rename); + if (currentOpcode_Is_CallMethNoArgs()) + return replaceNoArgWithZero(flagType); + Py_INCREF(flagType); + return flagType; + } + } + bool useFakeShortcuts = !(Enum::enumOption & Enum::ENOPT_NO_FAKESHORTCUT); + if (useFakeShortcuts) { + AutoDecRef tpDict(PepType_GetDict(type_base)); + auto *dict = tpDict.object(); + PyObject *key, *value; + Py_ssize_t pos = 0; + while (PyDict_Next(dict, &pos, &key, &value)) { + /* + * Part 2: Check for a duplication into outer scope. + * ------------------------------------------------- + * We need to replace the shortcut + + QtCore.Qt.AlignLeft + + * by the correct call + + QtCore.Qt.AlignmentFlag.AlignLeft + + * That means: We need to search all Enums of the class. + */ + if (Py_TYPE(value) == EnumMeta) { + auto *valtype = reinterpret_cast<PyTypeObject *>(value); + AutoDecRef valtypeDict(PepType_GetDict(valtype)); + auto *member_map = PyDict_GetItem(valtypeDict.object(), _member_map_); + if (member_map && PyDict_Check(member_map)) { + result = PyDict_GetItem(member_map, name); + Py_XINCREF(result); + if (result) + return result; + } + } + } + } + } + return nullptr; +} + +PyObject *mangled_type_getattro(PyTypeObject *type, PyObject *name) +{ + /* + * Note: This `type_getattro` version is only the default that comes + * from `PyType_Type.tp_getattro`. This does *not* interfere in any way + * with the complex `tp_getattro` of `QObject` and other instances. + * What we change here is the meta class of `QObject`. + */ + static getattrofunc const type_getattro = PepExt_Type_GetGetAttroSlot(&PyType_Type); + static PyObject *const ignAttr1 = PyName::qtStaticMetaObject(); + static PyObject *const ignAttr2 = PyMagicName::get(); + static PyTypeObject *const EnumMeta = getPyEnumMeta(); + + if (SelectFeatureSet != nullptr) + SelectFeatureSet(type); + auto *ret = type_getattro(reinterpret_cast<PyObject *>(type), name); + + // PYSIDE-1735: Be forgiving with strict enums and fetch the enum, silently. + // The PYI files now look correct, but the old duplication is + // emulated here. This should be removed in Qt 7, see `parser.py`. + // + // FIXME PYSIDE7 should remove this forgiveness: + // + // The duplication of enum values into the enclosing scope, allowing to write + // Qt.AlignLeft instead of Qt.Alignment.AlignLeft, is still implemented but + // no longer advertized in PYI files or line completion. + + if (ret && Py_TYPE(ret) == EnumMeta && currentOpcode_Is_CallMethNoArgs()) { + bool useZeroDefault = !(Enum::enumOption & Enum::ENOPT_NO_ZERODEFAULT); + if (useZeroDefault) { + // We provide a zero argument for compatibility if it is a call with no args. + auto *hold = replaceNoArgWithZero(ret); + Py_DECREF(ret); + ret = hold; + } + } + + if (!ret && name != ignAttr1 && name != ignAttr2) { + PyObject *error_type{}, *error_value{}, *error_traceback{}; + PyErr_Fetch(&error_type, &error_value, &error_traceback); + ret = lookupUnqualifiedOrOldEnum(type, name); + if (ret) { + Py_DECREF(error_type); + Py_XDECREF(error_value); + Py_XDECREF(error_traceback); + } else { + PyErr_Restore(error_type, error_value, error_traceback); + } + } + return ret; +} + +PyObject *Sbk_TypeGet___dict__(PyTypeObject *type, void * /* context */) +{ + /* + * This is the override for getting a dict. + */ + AutoDecRef tpDict(PepType_GetDict(type)); + auto *dict = tpDict.object();; + if (dict == nullptr) + Py_RETURN_NONE; + if (SelectFeatureSet != nullptr) { + SelectFeatureSet(type); + tpDict.reset(PepType_GetDict(type)); + dict = tpDict.object(); + } + return PyDictProxy_New(dict); +} + +// These functions replace the standard PyObject_Generic(Get|Set)Attr functions. +// They provide the default that "object" inherits. +// Everything else is directly handled by cppgenerator that calls `Feature::Select`. +PyObject *SbkObject_GenericGetAttr(PyObject *obj, PyObject *name) +{ + auto type = Py_TYPE(obj); + if (SelectFeatureSet != nullptr) + SelectFeatureSet(type); + return PyObject_GenericGetAttr(obj, name); +} + +int SbkObject_GenericSetAttr(PyObject *obj, PyObject *name, PyObject *value) +{ + auto type = Py_TYPE(obj); + if (SelectFeatureSet != nullptr) + SelectFeatureSet(type); + return PyObject_GenericSetAttr(obj, name, value); +} + +const char **SbkObjectType_GetPropertyStrings(PyTypeObject *type) +{ + return PepType_SOTP(type)->propertyStrings; +} + +void SbkObjectType_SetPropertyStrings(PyTypeObject *type, const char **strings) +{ + PepType_SOTP(type)->propertyStrings = strings; +} + +void SbkObjectType_SetEnumFlagInfo(PyTypeObject *type, const char **strings) +{ + PepType_SOTP(type)->enumFlagInfo = strings; +} + +// PYSIDE-1626: Enforcing a context switch without further action. +void SbkObjectType_UpdateFeature(PyTypeObject *type) +{ + if (SelectFeatureSet != nullptr) + SelectFeatureSet(type); +} + +} // extern "C" diff --git a/sources/shiboken6/libshiboken/sbkfeature_base.h b/sources/shiboken6/libshiboken/sbkfeature_base.h new file mode 100644 index 000000000..290884062 --- /dev/null +++ b/sources/shiboken6/libshiboken/sbkfeature_base.h @@ -0,0 +1,18 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef SBKFEATURE_BASE_H +#define SBKFEATURE_BASE_H + +extern "C" +{ + +LIBSHIBOKEN_API int currentSelectId(PyTypeObject *type); +LIBSHIBOKEN_API PyObject *mangled_type_getattro(PyTypeObject *type, PyObject *name); +LIBSHIBOKEN_API PyObject *Sbk_TypeGet___dict__(PyTypeObject *type, void *context); +LIBSHIBOKEN_API PyObject *SbkObject_GenericGetAttr(PyObject *obj, PyObject *name); +LIBSHIBOKEN_API int SbkObject_GenericSetAttr(PyObject *obj, PyObject *name, PyObject *value); + +} // extern "C" + +#endif // SBKFEATURE_BASE_H diff --git a/sources/shiboken6/libshiboken/sbkmodule.cpp b/sources/shiboken6/libshiboken/sbkmodule.cpp index e5f4e2f88..4153df27f 100644 --- a/sources/shiboken6/libshiboken/sbkmodule.cpp +++ b/sources/shiboken6/libshiboken/sbkmodule.cpp @@ -1,91 +1,471 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "sbkmodule.h" +#include "autodecref.h" #include "basewrapper.h" #include "bindingmanager.h" +#include "sbkstring.h" +#include "sbkcppstring.h" + #include <unordered_map> +#include <unordered_set> +#include <cstring> /// This hash maps module objects to arrays of Python types. -using ModuleTypesMap = std::unordered_map<PyObject *, PyTypeObject **> ; +using ModuleTypesMap = std::unordered_map<PyObject *, Shiboken::Module::TypeInitStruct *> ; /// This hash maps module objects to arrays of converters. using ModuleConvertersMap = std::unordered_map<PyObject *, SbkConverter **>; +/// This hash maps type names to type creation functions. +using TypeCreationFunctionModulePair = + std::pair<Shiboken::Module::TypeCreationFunction, PyObject *>; +using NameToTypeFunctionMap = std::unordered_map<std::string, TypeCreationFunctionModulePair>; + +/// This hash maps module objects to maps of names to functions. +using ModuleToFuncsMap = std::unordered_map<PyObject *, NameToTypeFunctionMap> ; + /// All types produced in imported modules are mapped here. static ModuleTypesMap moduleTypes; static ModuleConvertersMap moduleConverters; +static ModuleToFuncsMap moduleToFuncs; namespace Shiboken { namespace Module { +// PYSIDE-2404: Replacing the arguments generated by cpythonTypeNameExt +// by a function call. +LIBSHIBOKEN_API PyTypeObject *get(TypeInitStruct &typeStruct) +{ + if (typeStruct.type != nullptr) + return typeStruct.type; + + static PyObject *sysModules = PyImport_GetModuleDict(); + + // The slow path for initialization. + // We get the type by following the chain from the module. + // As soon as types[index] gets filled, we can stop. + + std::string_view names(typeStruct.fullName); + const bool usePySide = names.compare(0, 8, "PySide6.") == 0; + auto dotPos = usePySide ? names.find('.', 8) : names.find('.'); + auto startPos = dotPos + 1; + AutoDecRef modName(String::fromCppStringView(names.substr(0, dotPos))); + auto *modOrType = PyDict_GetItem(sysModules, modName); + if (modOrType == nullptr) { + PyErr_Format(PyExc_SystemError, "Module %s should already be in sys.modules", + PyModule_GetName(modOrType)); + return nullptr; + } + + do { + dotPos = names.find('.', startPos); + auto typeName = dotPos != std::string::npos + ? names.substr(startPos, dotPos - startPos) + : names.substr(startPos); + startPos = dotPos + 1; + AutoDecRef obTypeName(String::fromCppStringView(typeName)); + modOrType = PyObject_GetAttr(modOrType, obTypeName); + } while (typeStruct.type == nullptr && dotPos != std::string::npos); + + return typeStruct.type; +} + +static PyTypeObject *incarnateType(PyObject *module, const char *name, + NameToTypeFunctionMap &nameToFunc) +{ + // - locate the name and retrieve the generating function + auto funcIter = nameToFunc.find(name); + if (funcIter == nameToFunc.end()) { + // attribute does really not exist. + PyErr_SetNone(PyExc_AttributeError); + return nullptr; + } + // - call this function that returns a PyTypeObject + auto pair = funcIter->second; + auto initFunc = pair.first; + auto *modOrType = pair.second; + + // PYSIDE-2404: Make sure that no switching happens during type creation. + auto saveFeature = initSelectableFeature(nullptr); + PyTypeObject *type = initFunc(modOrType); + initSelectableFeature(saveFeature); + + // - assign this object to the name in the module + auto *res = reinterpret_cast<PyObject *>(type); + Py_INCREF(res); + PyModule_AddObject(module, name, res); // steals reference + // - remove the entry, if not by something cleared. + if (!nameToFunc.empty()) + nameToFunc.erase(funcIter); + // - return the PyTypeObject. + return type; +} + +// PYSIDE-2404: Make sure that the mentioned classes really exist. +// Used in `Pyside::typeName`. Because the result will be cached by +// the creation of the type(s), this is efficient. +void loadLazyClassesWithName(const char *name) +{ + for (auto const & tableIter : moduleToFuncs) { + auto nameToFunc = tableIter.second; + auto funcIter = nameToFunc.find(name); + if (funcIter != nameToFunc.end()) { + // attribute exists in the lazy types. + auto *module = tableIter.first; + incarnateType(module, name, nameToFunc); + } + } +} + +// PYSIDE-2404: Completely load all not yet loaded classes. +// This is needed to resolve a star import. +void resolveLazyClasses(PyObject *module) +{ + // - locate the module in the moduleTofuncs mapping + auto tableIter = moduleToFuncs.find(module); + if (tableIter == moduleToFuncs.end()) + return; + + // - see if there are still unloaded elements + auto &nameToFunc = tableIter->second; + + // - incarnate all types. + while (!nameToFunc.empty()) { + auto it = nameToFunc.begin(); + auto attrNameStr = it->first; + incarnateType(module, attrNameStr.c_str(), nameToFunc); + } +} + +// PYSIDE-2404: Override the gettattr function of modules. +static getattrofunc origModuleGetattro{}; + +// PYSIDE-2404: Use the patched module getattr to do on-demand initialization. +// This modifies _all_ modules but should have no impact. +static PyObject *PyModule_lazyGetAttro(PyObject *module, PyObject *name) +{ + // - check if the attribute is present and return it. + auto *attr = PyObject_GenericGetAttr(module, name); + // - we handle AttributeError, only. + if (!(attr == nullptr && PyErr_ExceptionMatches(PyExc_AttributeError))) + return attr; + + PyErr_Clear(); + // - locate the module in the moduleTofuncs mapping + auto tableIter = moduleToFuncs.find(module); + // - if this is not our module, use the original + if (tableIter == moduleToFuncs.end()) + return origModuleGetattro(module, name); + + // - locate the name and retrieve the generating function + const char *attrNameStr = Shiboken::String::toCString(name); + auto &nameToFunc = tableIter->second; + // - create the real type (incarnateType checks this) + auto *type = incarnateType(module, attrNameStr, nameToFunc); + auto *ret = reinterpret_cast<PyObject *>(type); + // - if attribute does really not exist use the original + if (ret == nullptr && PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + return origModuleGetattro(module, name); + } + + return ret; +} + +// PYSIDE-2404: Supply a new module dir for not yet visible entries. +// This modification is only for "our" modules. +static PyObject *_module_dir_template(PyObject * /* self */, PyObject *args) +{ + static PyObject *const _dict = Shiboken::String::createStaticString("__dict__"); + // The dir function must replace all of the builtin function. + PyObject *module{}; + if (!PyArg_ParseTuple(args, "O", &module)) + return nullptr; + + auto tableIter = moduleToFuncs.find(module); + assert(tableIter != moduleToFuncs.end()); + Shiboken::AutoDecRef dict(PyObject_GetAttr(module, _dict)); + auto *ret = PyDict_Keys(dict); + // Now add all elements that were not yet in the dict. + auto &nameToFunc = tableIter->second; + for (const auto &funcIter : nameToFunc) { + const char *name = funcIter.first.c_str(); + Shiboken::AutoDecRef pyName(PyUnicode_FromString(name)); + PyList_Append(ret, pyName); + } + return ret; +} + +static PyMethodDef module_methods[] = { + {"__dir__", (PyCFunction)_module_dir_template, METH_VARARGS, nullptr}, + {nullptr, nullptr, 0, nullptr} +}; + +// Python 3.8 - 3.12 +static int const LOAD_CONST_312 = 100; +static int const IMPORT_NAME_312 = 108; + +static bool isImportStar(PyObject *module) +{ + // Find out whether we have a star import. This must work even + // when we have no import support from feature. + static PyObject *const _f_code = Shiboken::String::createStaticString("f_code"); + static PyObject *const _f_lasti = Shiboken::String::createStaticString("f_lasti"); + static PyObject *const _f_back = Shiboken::String::createStaticString("f_back"); + static PyObject *const _co_code = Shiboken::String::createStaticString("co_code"); + static PyObject *const _co_consts = Shiboken::String::createStaticString("co_consts"); + static PyObject *const _co_names = Shiboken::String::createStaticString("co_names"); + + auto *obFrame = reinterpret_cast<PyObject *>(PyEval_GetFrame()); + if (obFrame == nullptr) + return true; // better assume worst-case. + + Py_INCREF(obFrame); + AutoDecRef dec_frame(obFrame); + + // Calculate the offset of the running import_name opcode on the stack. + // Right before that there must be a load_const with the tuple `("*",)`. + while (dec_frame.object() != Py_None) { + AutoDecRef dec_f_code(PyObject_GetAttr(dec_frame, _f_code)); + AutoDecRef dec_co_code(PyObject_GetAttr(dec_f_code, _co_code)); + AutoDecRef dec_f_lasti(PyObject_GetAttr(dec_frame, _f_lasti)); + Py_ssize_t f_lasti = PyLong_AsSsize_t(dec_f_lasti); + Py_ssize_t code_len; + char *co_code{}; + PyBytes_AsStringAndSize(dec_co_code, &co_code, &code_len); + uint8_t opcode2 = co_code[f_lasti]; + uint8_t opcode1 = co_code[f_lasti - 2]; + if (opcode1 == LOAD_CONST_312 && opcode2 == IMPORT_NAME_312) { + uint8_t oparg1 = co_code[f_lasti - 1]; + uint8_t oparg2 = co_code[f_lasti + 1]; + AutoDecRef dec_co_consts(PyObject_GetAttr(dec_f_code, _co_consts)); + auto *fromlist = PyTuple_GetItem(dec_co_consts, oparg1); + if (PyTuple_Check(fromlist) && PyTuple_Size(fromlist) == 1 + && Shiboken::String::toCString(PyTuple_GetItem(fromlist, 0))[0] == '*') { + AutoDecRef dec_co_names(PyObject_GetAttr(dec_f_code, _co_names)); + const char *name = String::toCString(PyTuple_GetItem(dec_co_names, oparg2)); + const char *modName = PyModule_GetName(module); + if (std::strcmp(name, modName) == 0) + return true; + } + } + dec_frame.reset(PyObject_GetAttr(dec_frame, _f_back)); + } + return false; +} + +// PYSIDE-2404: These modules produce ambiguous names which we cannot handle, yet. +static std::unordered_set<std::string> dontLazyLoad{ + "sample", + "smart", + "testbinding" +}; + +static const std::unordered_set<std::string> knownModules{ + "shiboken6.Shiboken", + "minimal", + "other", + "sample", + "smart", + "scriptableapplication", + "testbinding" +}; + +static bool canNotLazyLoad(PyObject *module) +{ + const char *modName = PyModule_GetName(module); + + // There are no more things that must be disabled :-D + return dontLazyLoad.find(modName) != dontLazyLoad.end(); +} + +static bool shouldLazyLoad(PyObject *module) +{ + const char *modName = PyModule_GetName(module); + + if (knownModules.find(modName) != knownModules.end()) + return true; + return std::strncmp(modName, "PySide6.", 8) == 0; +} + +void AddTypeCreationFunction(PyObject *module, + const char *name, + TypeCreationFunction func) +{ + static const char *flag = getenv("PYSIDE6_OPTION_LAZY"); + static const int value = flag != nullptr ? std::atoi(flag) : 1; + + // - locate the module in the moduleTofuncs mapping + auto tableIter = moduleToFuncs.find(module); + assert(tableIter != moduleToFuncs.end()); + // - Assign the name/generating function pair. + auto &nameToFunc = tableIter->second; + TypeCreationFunctionModulePair pair{func, module}; + auto nit = nameToFunc.find(name); + if (nit == nameToFunc.end()) + nameToFunc.insert(std::make_pair(name, pair)); + else + nit->second = pair; + + // PYSIDE-2404: Lazy Loading + // + // Options: + // 0 - switch lazy loading off. + // 1 - lazy loading for all known modules. + // 3 - lazy loading for any module. + // + // By default we lazy load all known modules (option = 1). + + if (value == 0 // completely disabled + || canNotLazyLoad(module) // for some reason we cannot lazy load + || (value == 1 && !shouldLazyLoad(module)) // not a known module + ) { + PyTypeObject *type = func(module); + PyModule_AddObject(module, name, reinterpret_cast<PyObject *>(type)); // steals reference + } +} + +void AddTypeCreationFunction(PyObject *module, + const char *name, + TypeCreationFunction func, + const char *containerName) +{ + // This version could be delayed as well, but for the few cases + // we simply fetch the container type and insert directly. + AutoDecRef obContainerType(PyObject_GetAttrString(module, containerName)); + PyTypeObject *type = func(obContainerType); + PyObject_SetAttrString(obContainerType, name, reinterpret_cast<PyObject *>(type)); // steals reference +} + +void AddTypeCreationFunction(PyObject *module, + const char *name, + TypeCreationFunction func, + const char *outerContainerName, + const char *innerContainerName) +{ + // This version has even more indirection. It is very rare, and + // we handle it directly. + AutoDecRef obOuterType(PyObject_GetAttrString(module, outerContainerName)); + AutoDecRef obInnerType(PyObject_GetAttrString(obOuterType, innerContainerName)); + PyTypeObject *type = func(obInnerType); + PyObject_SetAttrString(obInnerType, name, reinterpret_cast<PyObject *>(type)); // steals reference +} + +void AddTypeCreationFunction(PyObject *module, + const char *name, + TypeCreationFunction func, + const char *containerName3, + const char *containerName2, + const char *containerName) +{ + // This version has even mode indirection. It is very rare, and + // we handle it directly. + AutoDecRef obContainerType3(PyObject_GetAttrString(module, containerName3)); + AutoDecRef obContainerType2(PyObject_GetAttrString(obContainerType3, containerName2)); + AutoDecRef obContainerType(PyObject_GetAttrString(obContainerType2, containerName)); + PyTypeObject *type = func(obContainerType); + PyObject_SetAttrString(obContainerType, name, reinterpret_cast<PyObject *>(type)); // steals reference +} + PyObject *import(const char *moduleName) { PyObject *sysModules = PyImport_GetModuleDict(); PyObject *module = PyDict_GetItemString(sysModules, moduleName); - if (module) + if (module != nullptr) Py_INCREF(module); else module = PyImport_ImportModule(moduleName); - if (!module) - PyErr_Format(PyExc_ImportError,"could not import module '%s'", moduleName); + if (module == nullptr) + PyErr_Format(PyExc_ImportError, "could not import module '%s'", moduleName); return module; } -PyObject *create(const char *moduleName, void *moduleData) +// PYSIDE-2404: Redirecting import for "import *" support. +// +// The first import will be handled by the isImportStar function. +// But the same module might be imported twice, which would give no +// introspection due to module caching. + +static PyObject *origImportFunc{}; + +static PyObject *lazy_import(PyObject * /* self */, PyObject *args, PyObject *kwds) +{ + auto *ret = PyObject_Call(origImportFunc, args, kwds); + if (ret != nullptr) { + // PYSIDE-2404: Support star import when lazy loading. + if (PyTuple_Size(args) >= 4) { + auto *fromlist = PyTuple_GetItem(args, 3); + if (PyTuple_Check(fromlist) && PyTuple_Size(fromlist) == 1 + && Shiboken::String::toCString(PyTuple_GetItem(fromlist, 0))[0] == '*') + Shiboken::Module::resolveLazyClasses(ret); + } + } + return ret; +} + +static PyMethodDef lazy_methods[] = { + {"__lazy_import__", (PyCFunction)lazy_import, METH_VARARGS | METH_KEYWORDS, nullptr}, + {nullptr, nullptr, 0, nullptr} +}; + +PyObject *create(const char * /* modName */, void *moduleData) { + static auto *sysModules = PyImport_GetModuleDict(); + static auto *builtins = PyEval_GetBuiltins(); + static auto *partial = Pep_GetPartialFunction(); + static bool lazy_init{}; + Shiboken::init(); - return PyModule_Create(reinterpret_cast<PyModuleDef *>(moduleData)); + auto *module = PyModule_Create(reinterpret_cast<PyModuleDef *>(moduleData)); + + // Setup of a dir function for "missing" classes. + auto *moduleDirTemplate = PyCFunction_NewEx(module_methods, nullptr, nullptr); + // Turn this function into a bound object, so we have access to the module. + auto *moduleDir = PyObject_CallFunctionObjArgs(partial, moduleDirTemplate, module, nullptr); + PyModule_AddObject(module, module_methods->ml_name, moduleDir); // steals reference + // Insert an initial empty table for the module. + NameToTypeFunctionMap empty; + moduleToFuncs.insert(std::make_pair(module, empty)); + + // A star import must be done unconditionally. Use the complete name. + if (isImportStar(module)) + dontLazyLoad.insert(PyModule_GetName(module)); + + if (!lazy_init) { + // Install the getattr patch. + origModuleGetattro = PyModule_Type.tp_getattro; + PyModule_Type.tp_getattro = PyModule_lazyGetAttro; + // Add the lazy import redirection. + origImportFunc = PyDict_GetItemString(builtins, "__import__"); + auto *func = PyCFunction_NewEx(lazy_methods, nullptr, nullptr); + PyDict_SetItemString(builtins, "__import__", func); + // Everything is set. + lazy_init = true; + } + // PYSIDE-2404: Nuitka inserts some additional code in standalone mode + // in an invisible virtual module (i.e. `QtCore-postLoad`) + // that gets imported before the running import can call + // `_PyImport_FixupExtensionObject` which does the insertion + // into `sys.modules`. This can cause a race condition. + // Insert the module early into the module dict to prevend recursion. + PyDict_SetItemString(sysModules, PyModule_GetName(module), module); + return module; } -void registerTypes(PyObject *module, PyTypeObject **types) +void registerTypes(PyObject *module, TypeInitStruct *types) { auto iter = moduleTypes.find(module); if (iter == moduleTypes.end()) moduleTypes.insert(std::make_pair(module, types)); } -PyTypeObject **getTypes(PyObject *module) +TypeInitStruct *getTypes(PyObject *module) { auto iter = moduleTypes.find(module); return (iter == moduleTypes.end()) ? 0 : iter->second; diff --git a/sources/shiboken6/libshiboken/sbkmodule.h b/sources/shiboken6/libshiboken/sbkmodule.h index 02e4a2706..1b3de33b7 100644 --- a/sources/shiboken6/libshiboken/sbkmodule.h +++ b/sources/shiboken6/libshiboken/sbkmodule.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef SBK_MODULE_H #define SBK_MODULE_H @@ -48,8 +12,22 @@ extern "C" struct SbkConverter; } -namespace Shiboken { -namespace Module { +namespace Shiboken::Module { + +struct TypeInitStruct +{ + PyTypeObject *type; + const char *fullName; +}; + +/// PYSIDE-2404: Replacing the arguments in cpythonTypeNameExt by a function. +LIBSHIBOKEN_API PyTypeObject *get(TypeInitStruct &typeStruct); + +/// PYSIDE-2404: Make sure that mentioned classes really exist. +LIBSHIBOKEN_API void loadLazyClassesWithName(const char *name); + +/// PYSIDE-2404: incarnate all classes for star imports. +LIBSHIBOKEN_API void resolveLazyClasses(PyObject *module); /** * Imports and returns the module named \p moduleName, or a NULL pointer in case of failure. @@ -66,19 +44,43 @@ LIBSHIBOKEN_API PyObject *import(const char *moduleName); */ LIBSHIBOKEN_API PyObject *create(const char *moduleName, void *moduleData); +using TypeCreationFunction = PyTypeObject *(*)(PyObject *module); + +/// Adds a type creation function to the module. +LIBSHIBOKEN_API void AddTypeCreationFunction(PyObject *module, + const char *name, + TypeCreationFunction func); + +LIBSHIBOKEN_API void AddTypeCreationFunction(PyObject *module, + const char *name, + TypeCreationFunction func, + const char *containerName); + +LIBSHIBOKEN_API void AddTypeCreationFunction(PyObject *module, + const char *name, + TypeCreationFunction func, + const char *outerContainerName, + const char *innerContainerName); + +LIBSHIBOKEN_API void AddTypeCreationFunction(PyObject *module, + const char *name, + TypeCreationFunction func, + const char *containerName3, + const char *containerName2, + const char *containerName); /** * 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); +LIBSHIBOKEN_API void registerTypes(PyObject *module, TypeInitStruct *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); +LIBSHIBOKEN_API TypeInitStruct *getTypes(PyObject *module); /** * Registers the list of converters created by \p module for non-wrapper types. @@ -94,6 +96,6 @@ LIBSHIBOKEN_API void registerTypeConverters(PyObject *module, SbkConverter **con */ LIBSHIBOKEN_API SbkConverter **getTypeConverters(PyObject *module); -} } // namespace Shiboken::Module +} // namespace Shiboken::Module #endif // SBK_MODULE_H diff --git a/sources/shiboken6/libshiboken/sbknumpy.cpp b/sources/shiboken6/libshiboken/sbknumpy.cpp new file mode 100644 index 000000000..2e1c64d73 --- /dev/null +++ b/sources/shiboken6/libshiboken/sbknumpy.cpp @@ -0,0 +1,59 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + + +#ifdef HAVE_NUMPY +// Include numpy first to get the proper PyArray_Check +# include <numpy/arrayobject.h> +#endif + +#include "helper.h" +#include "sbknumpycheck.h" +#include "sbkcpptonumpy.h" +#include "sbknumpyview.h" + +#include <algorithm> + +namespace Shiboken::Numpy +{ + +#ifdef HAVE_NUMPY +static void initNumPy() +{ + // PYSIDE-2404: Delay-initialize numpy from check() as it causes a + // significant startup delay (~770 allocations in memray) + static bool initialized = false; + if (initialized) + return; + initialized = true; + // Expanded from macro "import_array" in __multiarray_api.h + // Make sure to read about the magic defines PY_ARRAY_UNIQUE_SYMBOL etc., + // when changing this or spreading the code over several source files. + if (_import_array() < 0) { + PyErr_Print(); + PyErr_Clear(); + } +} +#endif // HAVE_NUMPY + +bool check(PyObject *pyIn) +{ +#ifdef HAVE_NUMPY + initNumPy(); + return PyArray_Check(pyIn); +#else + SBK_UNUSED(pyIn); + return false; +#endif +} + +} //namespace Shiboken::Numpy + +// Include all sources files using numpy so that they are in the same +// translation unit (see comment at initNumPyArrayConverters()). + +#include "sbknumpyview.cpp" +#include "sbkcpptonumpy.cpp" +#ifdef HAVE_NUMPY +# include "sbknumpyarrayconverter.cpp" +#endif diff --git a/sources/shiboken6/libshiboken/sbknumpyarrayconverter.cpp b/sources/shiboken6/libshiboken/sbknumpyarrayconverter.cpp index 996968fa1..835a97524 100644 --- a/sources/shiboken6/libshiboken/sbknumpyarrayconverter.cpp +++ b/sources/shiboken6/libshiboken/sbknumpyarrayconverter.cpp @@ -1,41 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// included by sbknumpy.cpp #include "sbkarrayconverter.h" #include "helper.h" @@ -43,8 +9,6 @@ #include "sbkconverter_p.h" #include "sbkarrayconverter_p.h" -#include <numpy/arrayobject.h> - #include <algorithm> #include <iostream> #include <cstdint> @@ -116,8 +80,13 @@ std::ostream &operator<<(std::ostream &str, PyArrayObject *o) str << " NPY_ARRAY_NOTSWAPPED"; if ((flags & NPY_ARRAY_WRITEABLE) != 0) str << " NPY_ARRAY_WRITEABLE"; +#if NPY_VERSION >= 0x00000010 // NPY_1_23_API_VERSION + if ((flags & NPY_ARRAY_WRITEBACKIFCOPY) != 0) + str << " NPY_ARRAY_WRITEBACKIFCOPY"; +#else if ((flags & NPY_ARRAY_UPDATEIFCOPY) != 0) str << " NPY_ARRAY_UPDATEIFCOPY"; +#endif } else { str << '0'; } @@ -125,8 +94,7 @@ std::ostream &operator<<(std::ostream &str, PyArrayObject *o) return str; } -namespace Shiboken { -namespace Conversions { +namespace Shiboken::Conversions { // Internals from sbkarrayconverter.cpp SbkArrayConverter *createArrayConverter(IsArrayConvertibleToCppFunc toCppCheckFunc); @@ -136,6 +104,7 @@ SbkArrayConverter *unimplementedArrayConverter(); template <int dimension> static bool isPrimitiveArray(PyObject *pyIn, int expectedNpType) { + Shiboken::Numpy::initNumPy(); if (!PyArray_Check(pyIn)) return false; auto *pya = reinterpret_cast<PyArrayObject *>(pyIn); @@ -241,6 +210,9 @@ static PythonToCppFunc checkArray2(PyObject *pyIn, int dim1, int dim2) template <class T> static void setOrExtendArrayConverter(int dimension, IsArrayConvertibleToCppFunc toCppCheckFunc) { + // PYSIDE-2404/FIXME: When adding a C++ -> Python conversion, be sure + // to delay-initialize numpy in the converter (similar to the + // initialization in check() for the Python -> C++ conversion). SbkArrayConverter *arrayConverter = ArrayTypeConverter<T>(dimension); if (arrayConverter == unimplementedArrayConverter()) { arrayConverter = createArrayConverter(toCppCheckFunc); @@ -266,15 +238,6 @@ static inline void extendArrayConverter2() void initNumPyArrayConverters() { - // Expanded from macro "import_array" in __multiarray_api.h - // Make sure to read about the magic defines PY_ARRAY_UNIQUE_SYMBOL etc., - // when changing this or spreading the code over several source files. - if (_import_array() < 0) { - if (debugNumPy) - PyErr_Print(); - PyErr_Clear(); - return; - } // Extend the converters for primitive types by NumPy ones. extendArrayConverter1<short, NPY_SHORT>(); extendArrayConverter2<short, NPY_SHORT>(); @@ -304,5 +267,4 @@ void initNumPyArrayConverters() extendArrayConverter2<double, NPY_DOUBLE>(); } -} // namespace Conversions -} // namespace Shiboken +} // namespace Shiboken::Conversions diff --git a/sources/shiboken6/libshiboken/sbknumpycheck.h b/sources/shiboken6/libshiboken/sbknumpycheck.h new file mode 100644 index 000000000..cfe65372c --- /dev/null +++ b/sources/shiboken6/libshiboken/sbknumpycheck.h @@ -0,0 +1,30 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef SBKNUMPYCHECK_H +#define SBKNUMPYCHECK_H + +#include <sbkpython.h> +#include <shibokenmacros.h> + + +// This header provides a PyArray_Check() definition that can be used to avoid +// having to include the numpy headers. When using numpy headers, make sure +// to include this header after them to skip the definition. Also remember +// that import_array() must then be called to initialize numpy. + +namespace Shiboken::Numpy +{ + +/// Check whether the object is a PyArrayObject +/// \param pyIn object +/// \return Whether it is a PyArrayObject +LIBSHIBOKEN_API bool check(PyObject *pyIn); + +} //namespace Shiboken::Numpy + +#ifndef PyArray_Check +# define PyArray_Check(op) Shiboken::Numpy::check(op) +#endif + +#endif // SBKNUMPYCHECK_H diff --git a/sources/shiboken6/libshiboken/sbknumpyview.cpp b/sources/shiboken6/libshiboken/sbknumpyview.cpp new file mode 100644 index 000000000..bafbf8038 --- /dev/null +++ b/sources/shiboken6/libshiboken/sbknumpyview.cpp @@ -0,0 +1,265 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// included by sbknumpy.cpp + +#include "helper.h" +#include <iostream> +#include <iomanip> +#include <optional> + +#ifdef HAVE_NUMPY + +namespace Shiboken { +namespace Numpy { + +static std::optional<View::Type> viewTypeFromNumPy(int npt) +{ + switch (npt) { + case NPY_SHORT: + return View::Int16; + case NPY_USHORT: + return View::Unsigned16; + case NPY_INT: + return View::Int; + case NPY_UINT: + return View::Unsigned; + case NPY_LONG: + if constexpr (sizeof(long) == sizeof(int)) + return View::Int; + if constexpr (sizeof(long) == sizeof(int64_t)) + return View::Int64; + break; + case NPY_ULONG: + if constexpr (sizeof(long) == sizeof(int)) + return View::Unsigned; + if constexpr (sizeof(long) == sizeof(int64_t)) + return View::Unsigned64; + break; + case NPY_LONGLONG: + if constexpr (sizeof(long long) == 8) + return View::Int64; + break; + case NPY_ULONGLONG: + if constexpr (sizeof(long long) == 8) + return View::Unsigned64; + break; + case NPY_FLOAT: + return View::Float; + case NPY_DOUBLE: + return View::Double; + default: + break; + } + return {}; +} + +View View::fromPyObject(PyObject *pyIn) +{ + if (pyIn == nullptr || PyArray_Check(pyIn) == 0) + return {}; + auto *ar = reinterpret_cast<PyArrayObject *>(pyIn); + if ((PyArray_FLAGS(ar) & NPY_ARRAY_C_CONTIGUOUS) == 0) + return {}; + const int ndim = PyArray_NDIM(ar); + if (ndim > 2) + return {}; + + const auto typeO = viewTypeFromNumPy(PyArray_TYPE(ar)); + if (!typeO.has_value()) + return {}; + + View result; + result.ndim = ndim; + result.type = typeO.value(); + result.data = PyArray_DATA(ar); + result.dimensions[0] = PyArray_DIMS(ar)[0]; + result.stride[0] = PyArray_STRIDES(ar)[0]; + if (ndim > 1) { + result.dimensions[1] = PyArray_DIMS(ar)[1]; + result.stride[1] = PyArray_STRIDES(ar)[1]; + } else { + result.dimensions[1] = result.stride[1] = 0; + } + return result; +} + +} // namespace Numpy + +template <class T> +static void debugArray(std::ostream &str, const T *data, int n) +{ + static const int maxData = 10; + str << " = "; + auto *end = data + std::min(n, maxData); + for (auto *d = data; d != end; ++d) { + if (d != data) + str << ", "; + str << *d; + } + if (n > maxData) + str << "..."; +} + +std::ostream &operator<<(std::ostream &str, const debugPyArrayObject &a) +{ + str << "PyArrayObject("; + if (a.m_object == nullptr) { + str << '0'; + } else if (PyArray_Check(a.m_object) != 0) { + auto *ar = reinterpret_cast<PyArrayObject *>(a.m_object); + const int ndim = PyArray_NDIM(ar); + const int type = PyArray_TYPE(ar); + const int flags = PyArray_FLAGS(ar); + str << "ndim=" << ndim << " ["; + for (int d = 0; d < ndim; ++d) { + if (d) + str << ", "; + str << PyArray_DIMS(ar)[d]; + } + str << "], type="; + switch (type) { + case NPY_SHORT: + str << "short"; + break; + case NPY_USHORT: + str << "ushort"; + break; + case NPY_INT: + str << "int32"; + break; + case NPY_UINT: + str << "uint32"; + break; + case NPY_LONG: + str << "long"; + break; + case NPY_ULONG: + str << "ulong"; + break; + case NPY_LONGLONG: + str << "long long"; + break; + case NPY_ULONGLONG: + str << "ulong long"; + break; + case NPY_FLOAT: + str << "float"; + break; + case NPY_DOUBLE: + str << "double"; + break; + default: + str << '(' << type << ')'; + break; + } + str << ", flags=0x" << std::hex << flags << std::dec; + if ((flags & NPY_ARRAY_C_CONTIGUOUS) != 0) + str << " [C-contiguous]"; + if ((flags & NPY_ARRAY_F_CONTIGUOUS) != 0) + str << " [Fortran-contiguous]"; + if ((flags & NPY_ARRAY_ALIGNED) != 0) + str << " [aligned]"; + if ((flags & NPY_ARRAY_OWNDATA) != 0) + str << " [owndata]"; + if ((flags & NPY_ARRAY_WRITEABLE) != 0) + str << " [writeable]"; + + if (const int dim0 = PyArray_DIMS(ar)[0]) { + auto *data = PyArray_DATA(ar); + switch (type) { + case NPY_SHORT: + debugArray(str, reinterpret_cast<const short *>(data), dim0); + break; + case NPY_USHORT: + debugArray(str, reinterpret_cast<const unsigned short *>(data), dim0); + break; + case NPY_INT: + debugArray(str, reinterpret_cast<const int *>(data), dim0); + break; + case NPY_UINT: + debugArray(str, reinterpret_cast<const unsigned *>(data), dim0); + break; + case NPY_LONG: + debugArray(str, reinterpret_cast<const long *>(data), dim0); + break; + case NPY_ULONG: + debugArray(str, reinterpret_cast<const unsigned long*>(data), dim0); + break; + case NPY_LONGLONG: + debugArray(str, reinterpret_cast<const long long *>(data), dim0); + break; + case NPY_ULONGLONG: + debugArray(str, reinterpret_cast<const unsigned long long *>(data), dim0); + break; + case NPY_FLOAT: + debugArray(str, reinterpret_cast<const float *>(data), dim0); + break; + case NPY_DOUBLE: + debugArray(str, reinterpret_cast<const double *>(data), dim0); + break; + } + } + } else { + str << "Invalid"; + } + str << ')'; + return str; +} + +} //namespace Shiboken + +#else // HAVE_NUMPY + +namespace Shiboken::Numpy +{ + +View View::fromPyObject(PyObject *) +{ + return {}; +} + +std::ostream &operator<<(std::ostream &str, const debugPyArrayObject &) +{ + str << "Unimplemented function " << __FUNCTION__ << ", (numpy was not found)."; + return str; +} + +} //namespace Shiboken::Numpy + +#endif // !HAVE_NUMPY + +namespace Shiboken::Numpy +{ + +bool View::sameLayout(const View &rhs) const +{ + return rhs && *this && ndim == rhs.ndim && type == rhs.type; +} + +bool View::sameSize(const View &rhs) const +{ + return sameLayout(rhs) + && dimensions[0] == rhs.dimensions[0] && dimensions[1] == rhs.dimensions[1]; +} + +std::ostream &operator<<(std::ostream &str, const View &v) +{ + str << "Shiboken::Numpy::View("; + if (v) { + str << "type=" << v.type << ", ndim=" << v.ndim << " [" + << v.dimensions[0]; + if (v.ndim > 1) + str << ", " << v.dimensions[1]; + str << "], stride=[" << v.stride[0]; + if (v.ndim > 1) + str << ", " << v.stride[1]; + str << "], data=" << v.data; + } else { + str << "invalid"; + } + str << ')'; + return str; +} + +} //namespace Shiboken::Numpy diff --git a/sources/shiboken6/libshiboken/sbknumpyview.h b/sources/shiboken6/libshiboken/sbknumpyview.h new file mode 100644 index 000000000..918913b78 --- /dev/null +++ b/sources/shiboken6/libshiboken/sbknumpyview.h @@ -0,0 +1,47 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef SBKNUMPYVIEW_H +#define SBKNUMPYVIEW_H + +#include <sbkpython.h> +#include <shibokenmacros.h> + +#include <iosfwd> + +namespace Shiboken::Numpy +{ + +/// Check whether the object is a PyArrayObject +/// \param pyIn object +/// \return Whether it is a PyArrayObject +LIBSHIBOKEN_API bool check(PyObject *pyIn); + +/// A simple view of an up to 2 dimensional, C-contiguous array of a standard +/// type. It can be passed to compilation units that do not include the +/// numpy headers. +struct LIBSHIBOKEN_API View +{ + enum Type { Int, Unsigned, Float, Double, Int16, Unsigned16, Int64, Unsigned64 }; + + static View fromPyObject(PyObject *pyIn); + + operator bool() const { return ndim > 0; } + + /// Return whether rhs is of the same type and dimensionality + bool sameLayout(const View &rhs) const; + /// Return whether rhs is of the same type dimensionality and size + bool sameSize(const View &rhs) const; + + int ndim = 0; + Py_ssize_t dimensions[2]; + Py_ssize_t stride[2]; + void *data = nullptr; + Type type = Int; +}; + +LIBSHIBOKEN_API std::ostream &operator<<(std::ostream &, const View &v); + +} //namespace Shiboken::Numpy + +#endif // SBKNUMPYVIEW_H diff --git a/sources/shiboken6/libshiboken/sbkpython.h b/sources/shiboken6/libshiboken/sbkpython.h index 772fe76ac..e62fa13ae 100644 --- a/sources/shiboken6/libshiboken/sbkpython.h +++ b/sources/shiboken6/libshiboken/sbkpython.h @@ -1,47 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef SBKPYTHON_H #define SBKPYTHON_H #include "sbkversion.h" -#define PyEnumMeta_Check(x) (strcmp(Py_TYPE(x)->tp_name, "EnumMeta") == 0) // Qt's "slots" macro collides with the "slots" member variables // used in some Python structs. For compilers that support push_macro, @@ -101,28 +64,12 @@ extern "C" { # include "pep384impl.h" #endif -#define PyInt_Type PyLong_Type -#define PyInt_Check PyLong_Check -#define PyInt_CheckExact PyLong_CheckExact -#define PyInt_FromString PyLong_FromString -#define PyInt_FromSsize_t PyLong_FromSsize_t -#define PyInt_FromSize_t PyLong_FromSize_t -#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 - // In Python 3, Py_TPFLAGS_DEFAULT contains Py_TPFLAGS_HAVE_VERSION_TAG, // which will trigger the attribute cache, which is not intended in Qt for Python. // Use a customized Py_TPFLAGS_DEFAULT by defining Py_TPFLAGS_HAVE_VERSION_TAG = 0. #undef Py_TPFLAGS_HAVE_VERSION_TAG #define Py_TPFLAGS_HAVE_VERSION_TAG (0) +using SbkObjectType [[deprecated]] = PyTypeObject; // FIXME PYSIDE 7 remove + #endif diff --git a/sources/shiboken6/libshiboken/sbksmartpointer.cpp b/sources/shiboken6/libshiboken/sbksmartpointer.cpp new file mode 100644 index 000000000..ee28f7db8 --- /dev/null +++ b/sources/shiboken6/libshiboken/sbksmartpointer.cpp @@ -0,0 +1,58 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "sbksmartpointer.h" +#include "sbkstring.h" +#include "autodecref.h" + +#include <unordered_set> + +namespace Shiboken::SmartPointer +{ + +PyObject *repr(PyObject *pointer, PyObject *pointee) +{ + Shiboken::AutoDecRef pointerRepr(Shiboken::String::repr(pointer)); + if (pointer == nullptr) + return pointerRepr.release(); + + Shiboken::AutoDecRef pointeeRepr(pointee != nullptr + ? PyObject_Repr(pointee) + : Shiboken::String::repr(pointee)); + + return PyUnicode_FromFormat("%U (%U)", pointerRepr.object(), pointeeRepr.object()); +} + +// __dir__ for a smart pointer. Add the __dir__ entries of the pointee to the list. +PyObject *dir(PyObject *pointer, PyObject *pointee) +{ + if (pointer == nullptr) + return PyList_New(0); + // Get the pointer's dir entries. Note: PyObject_Dir() cannot be called on + // self, will crash. Work around by using the type dict keys. + AutoDecRef tpDict(PepType_GetDict(Py_TYPE(pointer))); + auto *result = PyMapping_Keys(tpDict); + + if (pointee != nullptr && pointee != Py_None) { + // Add the entries of the pointee that do not exist in the pointer's list. + // Since Python internally caches strings; we can use a set of PyObject *. + std::unordered_set<PyObject *> knownStrings; + for (Py_ssize_t i = 0, size = PySequence_Size(result); i < size; ++i) { + Shiboken::AutoDecRef item(PySequence_GetItem(result, i)); + knownStrings.insert(item.object()); + } + const auto knownEnd = knownStrings.end(); + + Shiboken::AutoDecRef pointeeDir(PyObject_Dir(pointee)); + for (Py_ssize_t i = 0, size = PySequence_Size(pointeeDir.object()); i < size; ++i) { + Shiboken::AutoDecRef item(PySequence_GetItem(pointeeDir, i)); + if (knownStrings.find(item.object()) == knownEnd) + PyList_Append(result, item.object()); + } + } + + PyList_Sort(result); + return result; +} + +} // namespace Shiboken::SmartPointer diff --git a/sources/shiboken6/libshiboken/sbksmartpointer.h b/sources/shiboken6/libshiboken/sbksmartpointer.h new file mode 100644 index 000000000..5e2022722 --- /dev/null +++ b/sources/shiboken6/libshiboken/sbksmartpointer.h @@ -0,0 +1,18 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef SBK_SBKSMARTPOINTER_H +#define SBK_SBKSMARTPOINTER_H + +#include "sbkpython.h" +#include "shibokenmacros.h" + +namespace Shiboken::SmartPointer +{ + +LIBSHIBOKEN_API PyObject *repr(PyObject *pointer, PyObject *pointee); +LIBSHIBOKEN_API PyObject *dir(PyObject *pointer, PyObject *pointee); + +} // namespace Shiboken::SmartPointer + +#endif // SBK_SBKSMARTPOINTER_H diff --git a/sources/shiboken6/libshiboken/sbkstaticstrings.cpp b/sources/shiboken6/libshiboken/sbkstaticstrings.cpp index 7b2ddfb3e..023de0ea4 100644 --- a/sources/shiboken6/libshiboken/sbkstaticstrings.cpp +++ b/sources/shiboken6/libshiboken/sbkstaticstrings.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "sbkstaticstrings.h" #include "sbkstaticstrings_p.h" @@ -60,12 +24,13 @@ STATIC_STRING_IMPL(im_self, "im_self") STATIC_STRING_IMPL(loads, "loads") STATIC_STRING_IMPL(multi, "multi") STATIC_STRING_IMPL(name, "name") +STATIC_STRING_IMPL(orig_dict, "orig_dict") STATIC_STRING_IMPL(qApp, "qApp") STATIC_STRING_IMPL(result, "result") STATIC_STRING_IMPL(select_id, "select_id") -STATIC_STRING_IMPL(underscore, "_") STATIC_STRING_IMPL(value, "value") STATIC_STRING_IMPL(values, "values") +STATIC_STRING_IMPL(qtStaticMetaObject, "staticMetaObject") // Internal: STATIC_STRING_IMPL(classmethod, "classmethod") @@ -96,12 +61,14 @@ STATIC_STRING_IMPL(property_methods, "__property_methods__") STATIC_STRING_IMPL(qualname, "__qualname__") STATIC_STRING_IMPL(self, "__self__") STATIC_STRING_IMPL(select_i, "__self__") +STATIC_STRING_IMPL(code, "__code__") +STATIC_STRING_IMPL(rlshift, "__rlshift__") +STATIC_STRING_IMPL(rrshift, "__rrshift__") // Internal: STATIC_STRING_IMPL(base, "__base__") STATIC_STRING_IMPL(bases, "__bases__") STATIC_STRING_IMPL(builtins, "__builtins__") -STATIC_STRING_IMPL(code, "__code__") STATIC_STRING_IMPL(dictoffset, "__dictoffset__") STATIC_STRING_IMPL(func, "__func__") STATIC_STRING_IMPL(func_kind, "__func_kind__") @@ -109,7 +76,13 @@ STATIC_STRING_IMPL(iter, "__iter__") STATIC_STRING_IMPL(mro, "__mro__") STATIC_STRING_IMPL(new_, "__new__") STATIC_STRING_IMPL(objclass, "__objclass__") -STATIC_STRING_IMPL(signature, "__signature__") STATIC_STRING_IMPL(weakrefoffset, "__weakrefoffset__") +STATIC_STRING_IMPL(opaque_container, "__opaque_container__") } // namespace PyMagicName + +namespace Messages +{ +STATIC_STRING_IMPL(unknownException, "An unknown exception was caught") +} + } // namespace Shiboken diff --git a/sources/shiboken6/libshiboken/sbkstaticstrings.h b/sources/shiboken6/libshiboken/sbkstaticstrings.h index 773ff4e6c..017790ee3 100644 --- a/sources/shiboken6/libshiboken/sbkstaticstrings.h +++ b/sources/shiboken6/libshiboken/sbkstaticstrings.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef SBKSTATICSTRINGS_H #define SBKSTATICSTRINGS_H @@ -59,11 +23,12 @@ LIBSHIBOKEN_API PyObject *im_self(); LIBSHIBOKEN_API PyObject *loads(); LIBSHIBOKEN_API PyObject *multi(); LIBSHIBOKEN_API PyObject *name(); +LIBSHIBOKEN_API PyObject *orig_dict(); LIBSHIBOKEN_API PyObject *result(); LIBSHIBOKEN_API PyObject *select_id(); -LIBSHIBOKEN_API PyObject *underscore(); LIBSHIBOKEN_API PyObject *value(); LIBSHIBOKEN_API PyObject *values(); +LIBSHIBOKEN_API PyObject *qtStaticMetaObject(); } // namespace PyName namespace PyMagicName @@ -81,7 +46,16 @@ LIBSHIBOKEN_API PyObject *name(); LIBSHIBOKEN_API PyObject *property_methods(); LIBSHIBOKEN_API PyObject *qualname(); LIBSHIBOKEN_API PyObject *self(); +LIBSHIBOKEN_API PyObject *opaque_container(); +LIBSHIBOKEN_API PyObject *code(); +LIBSHIBOKEN_API PyObject *rlshift(); +LIBSHIBOKEN_API PyObject *rrshift(); } // namespace PyMagicName + +namespace Messages +{ +LIBSHIBOKEN_API PyObject *unknownException(); +} // Messages } // namespace Shiboken #endif // SBKSTATICSTRINGS_H diff --git a/sources/shiboken6/libshiboken/sbkstaticstrings_p.h b/sources/shiboken6/libshiboken/sbkstaticstrings_p.h index 308966481..2a337bf7e 100644 --- a/sources/shiboken6/libshiboken/sbkstaticstrings_p.h +++ b/sources/shiboken6/libshiboken/sbkstaticstrings_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "sbkpython.h" #include "shibokenmacros.h" diff --git a/sources/shiboken6/libshiboken/sbkstring.cpp b/sources/shiboken6/libshiboken/sbkstring.cpp index 177b946fa..1471cd7fe 100644 --- a/sources/shiboken6/libshiboken/sbkstring.cpp +++ b/sources/shiboken6/libshiboken/sbkstring.cpp @@ -1,49 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "sbkstring.h" #include "sbkstaticstrings_p.h" #include "autodecref.h" -#include <vector> -#include <unordered_set> - namespace Shiboken::String { @@ -107,23 +68,33 @@ PyObject *fromCString(const char *value, int len) return PyUnicode_FromStringAndSize(value, len); } -const char *toCString(PyObject *str, Py_ssize_t *len) +const char *toCString(PyObject *str) { if (str == Py_None) return nullptr; + if (PyUnicode_Check(str)) + return _PepUnicode_AsString(str); + if (PyBytes_Check(str)) + return PyBytes_AS_STRING(str); + return nullptr; +} + +const char *toCString(PyObject *str, Py_ssize_t *len) +{ + if (str == Py_None) { + *len = 0; + return nullptr; + } 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()); - } + // 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 _PepUnicode_AsString(str); } if (PyBytes_Check(str)) { - if (len) - *len = PyBytes_GET_SIZE(str); + *len = PyBytes_GET_SIZE(str); return PyBytes_AS_STRING(str); } return nullptr; @@ -205,50 +176,9 @@ Py_ssize_t len(PyObject *str) // PyObject *attr = PyObject_GetAttr(obj, name()); // -using StaticStrings = std::unordered_set<PyObject *>; - -static void finalizeStaticStrings(); // forward - -static StaticStrings &staticStrings() -{ - static StaticStrings result; - return result; -} - -static void finalizeStaticStrings() -{ - auto &set = staticStrings(); - for (PyObject *ob : set) { - Py_REFCNT(ob) = 1; - Py_DECREF(ob); - } - set.clear(); -} - PyObject *createStaticString(const char *str) { - static bool initialized = false; - if (!initialized) { - Py_AtExit(finalizeStaticStrings); - initialized = true; - } - PyObject *result = PyUnicode_InternFromString(str); - if (result == nullptr) { - // This error is never checked, but also very unlikely. Report and exit. - PyErr_Print(); - Py_FatalError("unexpected error in createStaticString()"); - } - auto it = staticStrings().find(result); - if (it == staticStrings().end()) - staticStrings().insert(result); - /* - * Note: We always add one reference even if we have a new string. - * This makes the strings immortal, and we are safe if someone - * uses AutoDecRef, although the set cannot cope with deletions. - * The exit handler cleans that up, anyway. - */ - Py_INCREF(result); - return result; + return PyUnicode_InternFromString(str); } /////////////////////////////////////////////////////////////////////// @@ -303,4 +233,16 @@ PyObject *getSnakeCaseName(PyObject *name, bool lower) return name; } +// Return a generic representation of a PyObject as does PyObject_Repr(). +// Note: PyObject_Repr() may not be called on self from __repr__() as this +// causes a recursion. +PyObject *repr(PyObject *o) +{ + if (o == nullptr) + return PyUnicode_FromString("<NULL>"); + if (o == Py_None) + return PyUnicode_FromString("None"); + return PyUnicode_FromFormat("<%s object at %p>", Py_TYPE(o)->tp_name, o); +} + } // namespace Shiboken::String diff --git a/sources/shiboken6/libshiboken/sbkstring.h b/sources/shiboken6/libshiboken/sbkstring.h index 4b416e16c..f91847c11 100644 --- a/sources/shiboken6/libshiboken/sbkstring.h +++ b/sources/shiboken6/libshiboken/sbkstring.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef SBKSTRING_H #define SBKSTRING_H @@ -55,7 +19,8 @@ namespace String 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 = nullptr); + LIBSHIBOKEN_API const char *toCString(PyObject *str); + LIBSHIBOKEN_API const char *toCString(PyObject *str, Py_ssize_t *len); 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); @@ -64,6 +29,7 @@ namespace String LIBSHIBOKEN_API PyObject *createStaticString(const char *str); LIBSHIBOKEN_API PyObject *getSnakeCaseName(const char *name, bool lower); LIBSHIBOKEN_API PyObject *getSnakeCaseName(PyObject *name, bool lower); + LIBSHIBOKEN_API PyObject *repr(PyObject *o); } // namespace String } // namespace Shiboken diff --git a/sources/shiboken6/libshiboken/sbktypefactory.cpp b/sources/shiboken6/libshiboken/sbktypefactory.cpp new file mode 100644 index 000000000..079548eed --- /dev/null +++ b/sources/shiboken6/libshiboken/sbktypefactory.cpp @@ -0,0 +1,407 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "sbktypefactory.h" +#include "shiboken.h" + +extern "C" +{ + +using Shiboken::AutoDecRef; + +PyTypeObject *SbkType_FromSpec(PyType_Spec *spec) +{ + return SbkType_FromSpec_BMDWB(spec, nullptr, nullptr, 0, 0, nullptr); +} + +PyTypeObject *SbkType_FromSpecWithMeta(PyType_Spec *spec, PyTypeObject *meta) +{ + return SbkType_FromSpec_BMDWB(spec, nullptr, meta, 0, 0, nullptr); +} + +PyTypeObject *SbkType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) +{ + return SbkType_FromSpec_BMDWB(spec, bases, nullptr, 0, 0, nullptr); +} + +PyTypeObject *SbkType_FromSpecBasesMeta(PyType_Spec *spec, PyObject *bases, PyTypeObject *meta) +{ + return SbkType_FromSpec_BMDWB(spec, bases, meta, 0, 0, nullptr); +} + +#ifdef PYPY_VERSION + +static PyObject *_PyType_FromSpecWithBases(PyType_Spec *, PyObject *); + +#else + +#define _PyType_FromSpecWithBases PyType_FromSpecWithBases + +#endif // PYPY_VERSION + +// PYSIDE-2230: Not so temporary fix for Python 3.12. +// A tp_new is no longer allowed in a meta class. +// Hopefully, the Python devs will supply the missing support. +// It turned out that they will not fix that, as expected. +// Note: Python 3.12 is the first version that grabs the metaclass from base classes. +static PyObject *_PyType_FromSpecWithBasesHack(PyType_Spec *spec, + PyObject *bases, + PyTypeObject *meta) +{ + PyTypeObject *keepMeta{}; + newfunc keepNew{}; + AutoDecRef basesPatch{}; + + if (bases) { + if (bases == Py_None) { + // PYSIDE-2230: This is the SbkObject entry which has no base to provide + // the metaclass. We patch it in by modifying `object`s class. + assert(meta); + auto *base = reinterpret_cast<PyObject *>(&PyBaseObject_Type); + base->ob_type = meta; + basesPatch.reset(Py_BuildValue("(O)", &PyBaseObject_Type)); + bases = basesPatch.object(); + } + + Py_ssize_t n = PyTuple_GET_SIZE(bases); + for (auto idx = 0; idx < n; ++idx) { + PyTypeObject *base = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(bases, idx)); + PyTypeObject *meta = Py_TYPE(base); + if (meta->tp_new != PyType_Type.tp_new) { + // make sure there is no second meta class + assert(keepMeta == nullptr); + keepMeta = meta; + keepNew = meta->tp_new; + meta->tp_new = PyType_Type.tp_new; + } + } + } + +#if !defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x030C0000 + auto *ret = PyType_FromMetaclass(meta, nullptr /*module*/, spec, bases); +#else + auto *ret = _PyType_FromSpecWithBases(spec, bases); +#endif + + if (keepMeta) + keepMeta->tp_new = keepNew; + if (basesPatch.object()) { + // undo the metaclass patch. + auto *base = PyTuple_GET_ITEM(basesPatch.object(), 0); + base->ob_type = &PyType_Type; + } + return ret; +} + +PyTypeObject *SbkType_FromSpec_BMDWB(PyType_Spec *spec, + PyObject *bases, + PyTypeObject *meta, + int dictoffset, + int weaklistoffset, + PyBufferProcs *bufferprocs) +{ + // PYSIDE-1286: Generate correct __module__ and __qualname__ + // The name field can now be extended by an "n:" prefix which is + // the number of modules in the name. The default is 1. + // + // Example: + // "2:mainmod.submod.mainclass.subclass" + // results in + // __module__ : "mainmod.submod" + // __qualname__ : "mainclass.subclass" + // __name__ : "subclass" + + PyType_Spec new_spec = *spec; + const char *colon = strchr(spec->name, ':'); + assert(colon); + int package_level = atoi(spec->name); + const char *mod = new_spec.name = colon + 1; + + PyObject *obType = _PyType_FromSpecWithBasesHack(&new_spec, bases, meta); + if (obType == nullptr) + return nullptr; + + const char *qual = mod; + for (int idx = package_level; idx > 0; --idx) { + const char *dot = strchr(qual, '.'); + if (!dot) + break; + qual = dot + 1; + } + int mlen = qual - mod - 1; + AutoDecRef module(Shiboken::String::fromCString(mod, mlen)); + AutoDecRef qualname(Shiboken::String::fromCString(qual)); + + auto *type = reinterpret_cast<PyTypeObject *>(obType); + + if (meta) { + PyTypeObject *hold = Py_TYPE(type); + obType->ob_type = meta; + Py_INCREF(Py_TYPE(type)); + if (hold->tp_flags & Py_TPFLAGS_HEAPTYPE) + Py_DECREF(hold); + } + + if (dictoffset) + type->tp_dictoffset = dictoffset; + if (weaklistoffset) + type->tp_weaklistoffset = weaklistoffset; + if (bufferprocs) + PepType_AS_BUFFER(type) = bufferprocs; + +#ifdef PYPY_VERSION + // PYSIDE-535: Careful: Using PyObject_SetAttr would have the side-effect of calling + // PyType_Ready too early. (at least in PyPy, which caused pretty long debugging.) + auto *ht = reinterpret_cast<PyHeapTypeObject *>(type); + ht->ht_qualname = qualname; + AutoDecRef tpDict(PepType_GetDict(type)); + if (PyDict_SetItem(tpDict.object(), Shiboken::PyMagicName::qualname(), qualname)) + return nullptr; + if (PyDict_SetItem(tpDict.object(), Shiboken::PyMagicName::module(), module)) + return nullptr; + PyType_Ready(type); +#else + if (PyObject_SetAttr(obType, Shiboken::PyMagicName::module(), module) < 0) + return nullptr; + if (PyObject_SetAttr(obType, Shiboken::PyMagicName::qualname(), qualname) < 0) + return nullptr; + PyType_Modified(type); +#endif + return type; +} + +#ifdef PYPY_VERSION + +///////////////////////////////////////////////////////////////////////////// +// +// Reimplementation of `PyType_FromSpecWithBases` +// +// This is almost the original code from Python 3.7 with a few changes. +// Especially the call to `PyType_Ready` is deferred until the needed +// post-actions are carried out in `SbkType_FromSpec_BMDWBD`. +// +// FIXME remove ASAP. +// Version is not clear, yet. Current version == 7.3.6 +// + +static const short slotoffsets[] = { + -1, /* invalid slot */ +/* Generated by typeslots.py */ +0, +0, +offsetof(PyHeapTypeObject, as_mapping.mp_ass_subscript), +offsetof(PyHeapTypeObject, as_mapping.mp_length), +offsetof(PyHeapTypeObject, as_mapping.mp_subscript), +offsetof(PyHeapTypeObject, as_number.nb_absolute), +offsetof(PyHeapTypeObject, as_number.nb_add), +offsetof(PyHeapTypeObject, as_number.nb_and), +offsetof(PyHeapTypeObject, as_number.nb_bool), +offsetof(PyHeapTypeObject, as_number.nb_divmod), +offsetof(PyHeapTypeObject, as_number.nb_float), +offsetof(PyHeapTypeObject, as_number.nb_floor_divide), +offsetof(PyHeapTypeObject, as_number.nb_index), +offsetof(PyHeapTypeObject, as_number.nb_inplace_add), +offsetof(PyHeapTypeObject, as_number.nb_inplace_and), +offsetof(PyHeapTypeObject, as_number.nb_inplace_floor_divide), +offsetof(PyHeapTypeObject, as_number.nb_inplace_lshift), +offsetof(PyHeapTypeObject, as_number.nb_inplace_multiply), +offsetof(PyHeapTypeObject, as_number.nb_inplace_or), +offsetof(PyHeapTypeObject, as_number.nb_inplace_power), +offsetof(PyHeapTypeObject, as_number.nb_inplace_remainder), +offsetof(PyHeapTypeObject, as_number.nb_inplace_rshift), +offsetof(PyHeapTypeObject, as_number.nb_inplace_subtract), +offsetof(PyHeapTypeObject, as_number.nb_inplace_true_divide), +offsetof(PyHeapTypeObject, as_number.nb_inplace_xor), +offsetof(PyHeapTypeObject, as_number.nb_int), +offsetof(PyHeapTypeObject, as_number.nb_invert), +offsetof(PyHeapTypeObject, as_number.nb_lshift), +offsetof(PyHeapTypeObject, as_number.nb_multiply), +offsetof(PyHeapTypeObject, as_number.nb_negative), +offsetof(PyHeapTypeObject, as_number.nb_or), +offsetof(PyHeapTypeObject, as_number.nb_positive), +offsetof(PyHeapTypeObject, as_number.nb_power), +offsetof(PyHeapTypeObject, as_number.nb_remainder), +offsetof(PyHeapTypeObject, as_number.nb_rshift), +offsetof(PyHeapTypeObject, as_number.nb_subtract), +offsetof(PyHeapTypeObject, as_number.nb_true_divide), +offsetof(PyHeapTypeObject, as_number.nb_xor), +offsetof(PyHeapTypeObject, as_sequence.sq_ass_item), +offsetof(PyHeapTypeObject, as_sequence.sq_concat), +offsetof(PyHeapTypeObject, as_sequence.sq_contains), +offsetof(PyHeapTypeObject, as_sequence.sq_inplace_concat), +offsetof(PyHeapTypeObject, as_sequence.sq_inplace_repeat), +offsetof(PyHeapTypeObject, as_sequence.sq_item), +offsetof(PyHeapTypeObject, as_sequence.sq_length), +offsetof(PyHeapTypeObject, as_sequence.sq_repeat), +offsetof(PyHeapTypeObject, ht_type.tp_alloc), +offsetof(PyHeapTypeObject, ht_type.tp_base), +offsetof(PyHeapTypeObject, ht_type.tp_bases), +offsetof(PyHeapTypeObject, ht_type.tp_call), +offsetof(PyHeapTypeObject, ht_type.tp_clear), +offsetof(PyHeapTypeObject, ht_type.tp_dealloc), +offsetof(PyHeapTypeObject, ht_type.tp_del), +offsetof(PyHeapTypeObject, ht_type.tp_descr_get), +offsetof(PyHeapTypeObject, ht_type.tp_descr_set), +offsetof(PyHeapTypeObject, ht_type.tp_doc), +offsetof(PyHeapTypeObject, ht_type.tp_getattr), +offsetof(PyHeapTypeObject, ht_type.tp_getattro), +offsetof(PyHeapTypeObject, ht_type.tp_hash), +offsetof(PyHeapTypeObject, ht_type.tp_init), +offsetof(PyHeapTypeObject, ht_type.tp_is_gc), +offsetof(PyHeapTypeObject, ht_type.tp_iter), +offsetof(PyHeapTypeObject, ht_type.tp_iternext), +offsetof(PyHeapTypeObject, ht_type.tp_methods), +offsetof(PyHeapTypeObject, ht_type.tp_new), +offsetof(PyHeapTypeObject, ht_type.tp_repr), +offsetof(PyHeapTypeObject, ht_type.tp_richcompare), +offsetof(PyHeapTypeObject, ht_type.tp_setattr), +offsetof(PyHeapTypeObject, ht_type.tp_setattro), +offsetof(PyHeapTypeObject, ht_type.tp_str), +offsetof(PyHeapTypeObject, ht_type.tp_traverse), +offsetof(PyHeapTypeObject, ht_type.tp_members), +offsetof(PyHeapTypeObject, ht_type.tp_getset), +offsetof(PyHeapTypeObject, ht_type.tp_free), +offsetof(PyHeapTypeObject, as_number.nb_matrix_multiply), +offsetof(PyHeapTypeObject, as_number.nb_inplace_matrix_multiply), +offsetof(PyHeapTypeObject, as_async.am_await), +offsetof(PyHeapTypeObject, as_async.am_aiter), +offsetof(PyHeapTypeObject, as_async.am_anext), +offsetof(PyHeapTypeObject, ht_type.tp_finalize), +}; + +static PyTypeObject * +best_base(PyObject *bases) +{ + // We always have only one base + return reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(bases, 0)); +} + +static PyObject * +_PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) +{ + PyHeapTypeObject *res = reinterpret_cast<PyHeapTypeObject *>( + PyType_GenericAlloc(&PyType_Type, 0)); + PyTypeObject *type, *base; + PyObject *modname; + char *s; + char *res_start = reinterpret_cast<char *>(res); + PyType_Slot *slot; + + if (res == nullptr) + return nullptr; + + if (spec->name == nullptr) { + PyErr_SetString(PyExc_SystemError, + "Type spec does not define the name field."); + goto fail; + } + + /* Set the type name and qualname */ + s = strrchr(const_cast<char *>(spec->name), '.'); + if (s == nullptr) + s = (char*)spec->name; + else + s++; + + type = &res->ht_type; + /* The flags must be initialized early, before the GC traverses us */ + type->tp_flags = spec->flags | Py_TPFLAGS_HEAPTYPE; + res->ht_name = PyUnicode_FromString(s); + if (!res->ht_name) + goto fail; + res->ht_qualname = res->ht_name; + Py_INCREF(res->ht_qualname); + type->tp_name = spec->name; + + /* Adjust for empty tuple bases */ + if (!bases) { + base = &PyBaseObject_Type; + /* See whether Py_tp_base(s) was specified */ + for (slot = spec->slots; slot->slot; slot++) { + if (slot->slot == Py_tp_base) + base = reinterpret_cast<PyTypeObject *>(slot->pfunc); + else if (slot->slot == Py_tp_bases) { + bases = reinterpret_cast<PyObject *>(slot->pfunc); + Py_INCREF(bases); + } + } + if (!bases) + bases = PyTuple_Pack(1, base); + if (!bases) + goto fail; + } + else + Py_INCREF(bases); + + /* Calculate best base, and check that all bases are type objects */ + base = best_base(bases); + if (base == nullptr) { + goto fail; + } + + /* Initialize essential fields */ + type->tp_as_async = &res->as_async; + type->tp_as_number = &res->as_number; + type->tp_as_sequence = &res->as_sequence; + type->tp_as_mapping = &res->as_mapping; + type->tp_as_buffer = &res->as_buffer; + /* Set tp_base and tp_bases */ + type->tp_bases = bases; + bases = nullptr; + Py_INCREF(base); + type->tp_base = base; + + type->tp_basicsize = spec->basicsize; + type->tp_itemsize = spec->itemsize; + + for (slot = spec->slots; slot->slot; slot++) { + if (slot->slot == Py_tp_base || slot->slot == Py_tp_bases) + /* Processed above */ + continue; + *reinterpret_cast<void **>(res_start + slotoffsets[slot->slot]) = slot->pfunc; + + /* need to make a copy of the docstring slot, which usually + points to a static string literal */ + if (slot->slot == Py_tp_doc) { + const char *old_doc = reinterpret_cast<char *>(slot->pfunc); + //_PyType_DocWithoutSignature(type->tp_name, slot->pfunc); + size_t len = strlen(old_doc)+1; + char *tp_doc = reinterpret_cast<char *>(PyObject_MALLOC(len)); + if (tp_doc == nullptr) { + type->tp_doc = nullptr; + PyErr_NoMemory(); + goto fail; + } + memcpy(tp_doc, old_doc, len); + type->tp_doc = tp_doc; + } + } + if (type->tp_dealloc == nullptr) { + /* It's a heap type, so needs the heap types' dealloc. + subtype_dealloc will call the base type's tp_dealloc, if + necessary. */ + type->tp_dealloc = _PyPy_subtype_dealloc; + } + + /// Here is the only change needed: Do not finalize type creation. + // if (PyType_Ready(type) < 0) + // goto fail; + PepType_SetDict(type, PyDict_New()); + /// This is not found in PyPy: + // if (type->tp_dictoffset) { + // res->ht_cached_keys = _PyDict_NewKeysForClass(); + // } + + /* Set type.__module__ */ + /// Removed __module__ handling, already implemented. + + return (PyObject*)res; + + fail: + Py_DECREF(res); + return nullptr; +} + +#endif // PYPY_VERSION + +} //extern "C" diff --git a/sources/shiboken6/libshiboken/sbktypefactory.h b/sources/shiboken6/libshiboken/sbktypefactory.h new file mode 100644 index 000000000..81cb32d41 --- /dev/null +++ b/sources/shiboken6/libshiboken/sbktypefactory.h @@ -0,0 +1,26 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef SBKTYPEFACTORY_H +#define SBKTYPEFACTORY_H + +#include "sbkpython.h" + +extern "C" +{ + +// PYSIDE-535: Encapsulation of PyType_FromSpec special-cased for PyPy +LIBSHIBOKEN_API PyTypeObject *SbkType_FromSpec(PyType_Spec *); +LIBSHIBOKEN_API PyTypeObject *SbkType_FromSpecWithMeta(PyType_Spec *, PyTypeObject *); +LIBSHIBOKEN_API PyTypeObject *SbkType_FromSpecWithBases(PyType_Spec *, PyObject *); +LIBSHIBOKEN_API PyTypeObject *SbkType_FromSpecBasesMeta(PyType_Spec *, PyObject *, PyTypeObject *); +LIBSHIBOKEN_API PyTypeObject *SbkType_FromSpec_BMDWB(PyType_Spec *spec, + PyObject *bases, + PyTypeObject *meta, + int dictoffset, + int weaklistoffset, + PyBufferProcs *bufferprocs); + +} //extern "C" + +#endif // SBKTYPEFACTORY_H diff --git a/sources/shiboken6/libshiboken/sbkversion.h.in b/sources/shiboken6/libshiboken/sbkversion.h.in index 99ee7f93e..5c0b38fdb 100644 --- a/sources/shiboken6/libshiboken/sbkversion.h.in +++ b/sources/shiboken6/libshiboken/sbkversion.h.in @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef SBKVERSION_H #define SBKVERSION_H @@ -46,8 +10,8 @@ #define SHIBOKEN_MICRO_VERSION @shiboken_MICRO_VERSION@ #define SHIBOKEN_RELEASE_LEVEL "final" #define SHIBOKEN_SERIAL 0 -#define PYTHON_VERSION_MAJOR @PYTHON_VERSION_MAJOR@ -#define PYTHON_VERSION_MINOR @PYTHON_VERSION_MINOR@ -#define PYTHON_VERSION_PATCH @PYTHON_VERSION_PATCH@ +#define PYTHON_VERSION_MAJOR @Python_VERSION_MAJOR@ +#define PYTHON_VERSION_MINOR @Python_VERSION_MINOR@ +#define PYTHON_VERSION_PATCH @Python_VERSION_PATCH@ #endif diff --git a/sources/shiboken6/libshiboken/sbkwindows.h b/sources/shiboken6/libshiboken/sbkwindows.h new file mode 100644 index 000000000..9e753fa5e --- /dev/null +++ b/sources/shiboken6/libshiboken/sbkwindows.h @@ -0,0 +1,17 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef SBKWINDOWS_H +#define SBKWINDOWS_H + +#ifdef _WIN32 +# ifndef NOMINMAX +# define NOMINMAX +# endif +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include <windows.h> +#endif + +#endif // SBKWINDOWS_H diff --git a/sources/shiboken6/libshiboken/shiboken.h b/sources/shiboken6/libshiboken/shiboken.h index 3e1df5235..fcf777ae0 100644 --- a/sources/shiboken6/libshiboken/shiboken.h +++ b/sources/shiboken6/libshiboken/shiboken.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef SHIBOKEN_H #define SHIBOKEN_H @@ -47,9 +11,11 @@ #include "gilstate.h" #include "threadstatesaver.h" #include "helper.h" +#include "pyobjectholder.h" #include "sbkarrayconverter.h" #include "sbkconverter.h" #include "sbkenum.h" +#include "sbkerrors.h" #include "sbkmodule.h" #include "sbkstring.h" #include "sbkstaticstrings.h" diff --git a/sources/shiboken6/libshiboken/shibokenbuffer.cpp b/sources/shiboken6/libshiboken/shibokenbuffer.cpp index 45c727717..d04613895 100644 --- a/sources/shiboken6/libshiboken/shibokenbuffer.cpp +++ b/sources/shiboken6/libshiboken/shibokenbuffer.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "shibokenbuffer.h" #include <cstdlib> diff --git a/sources/shiboken6/libshiboken/shibokenbuffer.h b/sources/shiboken6/libshiboken/shibokenbuffer.h index 512d9db4d..6b17eb6eb 100644 --- a/sources/shiboken6/libshiboken/shibokenbuffer.h +++ b/sources/shiboken6/libshiboken/shibokenbuffer.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef SHIBOKEN_BUFFER_H #define SHIBOKEN_BUFFER_H diff --git a/sources/shiboken6/libshiboken/shibokenmacros.h b/sources/shiboken6/libshiboken/shibokenmacros.h index d8f45868a..3c083c5bb 100644 --- a/sources/shiboken6/libshiboken/shibokenmacros.h +++ b/sources/shiboken6/libshiboken/shibokenmacros.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef SHIBOKENMACROS_H #define SHIBOKENMACROS_H @@ -48,11 +12,9 @@ # else # define LIBSHIBOKEN_IMPORT # endif -# define SBK_DEPRECATED(func) __declspec(deprecated) func #else # define LIBSHIBOKEN_EXPORT __attribute__ ((visibility("default"))) # define LIBSHIBOKEN_IMPORT -# define SBK_DEPRECATED(func) func __attribute__ ((deprecated)) #endif #ifdef BUILD_LIBSHIBOKEN diff --git a/sources/shiboken6/libshiboken/signature.h b/sources/shiboken6/libshiboken/signature.h index 0459d8661..e0130b5a6 100644 --- a/sources/shiboken6/libshiboken/signature.h +++ b/sources/shiboken6/libshiboken/signature.h @@ -1,52 +1,18 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef SIGNATURE_H #define SIGNATURE_H +#include "shibokenmacros.h" +#include "sbkpython.h" + extern "C" { LIBSHIBOKEN_API int InitSignatureStrings(PyTypeObject *, const char *[]); LIBSHIBOKEN_API void FinishSignatureInitialization(PyObject *, const char *[]); LIBSHIBOKEN_API void SetError_Argument(PyObject *, const char *, PyObject *); -LIBSHIBOKEN_API PyObject *Sbk_TypeGet___signature__(PyObject *, PyObject *); LIBSHIBOKEN_API PyObject *Sbk_TypeGet___doc__(PyObject *); LIBSHIBOKEN_API PyObject *GetFeatureDict(); diff --git a/sources/shiboken6/libshiboken/signature/signature.cpp b/sources/shiboken6/libshiboken/signature/signature.cpp index 624d55a3e..3255cb56d 100644 --- a/sources/shiboken6/libshiboken/signature/signature.cpp +++ b/sources/shiboken6/libshiboken/signature/signature.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only //////////////////////////////////////////////////////////////////////////// // @@ -49,13 +13,16 @@ // General documentation can be found in `signature_doc.rst`. // +#include "signature.h" +#include "signature_p.h" + #include "basewrapper.h" #include "autodecref.h" #include "sbkstring.h" #include "sbkstaticstrings.h" #include "sbkstaticstrings_p.h" +#include "sbkfeature_base.h" -#include "signature_p.h" #include <structmember.h> using namespace Shiboken; @@ -86,6 +53,11 @@ PyObject *GetClassOrModOf(PyObject *ob) Py_INCREF(ob); return ob; } +#ifdef PYPY_VERSION + // PYSIDE-535: PyPy has a special builtin method that acts almost like PyCFunction. + if (Py_TYPE(ob) == PepBuiltinMethod_TypePtr) + return _get_class_of_bm(ob); +#endif if (PyType_IsSubtype(Py_TYPE(ob), &PyCFunction_Type)) return _get_class_of_cf(ob); if (Py_TYPE(ob) == PepStaticMethod_TypePtr) @@ -113,7 +85,7 @@ PyObject *GetTypeKey(PyObject *ob) module_name.reset(PyObject_GetAttr(ob, PyMagicName::name())); return Py_BuildValue("O", module_name.object()); } - AutoDecRef class_name(_get_qualname(ob)); + AutoDecRef class_name(PyObject_GetAttr(ob, PyMagicName::qualname())); if (class_name.isNull()) { Py_FatalError("Signature: missing class name in GetTypeKey"); return nullptr; @@ -123,7 +95,7 @@ PyObject *GetTypeKey(PyObject *ob) static PyObject *empty_dict = nullptr; -PyObject *TypeKey_to_PropsDict(PyObject *type_key, PyObject *obtype) +PyObject *TypeKey_to_PropsDict(PyObject *type_key) { PyObject *dict = PyDict_GetItem(pyside_globals->arg_dict, type_key); if (dict == nullptr) { @@ -164,6 +136,24 @@ static PyObject *_GetSignature_Cached(PyObject *props, PyObject *func_kind, PyOb return Py_INCREF(value), value; } +#ifdef PYPY_VERSION +PyObject *GetSignature_Method(PyObject *obfunc, PyObject *modifier) +{ + AutoDecRef obtype_mod(GetClassOrModOf(obfunc)); + AutoDecRef type_key(GetTypeKey(obtype_mod)); + if (type_key.isNull()) + Py_RETURN_NONE; + PyObject *dict = TypeKey_to_PropsDict(type_key); + if (dict == nullptr) + return nullptr; + AutoDecRef func_name(PyObject_GetAttr(obfunc, PyMagicName::name())); + PyObject *props = !func_name.isNull() ? PyDict_GetItem(dict, func_name) : nullptr; + if (props == nullptr) + Py_RETURN_NONE; + return _GetSignature_Cached(props, PyName::method(), modifier); +} +#endif + PyObject *GetSignature_Function(PyObject *obfunc, PyObject *modifier) { // make sure that we look into PyCFunction, only... @@ -173,7 +163,7 @@ PyObject *GetSignature_Function(PyObject *obfunc, PyObject *modifier) AutoDecRef type_key(GetTypeKey(obtype_mod)); if (type_key.isNull()) Py_RETURN_NONE; - PyObject *dict = TypeKey_to_PropsDict(type_key, obtype_mod); + PyObject *dict = TypeKey_to_PropsDict(type_key); if (dict == nullptr) return nullptr; AutoDecRef func_name(PyObject_GetAttr(obfunc, PyMagicName::name())); @@ -183,7 +173,7 @@ PyObject *GetSignature_Function(PyObject *obfunc, PyObject *modifier) int flags = PyCFunction_GET_FLAGS(obfunc); PyObject *func_kind; - if (PyModule_Check(obtype_mod)) + if (PyModule_Check(obtype_mod.object())) func_kind = PyName::function(); else if (flags & METH_CLASS) func_kind = PyName::classmethod(); @@ -201,13 +191,13 @@ PyObject *GetSignature_Wrapper(PyObject *ob, PyObject *modifier) AutoDecRef class_key(GetTypeKey(objclass)); if (func_name.isNull() || objclass.isNull() || class_key.isNull()) return nullptr; - PyObject *dict = TypeKey_to_PropsDict(class_key, objclass); + PyObject *dict = TypeKey_to_PropsDict(class_key); if (dict == nullptr) return nullptr; PyObject *props = PyDict_GetItem(dict, func_name); if (props == nullptr) { // handle `__init__` like the class itself - if (strcmp(String::toCString(func_name), "__init__") == 0) + if (PyUnicode_CompareWithASCIIString(func_name, "__init__") == 0) return GetSignature_TypeMod(objclass, modifier); Py_RETURN_NONE; } @@ -219,7 +209,7 @@ PyObject *GetSignature_TypeMod(PyObject *ob, PyObject *modifier) AutoDecRef ob_name(PyObject_GetAttr(ob, PyMagicName::name())); AutoDecRef ob_key(GetTypeKey(ob)); - PyObject *dict = TypeKey_to_PropsDict(ob_key, ob); + PyObject *dict = TypeKey_to_PropsDict(ob_key); if (dict == nullptr) return nullptr; PyObject *props = PyDict_GetItem(dict, ob_name); @@ -237,9 +227,17 @@ PyObject *GetSignature_TypeMod(PyObject *ob, PyObject *modifier) // The `modifier` argument is a string that is passed in from `loader.py`. // Configuration what the modifiers mean is completely in Python. // +// PYSIDE-2101: The __signature__ attribute is gone due to rlcompleter. +// PyObject *get_signature_intern(PyObject *ob, PyObject *modifier) { +#ifdef PYPY_VERSION + // PYSIDE-535: PyPy has a special builtin method that acts almost like PyCFunction. + if (Py_TYPE(ob) == PepBuiltinMethod_TypePtr) { + return pyside_bm_get___signature__(ob, modifier); + } +#endif if (PyType_IsSubtype(Py_TYPE(ob), &PyCFunction_Type)) return pyside_cf_get___signature__(ob, modifier); if (Py_TYPE(ob) == PepStaticMethod_TypePtr) @@ -250,6 +248,9 @@ PyObject *get_signature_intern(PyObject *ob, PyObject *modifier) return pyside_tp_get___signature__(ob, modifier); if (Py_TYPE(ob) == &PyWrapperDescr_Type) return pyside_wd_get___signature__(ob, modifier); + // For classmethods we use the simple wrapper description implementation. + if (Py_TYPE(ob) == &PyClassMethodDescr_Type) + return pyside_wd_get___signature__(ob, modifier); return nullptr; } @@ -258,8 +259,6 @@ static PyObject *get_signature(PyObject * /* self */, PyObject *args) PyObject *ob; PyObject *modifier = nullptr; - init_module_1(); - if (!PyArg_ParseTuple(args, "O|O", &ob, &modifier)) return nullptr; if (Py_TYPE(ob) == PepFunction_TypePtr) @@ -291,14 +290,25 @@ static PyObject *feature_import(PyObject * /* self */, PyObject *args, PyObject if (import_func == nullptr) { Py_FatalError("builtins has no \"__orig_import__\" function"); } - return PyObject_Call(import_func, args, kwds); + ret = PyObject_Call(import_func, args, kwds); + if (ret) { + // PYSIDE-2029: Intercept after the import to search for PySide usage. + PyObject *post = PyObject_CallFunctionObjArgs(pyside_globals->feature_imported_func, + ret, nullptr); + Py_XDECREF(post); + if (post == nullptr) { + Py_DECREF(ret); + return nullptr; + } + } + return ret; } PyMethodDef signature_methods[] = { - {"__feature_import__", (PyCFunction)feature_import, METH_VARARGS | METH_KEYWORDS}, + {"__feature_import__", (PyCFunction)feature_import, METH_VARARGS | METH_KEYWORDS, nullptr}, {"get_signature", (PyCFunction)get_signature, METH_VARARGS, - "get the __signature__, but pass an optional string parameter"}, - {nullptr, nullptr} + "get the signature, passing an optional string parameter"}, + {nullptr, nullptr, 0, nullptr} }; //////////////////////////////////////////////////////////////////////////// @@ -321,7 +331,6 @@ PyMethodDef signature_methods[] = { static int PySide_BuildSignatureArgs(PyObject *obtype_mod, const char *signatures[]) { - init_module_1(); AutoDecRef type_key(GetTypeKey(obtype_mod)); /* * PYSIDE-996: Avoid string overflow in MSVC, which has a limit of @@ -349,7 +358,6 @@ PyObject *PySide_BuildSignatureProps(PyObject *type_key) * We simply pick up the arguments that we stored here and replace * them by the function result. */ - init_module_2(); if (type_key == nullptr) return nullptr; PyObject *numkey = PyDict_GetItem(pyside_globals->arg_dict, type_key); @@ -379,8 +387,24 @@ PyObject *PySide_BuildSignatureProps(PyObject *type_key) // //////////////////////////////////////////////////////////////////////////// +#ifdef PYPY_VERSION +static bool get_lldebug_flag() +{ + auto *dic = PySys_GetObject("pypy_translation_info"); + int lldebug = PyObject_IsTrue(PyDict_GetItemString(dic, "translation.lldebug")); + int lldebug0 = PyObject_IsTrue(PyDict_GetItemString(dic, "translation.lldebug0")); + return lldebug || lldebug0; +} + +#endif + static int PySide_FinishSignatures(PyObject *module, const char *signatures[]) { +#ifdef PYPY_VERSION + static const bool have_problem = get_lldebug_flag(); + if (have_problem) + return 0; // crash with lldebug at `PyDict_Next` +#endif /* * Initialization of module functions and resolving of static methods. */ @@ -407,8 +431,6 @@ static int PySide_FinishSignatures(PyObject *module, const char *signatures[]) if (PyCFunction_Check(func)) if (PyDict_SetItem(pyside_globals->map_dict, func, module) < 0) return -1; - if (_finish_nested_classes(obdict) < 0) - return -1; // The finish_import function will not work the first time since phase 2 // was not yet run. But that is ok, because the first import is always for // the shiboken module (or a test module). @@ -430,9 +452,12 @@ static int PySide_FinishSignatures(PyObject *module, const char *signatures[]) int InitSignatureStrings(PyTypeObject *type, const char *signatures[]) { + // PYSIDE-2404: This function now also builds the mapping for static methods. + // It was one missing spot to let Lazy import work. + init_shibokensupport_module(); auto *ob_type = reinterpret_cast<PyObject *>(type); int ret = PySide_BuildSignatureArgs(ob_type, signatures); - if (ret < 0) { + if (ret < 0 || _build_func_to_type(ob_type) < 0) { PyErr_Print(); PyErr_SetNone(PyExc_ImportError); } @@ -449,7 +474,17 @@ void FinishSignatureInitialization(PyObject *module, const char *signatures[]) * Still, it is not possible to call init phase 2 from here, * because the import is still running. Do it from Python! */ - if ( PySide_PatchTypes() < 0 + init_shibokensupport_module(); + +#ifndef PYPY_VERSION + static const bool patch_types = true; +#else + // PYSIDE-535: On PyPy we cannot patch builtin types. This can be + // re-implemented later. For now, we use `get_signature`, instead. + static const bool patch_types = false; +#endif + + if ((patch_types && PySide_PatchTypes() < 0) || PySide_FinishSignatures(module, signatures) < 0) { PyErr_Print(); PyErr_SetNone(PyExc_ImportError); @@ -491,7 +526,11 @@ static PyObject *adjustFuncName(const char *func_name) return nullptr; // Run `eval` on the type string to get the object. + // PYSIDE-1710: If the eval does not work, return the given string. AutoDecRef obtype(PyRun_String(_path, Py_eval_input, ns, ns)); + if (obtype.isNull()) + return String::fromCString(func_name); + if (PyModule_Check(obtype.object())) { // This is a plain function. Return the unmangled name. return String::fromCString(func_name); @@ -500,8 +539,8 @@ static PyObject *adjustFuncName(const char *func_name) // Find the feature flags auto type = reinterpret_cast<PyTypeObject *>(obtype.object()); - auto dict = type->tp_dict; - int id = SbkObjectType_GetReserved(type); + AutoDecRef dict(PepType_GetDict(type)); + int id = currentSelectId(type); id = id < 0 ? 0 : id; // if undefined, set to zero auto lower = id & 0x01; auto is_prop = id & 0x02; @@ -509,7 +548,7 @@ static PyObject *adjustFuncName(const char *func_name) // Compute all needed info. PyObject *name = String::getSnakeCaseName(_name, lower); - PyObject *prop_name; + PyObject *prop_name{}; if (is_prop) { PyObject *prop_methods = PyDict_GetItem(dict, PyMagicName::property_methods()); prop_name = PyDict_GetItem(prop_methods, name); @@ -521,36 +560,37 @@ static PyObject *adjustFuncName(const char *func_name) // Finally, generate the correct path expression. char _buf[250 + 1] = {}; - if (is_prop) { + if (prop_name) { auto _prop_name = String::toCString(prop_name); if (is_class_prop) - sprintf(_buf, "%s.__dict__['%s'].fset", _path, _prop_name); + snprintf(_buf, sizeof(_buf), "%s.__dict__['%s'].fset", _path, _prop_name); else - sprintf(_buf, "%s.%s.fset", _path, _prop_name); + snprintf(_buf, sizeof(_buf), "%s.%s.fset", _path, _prop_name); } else { auto _name = String::toCString(name); - sprintf(_buf, "%s.%s", _path, _name); + snprintf(_buf, sizeof(_buf), "%s.%s", _path, _name); } return String::fromCString(_buf); } void SetError_Argument(PyObject *args, const char *func_name, PyObject *info) { + init_shibokensupport_module(); /* * This function replaces the type error construction with extra * overloads parameter in favor of using the signature module. * Error messages are rare, so we do it completely in Python. */ - init_module_1(); - init_module_2(); // PYSIDE-1305: Handle errors set by fillQtProperties. if (PyErr_Occurred()) { PyObject *e, *v, *t; // Note: These references are all borrowed. PyErr_Fetch(&e, &v, &t); + Py_DECREF(e); info = v; + Py_XDECREF(t); } // PYSIDE-1019: Modify the function name expression according to feature. AutoDecRef new_func_name(adjustFuncName(func_name)); @@ -581,21 +621,19 @@ void SetError_Argument(PyObject *args, const char *func_name, PyObject *info) * But the __doc__ attribute existed already by inheritance, and calling * PyType_Modified() is not supported. So we added the getsets explicitly * to the metatype. + * + * PYSIDE-2101: The __signature__ attribute is gone due to rlcompleter. */ -PyObject *Sbk_TypeGet___signature__(PyObject *ob, PyObject *modifier) -{ - return pyside_tp_get___signature__(ob, modifier); -} - PyObject *Sbk_TypeGet___doc__(PyObject *ob) { + init_shibokensupport_module(); return pyside_tp_get___doc__(ob); } PyObject *GetFeatureDict() { - init_module_1(); + init_shibokensupport_module(); return pyside_globals->feature_dict; } diff --git a/sources/shiboken6/libshiboken/signature/signature_doc.rst b/sources/shiboken6/libshiboken/signature/signature_doc.rst deleted file mode 100644 index 0fb26ae52..000000000 --- a/sources/shiboken6/libshiboken/signature/signature_doc.rst +++ /dev/null @@ -1,376 +0,0 @@ -************************* -The signature C extension -************************* - -This module is a C extension for CPython 3.5 and up, and CPython 2.7. -Its purpose is to provide support for the ``__signature__`` attribute -of builtin PyCFunction objects. - - -Short Introduction to the Topic -=============================== - -Beginning with CPython 3.5, Python functions began to grow a ``__signature__`` -attribute for normal Python functions. This is totally optional and just -a nice-to-have feature in Python. - -PySide, on the other hand, could use ``__signature__`` very much, because the -typing info for the 15000+ PySide functions is really missing, and it -would be nice to have this info directly available. - - -The Idea to Support Signatures -============================== - -We want to have an additional ``__signature__`` attribute in all PySide -methods, without changing lots of generated code. -Therefore, we did not change any of the existing data structures, -but supported the new attribute by a global dictionary. - -When the ``__signature__`` property is requested, a method is called that -does a lookup in the global dict. This is a flexible approach with little impact -to the rest of the project. It has very limited overhead compared to direct -attribute access, but for the need of a signature access from time to time, -this is an adequate compromise. - - -How this Code Works -------------------- - -Signatures are supported for regular Python functions, only. Creating signatures -for ``PyCFunction`` objects would require quite some extra effort in Python. - -Fortunately, we found this special *stealth* technique, that saves us most of the -needed effort: - -The basic idea is to create a dummy Python function with **varnames**, **defaults** -and **annotations** properties, and then to use the inspect -module to create a signature object. This object is returned as the computed -result of the ``__signature__`` attribute of the real ``PyCFunction`` object. - -There is one thing that really changes Python a bit: - -* We added the ``__signature__`` attribute to every function. - -That is a little change to Python that does not harm, but it saves us -tons of code, that was needed in the early versions of the module. - -The internal work is done in two steps: - -* All functions of a class get the *signature text* when the module is imported. - This is only a very small overhead added to the startup time. It is a single - string for each whole class. -* The actual signature object is created later, when the attribute is really - requested. Signatures are cached and only created on first access. - -Example: - -The ``PyCFunction`` ``QtWidgets.QApplication.palette`` is interrogated for its -signature. That means ``pyside_sm_get___signature__()`` is called. -It calls ``GetSignature_Function`` which returns the signature if it is found. - - -Why this Code is Fast ---------------------- - -It costs a little time (maybe 6 seconds) to run through every single signature -object, since these are more than 25000 Python objects. But all the signature -objects will be rarely accessed but in special applications. -The normal case are only a few accesses, and these are working pretty fast. - -The key to make this signature module fast is to avoid computation as much as -possible. When no signature objects are used, then almost no time is lost in -initialization. Only the above mentioned strings and some support modules are -additionally loaded on ``import PySide6``. -When it comes to signature usage, then late initialization is used and cached. -This technique is also known as *full laziness* in haskell. - -There are actually two locations where late initialization occurs: - -* ``dict`` can be no dict but a tuple. That is the initial argument tuple that - was saved by ``PySide_BuildSignatureArgs`` at module load time. - If so, then ``pyside_type_init`` in parser.py will be called, - which parses the string and creates the dict. -* ``props`` can be empty. Then ``create_signature`` in loader.py - is called, which uses a dummy function to produce a signature instance - with the inspect module. - -The initialization that is always done is just two dictionary writes -per class, and we have about 1000 classes. -To measure the additional overhead, we have simulated what happens -when ``from PySide6 import *`` is performed. -It turned out that the overhead is below 0.5 ms. - - -The Signature Package Structure -------------------------------- - -The C++ code involved with the signature module is completely in the file -shiboken6/libshiboken/signature.cpp . All other functionality is implemented in -the ``signature`` Python package. It has the following structure:: - - shiboken6/files.dir/shibokensupport/ - backport_inspect.py - - signature/ - loader.py - parser.py - mapping.py - errorhandler.py - layout.py - - lib/ - enum_sig.py - tool.py - - - -Really important are the **parser**, **mapping**, **errorhandler**, **enum_sig**, -**layout** and **loader** modules. The rest is needed to create Python 2 compatibility -or be compatible with embedding and installers. - - -loader.py -~~~~~~~~~ - -This module assembles and imports the ``inspect`` module, and then exports the -``create_signature`` function. This function takes a fake function and some -attributes and builds a ``__signature__`` object with the inspect module. - - -parser.py -~~~~~~~~~ - -This module takes a class signatures string from C++ and parses it into the -needed properties for the ``create_signature`` function. Its entry point is the -``pyside_type_init`` function, which is called from the C module via ``loader.py``. - - -mapping.py -~~~~~~~~~~ - -The purpose of the mapping module is maintaining a list of replacement strings -that map from the *signature text* in C to the property strings that Python -needs. A lot of mappings are resolved by rather complex expressions in ``parser.py``, -but a few hundred cases are better to spell explicitly, here. - - -errorhandler.py -~~~~~~~~~~~~~~~ - -Since ``Qt For Python 5.12``, we no longer use the builtin type error messages from C++. -Instead, we get much better results with the signature module. At the same time, -this enforced supporting shiboken as well, and the signature module was no longer -optional. - - -enum_sig.py -~~~~~~~~~~~ - -The diverse applications of the signature module all needed to iterate over modules, -classes and functions. In order to centralize this enumeration, the process has -been factored out as a context manager. The user has only to supply functions -that do the actual formatting. - -See for example the .pyi generator ``pyside6/PySide6/support/generate_pyi.py``. - - -layout.py -~~~~~~~~~ - -As more applications used the signature module, different formatting of signatures -was needed. To support that, we created the function ``create_signature``, which -has a parameter to choose from some predefined layouts. - - -*typing27.py* -~~~~~~~~~~~~~ - -Python 2 has no typing module at all. This is a backport of the minimum that is needed. - - -*backport_inspect.py* -~~~~~~~~~~~~~~~~~~~~~ - -Python 2 has an inspect module, but lacks the signature functions, completely. -This module adds the missing functionality, which is merged at runtime into -the inspect module. - - -Multiple Arities ----------------- - -One aspect that was ignored so far was *multiple arities*: How to handle it when -a function has more than one signature? - -I did not find any note on how multiple signatures should be treated in Python, -but this simple rules seem to work well: - -* If there is a list, then it is a multi-signature. -* Otherwise, it is a simple signature. - - -Impacts of The Signature Module -=============================== - -The signature module has a number of impacts to other PySide modules, which were -created as a consequence of its existence, and there will be a few more in the -future: - - -existence_test.py ------------------ - -The file ``pyside6/tests/registry/existence_test.py`` was written using the -signatures from the signatures module. The idea is that there are some 15000 -functions with a certain signature. - -These functions should not get lost by some bad check-in. Therefore, a list -of all existing signatures is kept as a module that assembles a -dictionary. The function existence is checked, and also the exact arity. - -This module exists for every PySide release and every platform. The initial -module is generated once and saved as ``exists_{plat}_{version}.py``. - -An error is normally only reported as a warning, but: - - -Interaction With The Coin Module -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When this test program is run in COIN, then the warnings are turned into -errors. The reason is that only in COIN, we have a stable configuration -of PySide modules that can reliably be compared. - -These modules have the name ``exists_{platf}_{version}_ci.py``, and as a big -exception for generated code, these files are *intentionally* checked in. - - -What Happens When a List is Missing? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When a new version of PySide gets created, then the existence test files -initially do not exist. - -When a COIN test is run, then it will complain about the error and create -the missing module on standard output. -But since COIN tests are run multiple times, the output that was generated -by the first test will still exist at the subsequent runs. -(If COIN was properly implemented, we could not take that advantage and -would need to implement that as an extra exception.) - -As a result, a missing module will be reported as a test which partially -succeeded (called "FLAKY"). To avoid further flaky tests and to activate as a real test, -we can now capture the error output of COIN and check the generated module -in. - - -Explicitly Enforcing Recreation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The former way to regenerate the registry files was to remove the files -and check that in. This has the desired effect, but creates huge deltas. -As a more efficient way, we have prepared a comment in the first line -that contains the word "recreate". -By uncommenting this line, a NameError is triggered, which has the same -effect. - - -init_platform.py -~~~~~~~~~~~~~~~~ - -For generating the ``exists_{platf}_{version}`` modules, the module -``pyside6/tests/registry/init_platform.py`` was written. It can be used -standalone from the commandline, to check the compatibility of some -changes, directly. - - -scrape_testresults.py ---------------------- - -To simplify and automate the process of extracting the ``exists_{platf}_{version}_ci.py`` -files, the script ``pyside6/tests/registry/scrape_testresults.py`` has been written. - -This script scans the whole testresults website for PySide, that is:: - - https://testresults.qt.io/coin/api/results/pyside/pyside-setup/ - -On the first scan, the script runs less than 30 minutes. After that, a cache -is generated and the scan works *much* faster. The test results are placed -into the folder ``pyside6/tests/registry/testresults/embedded/`` with a -unique name that allows for easy sorting. Example:: - - testresults/embedded/2018_09_10_10_40_34-test_1536891759-exists_linux_5_11_2_ci.py - -These files are created only once. If they already exist, they are not touched, again. -The file `pyside6/tests/registry/known_urls.json`` holds all scanned URLs after -a successful scan. The ``testresults/embedded`` folder can be kept for reference -or can be removed. Important is only the json file. - -The result of a scan is then directly placed into the ``pyside6/tests/registry/`` -folder. It should be reviewed and then eventually checked in. - - -generate_pyi.py ---------------- - -``pyside6/PySide6/support/generate_pyi.py`` is still under development. -This module generates so-called hinting stubs for integration of PySide -with diverse *Python IDEs*. - -Although this module creates the stubs as an add-on, the -impact on the quality of the signature module is considerable: - -The module must create syntactically correct ``.pyi`` files which contain -not only signatures but also constants and enums of all PySide modules. -This serves as an extra challenge that has a very positive effect on -the completeness and correctness of signatures. - -The module has a ``--feature`` option to generate modified .pyi files. -A shortcut for this command is ``pyside6-genpyi``. - -A useful command to change all .pyi files to use all features is - -.. code-block:: python - - pyside6-genpyi all --feature snake_case true_property - - -pyi_generator.py ----------------- - -``shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/pyi_generator.py`` -has been extracted from ``generate_pyi.py``. It allows the generation of ``.pyi`` -files from arbitrary extension modules created with shiboken. - -A shortcut for this command is ``shiboken6-genpyi``. - - -Current Extensions ------------------- - -Before the signature module was written, there already existed the concept of -signatures, but in a more C++ - centric way. From that time, there existed -the error messages, which are created when a function gets wrong argument types. - -These error messages were replaced by text generated on demand by -the signature module, in order to be more consistent and correct. -This was implemented in ``Qt For Python 5.12.0``. - -Additionally, the ``__doc__`` attribute of PySide methods was not set. -It was easy to get a nice ``help()`` feature by creating signatures -as default content for docstrings. -This was implemented in ``Qt For Python 5.12.1``. - - -Literature -========== - - `PEP 362 – Function Signature Object <https://www.python.org/dev/peps/pep-0362/>`__ - - `PEP 484 – Type Hints <https://www.python.org/dev/peps/pep-0484/>`__ - - `PEP 3107 – Function Annotations <https://www.python.org/dev/peps/pep-3107/>`__ - - -*Personal Remark: This module is dedicated to our lovebird "Püppi", who died on 2017-09-15.* diff --git a/sources/shiboken6/libshiboken/signature/signature_extend.cpp b/sources/shiboken6/libshiboken/signature/signature_extend.cpp index 43a78ed21..7292f8216 100644 --- a/sources/shiboken6/libshiboken/signature/signature_extend.cpp +++ b/sources/shiboken6/libshiboken/signature/signature_extend.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only //////////////////////////////////////////////////////////////////////////// // @@ -48,12 +12,14 @@ // PyMethodDescr_Type // PyCFunction_Type // PyStaticMethod_Type -// PyType_Type +// (*) PyType_Type // PyWrapperDescr_Type // -// Their `tp_getset` fields are modified so support the `__signature__` +// Their `tp_getset` fields are modified to support the `__signature__` // attribute and additions to the `__doc__` attribute. // +// PYSIDE-535: PyType_Type patching is removed, +// Shiboken.ObjectType and Shiboken.EnumMeta have new getsets, instead. #include "autodecref.h" #include "sbkstring.h" @@ -89,27 +55,29 @@ static PyObject *_get_written_signature(signaturefunc sf, PyObject *ob, PyObject return ret; } +#ifdef PYPY_VERSION +PyObject *pyside_bm_get___signature__(PyObject *func, PyObject *modifier) +{ + return _get_written_signature(GetSignature_Method, func, modifier); +} +#endif + PyObject *pyside_cf_get___signature__(PyObject *func, PyObject *modifier) { - init_module_2(); return _get_written_signature(GetSignature_Function, func, modifier); } PyObject *pyside_sm_get___signature__(PyObject *sm, PyObject *modifier) { - init_module_2(); AutoDecRef func(PyObject_GetAttr(sm, PyMagicName::func())); - if (Py_TYPE(func) == PepFunction_TypePtr) - return PyObject_GetAttr(func, PyMagicName::signature()); return _get_written_signature(GetSignature_Function, func, modifier); } PyObject *pyside_md_get___signature__(PyObject *ob_md, PyObject *modifier) { - init_module_2(); AutoDecRef func(name_key_to_func(ob_md)); if (func.object() == Py_None) - return Py_None; + Py_RETURN_NONE; if (func.isNull()) Py_FatalError("missing mapping in MethodDescriptor"); return pyside_cf_get___signature__(func, modifier); @@ -117,13 +85,11 @@ PyObject *pyside_md_get___signature__(PyObject *ob_md, PyObject *modifier) PyObject *pyside_wd_get___signature__(PyObject *ob, PyObject *modifier) { - init_module_2(); return _get_written_signature(GetSignature_Wrapper, ob, modifier); } PyObject *pyside_tp_get___signature__(PyObject *obtype_mod, PyObject *modifier) { - init_module_2(); return _get_written_signature(GetSignature_TypeMod, obtype_mod, modifier); } @@ -153,29 +119,29 @@ static int handle_doc_in_progress = 0; static PyObject *handle_doc(PyObject *ob, PyObject *old_descr) { - init_module_1(); - init_module_2(); AutoDecRef ob_type_mod(GetClassOrModOf(ob)); const char *name; - if (PyModule_Check(ob_type_mod)) - name = PyModule_GetName(ob_type_mod); + bool isModule = PyModule_Check(ob_type_mod.object()); + if (isModule) + name = PyModule_GetName(ob_type_mod.object()); else name = reinterpret_cast<PyTypeObject *>(ob_type_mod.object())->tp_name; + PyObject *res{}; + if (handle_doc_in_progress || name == nullptr - || strncmp(name, "PySide6.", 8) != 0) - return PyObject_CallMethodObjArgs(old_descr, - PyMagicName::get(), - ob, nullptr); - handle_doc_in_progress++; - PyObject *res = PyObject_CallFunction( - pyside_globals->make_helptext_func, - "(O)", ob); - handle_doc_in_progress--; - if (res == nullptr) { - PyErr_Print(); - Py_FatalError("handle_doc did not receive a result"); + || (isModule && strncmp(name, "PySide6.", 8) != 0)) { + res = PyObject_CallMethodObjArgs(old_descr, PyMagicName::get(), ob, nullptr); + } else { + handle_doc_in_progress++; + res = PyObject_CallFunction(pyside_globals->make_helptext_func, "(O)", ob); + handle_doc_in_progress--; } - return res; + + if (res) + return res; + + PyErr_Clear(); + Py_RETURN_NONE; } static PyObject *pyside_cf_get___doc__(PyObject *cf) @@ -203,66 +169,29 @@ static PyObject *pyside_wd_get___doc__(PyObject *wd) return handle_doc(wd, old_wd_doc_descr); } -// the default setter for all objects -static int pyside_set___signature__(PyObject *op, PyObject *value) -{ - // By this additional check, this function refuses write access. - // We consider both nullptr and Py_None as not been written. - AutoDecRef has_val(get_signature_intern(op, nullptr)); - if (!(has_val.isNull() || has_val == Py_None)) { - PyErr_Format(PyExc_AttributeError, - "Attribute '__signature__' of '%.50s' object is not writable", - Py_TYPE(op)->tp_name); - return -1; - } - int ret = value == nullptr ? PyDict_DelItem(pyside_globals->value_dict, op) - : PyDict_SetItem(pyside_globals->value_dict, op, value); - Py_XINCREF(value); - return ret; -} - +// PYSIDE-535: We cannot patch types easily in PyPy. +// Let's use the `get_signature` function, instead. static PyGetSetDef new_PyCFunction_getsets[] = { {const_cast<char *>("__doc__"), reinterpret_cast<getter>(pyside_cf_get___doc__), nullptr, nullptr, nullptr}, - {const_cast<char *>("__signature__"), reinterpret_cast<getter>(pyside_cf_get___signature__), - reinterpret_cast<setter>(pyside_set___signature__), - nullptr, nullptr}, {nullptr, nullptr, nullptr, nullptr, nullptr} }; static PyGetSetDef new_PyStaticMethod_getsets[] = { {const_cast<char *>("__doc__"), reinterpret_cast<getter>(pyside_sm_get___doc__), nullptr, nullptr, nullptr}, - {const_cast<char *>("__signature__"), reinterpret_cast<getter>(pyside_sm_get___signature__), - reinterpret_cast<setter>(pyside_set___signature__), - nullptr, nullptr}, {nullptr, nullptr, nullptr, nullptr, nullptr} }; static PyGetSetDef new_PyMethodDescr_getsets[] = { {const_cast<char *>("__doc__"), reinterpret_cast<getter>(pyside_md_get___doc__), nullptr, nullptr, nullptr}, - {const_cast<char *>("__signature__"), reinterpret_cast<getter>(pyside_md_get___signature__), - reinterpret_cast<setter>(pyside_set___signature__), - nullptr, nullptr}, - {nullptr, nullptr, nullptr, nullptr, nullptr} -}; - -static PyGetSetDef new_PyType_getsets[] = { - {const_cast<char *>("__doc__"), reinterpret_cast<getter>(pyside_tp_get___doc__), - nullptr, nullptr, nullptr}, - {const_cast<char *>("__signature__"), reinterpret_cast<getter>(pyside_tp_get___signature__), - reinterpret_cast<setter>(pyside_set___signature__), - nullptr, nullptr}, {nullptr, nullptr, nullptr, nullptr, nullptr} }; static PyGetSetDef new_PyWrapperDescr_getsets[] = { {const_cast<char *>("__doc__"), reinterpret_cast<getter>(pyside_wd_get___doc__), nullptr, nullptr, nullptr}, - {const_cast<char *>("__signature__"), reinterpret_cast<getter>(pyside_wd_get___signature__), - reinterpret_cast<setter>(pyside_set___signature__), - nullptr, nullptr}, {nullptr, nullptr, nullptr, nullptr, nullptr} }; @@ -272,7 +201,7 @@ int PySide_PatchTypes(void) if (!init_done) { AutoDecRef meth_descr(PyObject_GetAttrString( - reinterpret_cast<PyObject *>(&PyString_Type), "split")); + reinterpret_cast<PyObject *>(&PyUnicode_Type), "split")); AutoDecRef wrap_descr(PyObject_GetAttrString( reinterpret_cast<PyObject *>(Py_TYPE(Py_True)), "__add__")); // abbreviations for readability @@ -282,8 +211,6 @@ int PySide_PatchTypes(void) auto cf_doc = &old_cf_doc_descr; auto sm_gs = new_PyStaticMethod_getsets; auto sm_doc = &old_sm_doc_descr; - auto tp_gs = new_PyType_getsets; - auto tp_doc = &old_tp_doc_descr; auto wd_gs = new_PyWrapperDescr_getsets; auto wd_doc = &old_wd_doc_descr; @@ -292,7 +219,6 @@ int PySide_PatchTypes(void) || add_more_getsets(PepMethodDescr_TypePtr, md_gs, md_doc) < 0 || add_more_getsets(&PyCFunction_Type, cf_gs, cf_doc) < 0 || add_more_getsets(PepStaticMethod_TypePtr, sm_gs, sm_doc) < 0 - || add_more_getsets(&PyType_Type, tp_gs, tp_doc) < 0 || add_more_getsets(Py_TYPE(wrap_descr), wd_gs, wd_doc) < 0 ) return -1; diff --git a/sources/shiboken6/libshiboken/signature/signature_globals.cpp b/sources/shiboken6/libshiboken/signature/signature_globals.cpp index f456e6a54..3a79a12d5 100644 --- a/sources/shiboken6/libshiboken/signature/signature_globals.cpp +++ b/sources/shiboken6/libshiboken/signature/signature_globals.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only //////////////////////////////////////////////////////////////////////////// // @@ -64,51 +28,37 @@ static const unsigned char PySide_SignatureLoader[] = { #include "embed/signature_bootstrap_inc.h" }; -static PyObject *_init_pyside_extension(PyObject * /* self */, PyObject * /* args */) +static safe_globals_struc *init_phase_1() { - init_module_1(); - init_module_2(); - Py_RETURN_NONE; -} - -// This function will be inserted into __builtins__. -static PyMethodDef init_methods[] = { - {"_init_pyside_extension", (PyCFunction)_init_pyside_extension, METH_NOARGS}, - {nullptr, nullptr} -}; - -static safe_globals_struc *init_phase_1(PyMethodDef *init_meth) -{ - { + do { auto *p = reinterpret_cast<safe_globals_struc *> (malloc(sizeof(safe_globals_struc))); if (p == nullptr) - goto error; + break; /* * Initializing module signature_bootstrap. * Since we now have an embedding script, we can do this without any * Python strings in the C code. */ -#ifdef Py_LIMITED_API - // We must work for multiple versions, so use source code. +#if defined(Py_LIMITED_API) || defined(SHIBOKEN_NO_EMBEDDING_PYC) + // We must work for multiple versions or we are cross-building for a different + // Python version interpreter, so use source code. #else - AutoDecRef marshal_module(PyImport_Import(PyName::marshal())); - if (marshal_module.isNull()) - goto error; + AutoDecRef marshal_module(PyImport_Import(PyName::marshal())); // builtin AutoDecRef loads(PyObject_GetAttr(marshal_module, PyName::loads())); if (loads.isNull()) - goto error; + break; #endif char *bytes_cast = reinterpret_cast<char *>( const_cast<unsigned char *>(PySide_SignatureLoader)); AutoDecRef bytes(PyBytes_FromStringAndSize(bytes_cast, sizeof(PySide_SignatureLoader))); if (bytes.isNull()) - goto error; -#ifdef Py_LIMITED_API + break; +#if defined(Py_LIMITED_API) || defined(SHIBOKEN_NO_EMBEDDING_PYC) PyObject *builtins = PyEval_GetBuiltins(); PyObject *compile = PyDict_GetItem(builtins, PyName::compile()); if (compile == nullptr) - goto error; + break; AutoDecRef code_obj(PyObject_CallFunction(compile, "Oss", bytes.object(), "signature_bootstrap.py", "exec")); #else @@ -116,69 +66,62 @@ static safe_globals_struc *init_phase_1(PyMethodDef *init_meth) loads, bytes.object(), nullptr)); #endif if (code_obj.isNull()) - goto error; + break; p->helper_module = PyImport_ExecCodeModule("signature_bootstrap", code_obj); if (p->helper_module == nullptr) - goto error; + break; // Initialize the module PyObject *mdict = PyModule_GetDict(p->helper_module); if (PyDict_SetItem(mdict, PyMagicName::builtins(), PyEval_GetBuiltins()) < 0) - goto error; - /* - * Unpack an embedded ZIP file with more signature modules. + break; + + /********************************************************************* + * + * Attention! + * ---------- + * + * We are unpacking an embedded ZIP file with more signature modules. * They will be loaded later with the zipimporter. - * Due to MSVC's limitation to 64k strings, we need to assemble pieces. + * The file `signature_bootstrap.py` does the unpacking and starts the + * loader. See `init_phase_2`. + * + * Due to MSVC's limitation to 64k strings, we needed to assemble pieces. */ - const char **block_ptr = (const char **)PySide_CompressedSignaturePackage; - int npieces = 0; - PyObject *piece, *zipped_string_sequence = PyList_New(0); - if (zipped_string_sequence == nullptr) - return nullptr; + auto **block_ptr = reinterpret_cast<const char **>(PySide_CompressedSignaturePackage); + PyObject *piece{}; + AutoDecRef zipped_string_sequence(PyList_New(0)); for (; **block_ptr != 0; ++block_ptr) { - npieces++; // we avoid the string/unicode dilemma by not using PyString_XXX: piece = Py_BuildValue("s", *block_ptr); if (piece == nullptr || PyList_Append(zipped_string_sequence, piece) < 0) - goto error; + break; } if (PyDict_SetItemString(mdict, "zipstring_sequence", zipped_string_sequence) < 0) - goto error; - Py_DECREF(zipped_string_sequence); + break; // build a dict for diverse mappings p->map_dict = PyDict_New(); - if (p->map_dict == nullptr) - goto error; // build a dict for the prepared arguments p->arg_dict = PyDict_New(); - if (p->arg_dict == nullptr - || PyObject_SetAttrString(p->helper_module, "pyside_arg_dict", p->arg_dict) < 0) - goto error; + if (PyObject_SetAttrString(p->helper_module, "pyside_arg_dict", p->arg_dict) < 0) + break; // build a dict for assigned signature values p->value_dict = PyDict_New(); - if (p->value_dict == nullptr) - goto error; // PYSIDE-1019: build a __feature__ dict p->feature_dict = PyDict_New(); - if (p->feature_dict == nullptr - || PyObject_SetAttrString(p->helper_module, "pyside_feature_dict", p->feature_dict) < 0) - goto error; + if (PyObject_SetAttrString(p->helper_module, "pyside_feature_dict", p->feature_dict) < 0) + break; // This function will be disabled until phase 2 is done. p->finish_import_func = nullptr; - // Initialize the explicit init function. - AutoDecRef init(PyCFunction_NewEx(init_meth, nullptr, nullptr)); - if (init.isNull() - || PyDict_SetItemString(PyEval_GetBuiltins(), init_meth->ml_name, init) != 0) - goto error; - return p; - } -error: + + } while (0); + PyErr_Print(); Py_FatalError("could not initialize part 1"); return nullptr; @@ -186,7 +129,7 @@ error: static int init_phase_2(safe_globals_struc *p, PyMethodDef *methods) { - { + do { PyMethodDef *ml; // The single function to be called, but maybe more to come. @@ -194,7 +137,7 @@ static int init_phase_2(safe_globals_struc *p, PyMethodDef *methods) PyObject *v = PyCFunction_NewEx(ml, nullptr, nullptr); if (v == nullptr || PyObject_SetAttrString(p->helper_module, ml->ml_name, v) != 0) - goto error; + break; Py_DECREF(v); } // The first entry is __feature_import__, add documentation. @@ -205,33 +148,59 @@ static int init_phase_2(safe_globals_struc *p, PyMethodDef *methods) PyObject *bootstrap_func = PyObject_GetAttrString(p->helper_module, "bootstrap"); if (bootstrap_func == nullptr) - goto error; - // The return value of the bootstrap function is the loader module. - PyObject *loader = PyObject_CallFunction(bootstrap_func, "()"); + break; + + /********************************************************************* + * + * Attention! + * ---------- + * + * This is the entry point where everything in folder + * `shibokensupport` becomes initialized. It starts with + * `signature_bootstrap.py` and continues from there to `loader.py`. + * + * The return value of the bootstrap function is the loader module. + */ + PyObject *loader = PyObject_CallFunctionObjArgs(bootstrap_func, nullptr); if (loader == nullptr) - goto error; + break; + // now the loader should be initialized p->pyside_type_init_func = PyObject_GetAttrString(loader, "pyside_type_init"); if (p->pyside_type_init_func == nullptr) - goto error; + break; p->create_signature_func = PyObject_GetAttrString(loader, "create_signature"); if (p->create_signature_func == nullptr) - goto error; + break; p->seterror_argument_func = PyObject_GetAttrString(loader, "seterror_argument"); if (p->seterror_argument_func == nullptr) - goto error; + break; p->make_helptext_func = PyObject_GetAttrString(loader, "make_helptext"); if (p->make_helptext_func == nullptr) - goto error; + break; p->finish_import_func = PyObject_GetAttrString(loader, "finish_import"); if (p->finish_import_func == nullptr) - goto error; + break; p->feature_import_func = PyObject_GetAttrString(loader, "feature_import"); if (p->feature_import_func == nullptr) - goto error; + break; + p->feature_imported_func = PyObject_GetAttrString(loader, "feature_imported"); + if (p->feature_imported_func == nullptr) + break; + + // We call stuff like the feature initialization late, + // after all the function pointers are in place. + PyObject *post_init_func = PyObject_GetAttrString(loader, "post_init"); + if (post_init_func == nullptr) + break; + PyObject *ret = PyObject_CallFunctionObjArgs(post_init_func, nullptr); + if (ret == nullptr) + break; + return 0; - } -error: + + } while (0); + PyErr_Print(); Py_FatalError("could not initialize part 2"); return -1; @@ -240,12 +209,12 @@ error: #ifndef _WIN32 //////////////////////////////////////////////////////////////////////////// // a stack trace for linux-like platforms -#include <stdio.h> +#include <cstdio> #if defined(__GLIBC__) # include <execinfo.h> #endif #include <signal.h> -#include <stdlib.h> +#include <cstdlib> #include <unistd.h> static void handler(int sig) { @@ -258,7 +227,7 @@ static void handler(int sig) { // print out all the frames to stderr #endif - fprintf(stderr, "Error: signal %d:\n", sig); + std::fprintf(stderr, "Error: signal %d:\n", sig); #if defined(__GLIBC__) backtrace_symbols_fd(array, size, STDERR_FILENO); #endif @@ -268,14 +237,14 @@ static void handler(int sig) { //////////////////////////////////////////////////////////////////////////// #endif // _WIN32 -safe_globals pyside_globals = nullptr; +safe_globals_struc *pyside_globals = nullptr; -void init_module_1(void) +void init_shibokensupport_module(void) { static int init_done = 0; if (!init_done) { - pyside_globals = init_phase_1(init_methods); + pyside_globals = init_phase_1(); if (pyside_globals != nullptr) init_done = 1; @@ -286,17 +255,6 @@ void init_module_1(void) signal(SIGSEGV, handler); // install our handler #endif // _WIN32 - } -} - -void init_module_2(void) -{ - static int init_done = 0; - - if (!init_done) { - // Phase 2 will call __init__.py which touches a signature, itself. - // Therefore we set init_done prior to init_phase_2(). - init_done = 1; init_phase_2(pyside_globals, signature_methods); // Enum must be initialized when signatures exist, not earlier. init_enum(); diff --git a/sources/shiboken6/libshiboken/signature/signature_helper.cpp b/sources/shiboken6/libshiboken/signature/signature_helper.cpp index f38740254..cf84cfa13 100644 --- a/sources/shiboken6/libshiboken/signature/signature_helper.cpp +++ b/sources/shiboken6/libshiboken/signature/signature_helper.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only //////////////////////////////////////////////////////////////////////////// // @@ -57,19 +21,6 @@ using namespace Shiboken; extern "C" { -// Helper for __qualname__ which might not always exist in Python 2 (type). -PyObject *_get_qualname(PyObject *ob) -{ - // We support __qualname__ for types, only. - assert(PyType_Check(ob)); - PyObject *name = PyObject_GetAttr(ob, PyMagicName::qualname()); - if (name == nullptr) { - PyErr_Clear(); - name = PyObject_GetAttr(ob, PyMagicName::name()); - } - return name; -} - static int _fixup_getset(PyTypeObject *type, const char *name, PyGetSetDef *new_gsp) { /* @@ -92,8 +43,6 @@ static int _fixup_getset(PyTypeObject *type, const char *name, PyGetSetDef *new_ for (; md->name != nullptr; md++) if (strcmp(md->name, name) == 0) return 1; - // staticmethod has just a `__doc__` in the class - assert(strcmp(type->tp_name, "staticmethod") == 0 && strcmp(name, "__doc__") == 0); return 0; } @@ -102,10 +51,13 @@ int add_more_getsets(PyTypeObject *type, PyGetSetDef *gsp, PyObject **doc_descr) /* * This function is used to assign a new `__signature__` attribute, * and also to override a `__doc__` or `__name__` attribute. + * + * PYSIDE-2101: The __signature__ attribute is gone due to rlcompleter. */ assert(PyType_Check(type)); PyType_Ready(type); - PyObject *dict = type->tp_dict; + AutoDecRef tpDict(PepType_GetDict(type)); + auto *dict = tpDict.object(); for (; gsp->name != nullptr; gsp++) { PyObject *have_descr = PyDict_GetItemString(dict, gsp->name); if (have_descr != nullptr) { @@ -120,6 +72,9 @@ int add_more_getsets(PyTypeObject *type, PyGetSetDef *gsp, PyObject **doc_descr) AutoDecRef descr(PyDescr_NewGetSet(type, gsp)); if (descr.isNull()) return -1; + // PYSIDE-535: We cannot set the attribute. For simplicity, we use + // get_signature in PyPy, instead. This can be re-implemented + // later by deriving extra heap types. if (PyDict_SetItemString(dict, gsp->name, descr) < 0) return -1; } @@ -236,7 +191,7 @@ static PyObject *_build_new_entry(PyObject *new_name, PyObject *value) PyObject *new_value = PyDict_Copy(value); PyObject *multi = PyDict_GetItem(value, PyName::multi()); if (multi != nullptr && Py_TYPE(multi) == &PyList_Type) { - ssize_t len = PyList_Size(multi); + Py_ssize_t len = PyList_Size(multi); AutoDecRef list(PyList_New(len)); if (list.isNull()) return nullptr; @@ -271,6 +226,15 @@ int insert_snake_case_variants(PyObject *dict) return PyDict_Merge(dict, snake_dict, 0); } +#ifdef PYPY_VERSION +PyObject *_get_class_of_bm(PyObject *ob_bm) +{ + AutoDecRef self(PyObject_GetAttr(ob_bm, PyMagicName::self())); + auto *klass = PyObject_GetAttr(self, PyMagicName::class_()); + return klass; +} +#endif + PyObject *_get_class_of_cf(PyObject *ob_cf) { PyObject *selftype = PyCFunction_GET_SELF(ob_cf); @@ -314,7 +278,7 @@ PyObject *_address_to_stringlist(PyObject *numkey) * When needed in `PySide_BuildSignatureProps`, the strings are * finally materialized. */ - ssize_t address = PyNumber_AsSsize_t(numkey, PyExc_ValueError); + Py_ssize_t address = PyNumber_AsSsize_t(numkey, PyExc_ValueError); if (address == -1 && PyErr_Occurred()) return nullptr; char **sig_strings = reinterpret_cast<char **>(address); @@ -330,7 +294,7 @@ PyObject *_address_to_stringlist(PyObject *numkey) return res_list; } -static int _build_func_to_type(PyObject *obtype) +int _build_func_to_type(PyObject *obtype) { /* * There is no general way to directly get the type of a static method. @@ -346,7 +310,17 @@ static int _build_func_to_type(PyObject *obtype) * We also check for hidden methods, see below. */ auto *type = reinterpret_cast<PyTypeObject *>(obtype); - PyObject *dict = type->tp_dict; + AutoDecRef tpDict(PepType_GetDict(type)); + auto *dict = tpDict.object(); + + // PYSIDE-2404: Get the original dict for late initialization. + // The dict might have been switched before signature init. + static const auto *pyTypeType_tp_dict = PepType_GetDict(&PyType_Type); + if (Py_TYPE(dict) != Py_TYPE(pyTypeType_tp_dict)) { + tpDict.reset(PyObject_GetAttr(dict, PyName::orig_dict())); + dict = tpDict.object(); + } + PyMethodDef *meth = type->tp_methods; if (meth == nullptr) @@ -412,26 +386,4 @@ static int _build_func_to_type(PyObject *obtype) return 0; } -int _finish_nested_classes(PyObject *obdict) -{ - PyObject *key, *value, *obtype; - PyTypeObject *subtype; - Py_ssize_t pos = 0; - - if (obdict == nullptr) - return -1; - while (PyDict_Next(obdict, &pos, &key, &value)) { - if (PyType_Check(value)) { - obtype = value; - if (_build_func_to_type(obtype) < 0) - return -1; - // now continue with nested cases - subtype = reinterpret_cast<PyTypeObject *>(obtype); - if (_finish_nested_classes(subtype->tp_dict) < 0) - return -1; - } - } - return 0; -} - } // extern "C" diff --git a/sources/shiboken6/libshiboken/signature/signature_p.h b/sources/shiboken6/libshiboken/signature/signature_p.h deleted file mode 100644 index cdfc1cbc1..000000000 --- a/sources/shiboken6/libshiboken/signature/signature_p.h +++ /dev/null @@ -1,108 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef SIGNATURE_IMPL_H -#define SIGNATURE_IMPL_H - -#include "signature.h" - -extern "C" { - -// signature_globals.cpp - -typedef struct safe_globals_struc { - // init part 1: get arg_dict - PyObject *helper_module; - PyObject *arg_dict; - PyObject *map_dict; - PyObject *value_dict; // for writing signatures - PyObject *feature_dict; // registry for PySide.support.__feature__ - // init part 2: run module - PyObject *pyside_type_init_func; - PyObject *create_signature_func; - PyObject *seterror_argument_func; - PyObject *make_helptext_func; - PyObject *finish_import_func; - PyObject *feature_import_func; -} safe_globals_struc, *safe_globals; - -extern safe_globals pyside_globals; -extern PyMethodDef signature_methods[]; - -void init_module_1(void); -void init_module_2(void); - -// signature.cpp - -PyObject *GetTypeKey(PyObject *ob); - -PyObject *GetSignature_Function(PyObject *, PyObject *); -PyObject *GetSignature_TypeMod(PyObject *, PyObject *); -PyObject *GetSignature_Wrapper(PyObject *, PyObject *); - -PyObject *get_signature_intern(PyObject *ob, PyObject *modifier); -PyObject *PySide_BuildSignatureProps(PyObject *class_mod); -PyObject *GetClassOrModOf(PyObject *ob); - -// signature_extend.cpp - -PyObject *pyside_cf_get___signature__(PyObject *func, PyObject *modifier); -PyObject *pyside_sm_get___signature__(PyObject *sm, PyObject *modifier); -PyObject *pyside_md_get___signature__(PyObject *ob_md, PyObject *modifier); -PyObject *pyside_wd_get___signature__(PyObject *ob, PyObject *modifier); -PyObject *pyside_tp_get___signature__(PyObject *obtype_mod, PyObject *modifier); - -int PySide_PatchTypes(void); -PyObject *pyside_tp_get___doc__(PyObject *tp); - -// signature_helper.cpp - -PyObject *_get_qualname(PyObject *ob); -int add_more_getsets(PyTypeObject *type, PyGetSetDef *gsp, PyObject **doc_descr); -PyObject *name_key_to_func(PyObject *ob); -int insert_snake_case_variants(PyObject *dict); -PyObject *_get_class_of_cf(PyObject *ob_cf); -PyObject *_get_class_of_sm(PyObject *ob_sm); -PyObject *_get_class_of_descr(PyObject *ob); -PyObject *_address_to_stringlist(PyObject *numkey); -int _finish_nested_classes(PyObject *dict); - -} // extern "C" - -#endif // SIGNATURE_IMPL_H diff --git a/sources/shiboken6/libshiboken/signature_p.h b/sources/shiboken6/libshiboken/signature_p.h new file mode 100644 index 000000000..d0c4ee537 --- /dev/null +++ b/sources/shiboken6/libshiboken/signature_p.h @@ -0,0 +1,78 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef SIGNATURE_IMPL_H +#define SIGNATURE_IMPL_H + +#include "signature.h" + +extern "C" { + +// signature_globals.cpp + +struct safe_globals_struc { + // init part 1: get arg_dict + PyObject *helper_module; + PyObject *arg_dict; + PyObject *map_dict; + PyObject *value_dict; // for writing signatures + PyObject *feature_dict; // registry for PySide.support.__feature__ + // init part 2: run module + PyObject *pyside_type_init_func; + PyObject *create_signature_func; + PyObject *seterror_argument_func; + PyObject *make_helptext_func; + PyObject *finish_import_func; + PyObject *feature_import_func; + PyObject *feature_imported_func; +}; + +extern safe_globals_struc *pyside_globals; +extern PyMethodDef signature_methods[]; + +void init_shibokensupport_module(void); + +// signature.cpp + +PyObject *GetTypeKey(PyObject *ob); + +PyObject *GetSignature_Function(PyObject *, PyObject *); +PyObject *GetSignature_TypeMod(PyObject *, PyObject *); +PyObject *GetSignature_Wrapper(PyObject *, PyObject *); + +LIBSHIBOKEN_API PyObject *get_signature_intern(PyObject *ob, PyObject *modifier); +PyObject *PySide_BuildSignatureProps(PyObject *class_mod); +PyObject *GetClassOrModOf(PyObject *ob); + +// signature_extend.cpp +PyObject *pyside_cf_get___signature__(PyObject *func, PyObject *modifier); +PyObject *pyside_sm_get___signature__(PyObject *sm, PyObject *modifier); +PyObject *pyside_md_get___signature__(PyObject *ob_md, PyObject *modifier); +PyObject *pyside_wd_get___signature__(PyObject *ob, PyObject *modifier); +PyObject *pyside_tp_get___signature__(PyObject *obtype_mod, PyObject *modifier); + +int PySide_PatchTypes(void); +PyObject *pyside_tp_get___doc__(PyObject *tp); + +// signature_helper.cpp + +int add_more_getsets(PyTypeObject *type, PyGetSetDef *gsp, PyObject **doc_descr); +PyObject *name_key_to_func(PyObject *ob); +int insert_snake_case_variants(PyObject *dict); +PyObject *_get_class_of_cf(PyObject *ob_cf); +PyObject *_get_class_of_sm(PyObject *ob_sm); +PyObject *_get_class_of_descr(PyObject *ob); +PyObject *_address_to_stringlist(PyObject *numkey); +int _build_func_to_type(PyObject *obtype); +int _finish_nested_classes(PyObject *dict); + +#ifdef PYPY_VERSION +// PyPy has a special builtin method. +PyObject *GetSignature_Method(PyObject *, PyObject *); +PyObject *pyside_bm_get___signature__(PyObject *func, PyObject *modifier); +PyObject *_get_class_of_bm(PyObject *ob_cf); +#endif + +} // extern "C" + +#endif // SIGNATURE_IMPL_H diff --git a/sources/shiboken6/libshiboken/threadstatesaver.cpp b/sources/shiboken6/libshiboken/threadstatesaver.cpp index 7c587b405..9f74ed442 100644 --- a/sources/shiboken6/libshiboken/threadstatesaver.cpp +++ b/sources/shiboken6/libshiboken/threadstatesaver.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "threadstatesaver.h" diff --git a/sources/shiboken6/libshiboken/threadstatesaver.h b/sources/shiboken6/libshiboken/threadstatesaver.h index ddfbcb93b..4289f6726 100644 --- a/sources/shiboken6/libshiboken/threadstatesaver.h +++ b/sources/shiboken6/libshiboken/threadstatesaver.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef THREADSTATESAVER_H #define THREADSTATESAVER_H diff --git a/sources/shiboken6/libshiboken/voidptr.cpp b/sources/shiboken6/libshiboken/voidptr.cpp index 6a69c39fd..7045b08b1 100644 --- a/sources/shiboken6/libshiboken/voidptr.cpp +++ b/sources/shiboken6/libshiboken/voidptr.cpp @@ -1,43 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "voidptr.h" +#include "pep384ext.h" #include "sbkconverter.h" #include "basewrapper.h" #include "basewrapper_p.h" @@ -46,22 +11,21 @@ extern "C" { // Void pointer object definition. -typedef struct { +struct SbkVoidPtrObject { PyObject_HEAD void *cptr; Py_ssize_t size; bool isWritable; -} SbkVoidPtrObject; +}; -PyObject *SbkVoidPtrObject_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +PyObject *SbkVoidPtrObject_new(PyTypeObject *type, PyObject * /* args */, PyObject * /* kwds */) { // PYSIDE-560: It is much safer to first call a function and then do a // type cast than to do everything in one line. The bad construct looked // like this, actual call forgotten: // SbkVoidPtrObject *self = // reinterpret_cast<SbkVoidPtrObject *>(type->tp_alloc); - PyObject *ob = type->tp_alloc(type, 0); - auto *self = reinterpret_cast<SbkVoidPtrObject *>(ob); + auto *self = PepExt_TypeCallAlloc<SbkVoidPtrObject>(type, 0); if (self != nullptr) { self->cptr = nullptr; @@ -72,7 +36,7 @@ PyObject *SbkVoidPtrObject_new(PyTypeObject *type, PyObject *args, PyObject *kwd return reinterpret_cast<PyObject *>(self); } -#define SbkVoidPtr_Check(op) (Py_TYPE(op) == SbkVoidPtrTypeF()) +#define SbkVoidPtr_Check(op) (Py_TYPE(op) == SbkVoidPtr_TypeF()) int SbkVoidPtrObject_init(PyObject *self, PyObject *args, PyObject *kwds) @@ -189,7 +153,7 @@ PyObject *SbkVoidPtrObject_int(PyObject *v) return PyLong_FromVoidPtr(sbkObject->cptr); } -PyObject *toBytes(PyObject *self, PyObject *args) +PyObject *toBytes(PyObject *self, PyObject * /* args */) { auto *sbkObject = reinterpret_cast<SbkVoidPtrObject *>(self); if (sbkObject->size < 0) { @@ -203,8 +167,8 @@ PyObject *toBytes(PyObject *self, PyObject *args) } static struct PyMethodDef SbkVoidPtrObject_methods[] = { - {"toBytes", toBytes, METH_NOARGS}, - {nullptr} + {"toBytes", toBytes, METH_NOARGS, nullptr}, + {nullptr, nullptr, 0, nullptr} }; static Py_ssize_t SbkVoidPtrObject_length(PyObject *v) @@ -292,47 +256,49 @@ static PyBufferProcs SbkVoidPtrObjectBufferProc = { (releasebufferproc)nullptr // bf_releasebuffer }; -// Void pointer type definition. -static PyType_Slot SbkVoidPtrType_slots[] = { - {Py_tp_repr, reinterpret_cast<void *>(SbkVoidPtrObject_repr)}, - {Py_nb_int, reinterpret_cast<void *>(SbkVoidPtrObject_int)}, - {Py_sq_length, reinterpret_cast<void *>(SbkVoidPtrObject_length)}, - {Py_tp_str, reinterpret_cast<void *>(SbkVoidPtrObject_str)}, - {Py_tp_richcompare, reinterpret_cast<void *>(SbkVoidPtrObject_richcmp)}, - {Py_tp_init, reinterpret_cast<void *>(SbkVoidPtrObject_init)}, - {Py_tp_new, reinterpret_cast<void *>(SbkVoidPtrObject_new)}, - {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)}, - {Py_tp_methods, reinterpret_cast<void *>(SbkVoidPtrObject_methods)}, - {0, nullptr} -}; -static PyType_Spec SbkVoidPtrType_spec = { - "2:shiboken6.shiboken6.VoidPtr", - sizeof(SbkVoidPtrObject), - 0, - Py_TPFLAGS_DEFAULT, - SbkVoidPtrType_slots, -}; - - +static PyTypeObject *createVoidPtrType() +{ + PyType_Slot SbkVoidPtrType_slots[] = { + {Py_tp_repr, reinterpret_cast<void *>(SbkVoidPtrObject_repr)}, + {Py_nb_int, reinterpret_cast<void *>(SbkVoidPtrObject_int)}, + {Py_sq_length, reinterpret_cast<void *>(SbkVoidPtrObject_length)}, + {Py_tp_str, reinterpret_cast<void *>(SbkVoidPtrObject_str)}, + {Py_tp_richcompare, reinterpret_cast<void *>(SbkVoidPtrObject_richcmp)}, + {Py_tp_init, reinterpret_cast<void *>(SbkVoidPtrObject_init)}, + {Py_tp_new, reinterpret_cast<void *>(SbkVoidPtrObject_new)}, + {Py_tp_dealloc, reinterpret_cast<void *>(Sbk_object_dealloc)}, + {Py_tp_methods, reinterpret_cast<void *>(SbkVoidPtrObject_methods)}, + {0, nullptr} + }; + + PyType_Spec SbkVoidPtrType_spec = { + "2:shiboken6.Shiboken.VoidPtr", + sizeof(SbkVoidPtrObject), + 0, + Py_TPFLAGS_DEFAULT, + SbkVoidPtrType_slots, + }; + + return SbkType_FromSpec_BMDWB(&SbkVoidPtrType_spec, + nullptr, nullptr, 0, 0, + &SbkVoidPtrObjectBufferProc); } -PyTypeObject *SbkVoidPtrTypeF(void) +PyTypeObject *SbkVoidPtr_TypeF(void) { - static PyTypeObject *type = nullptr; - if (!type) { - type = reinterpret_cast<PyTypeObject *>(SbkType_FromSpec(&SbkVoidPtrType_spec)); - PepType_AS_BUFFER(type) = &SbkVoidPtrObjectBufferProc; - } + static auto *type = createVoidPtrType(); return type; } +} // extern "C" + namespace VoidPtr { static int voidPointerInitialized = false; void init() { - if (PyType_Ready(reinterpret_cast<PyTypeObject *>(SbkVoidPtrTypeF())) < 0) + if (PyType_Ready(SbkVoidPtr_TypeF()) < 0) Py_FatalError("[libshiboken] Failed to initialize Shiboken.VoidPtr type."); else voidPointerInitialized = true; @@ -341,9 +307,9 @@ void init() void addVoidPtrToModule(PyObject *module) { if (voidPointerInitialized) { - Py_INCREF(SbkVoidPtrTypeF()); - PyModule_AddObject(module, PepType_GetNameStr(SbkVoidPtrTypeF()), - reinterpret_cast<PyObject *>(SbkVoidPtrTypeF())); + Py_INCREF(SbkVoidPtr_TypeF()); + PyModule_AddObject(module, PepType_GetNameStr(SbkVoidPtr_TypeF()), + reinterpret_cast<PyObject *>(SbkVoidPtr_TypeF())); } } @@ -352,7 +318,7 @@ static PyObject *createVoidPtr(void *cppIn, Py_ssize_t size = 0, bool isWritable if (!cppIn) Py_RETURN_NONE; - SbkVoidPtrObject *result = PyObject_New(SbkVoidPtrObject, SbkVoidPtrTypeF()); + SbkVoidPtrObject *result = PyObject_New(SbkVoidPtrObject, SbkVoidPtr_TypeF()); if (!result) Py_RETURN_NONE; @@ -425,7 +391,7 @@ static PythonToCppFunc PythonBufferToCppIsConvertible(PyObject *pyIn) SbkConverter *createConverter() { - SbkConverter *converter = Shiboken::Conversions::createConverter(SbkVoidPtrTypeF(), toPython); + SbkConverter *converter = Shiboken::Conversions::createConverter(SbkVoidPtr_TypeF(), toPython); Shiboken::Conversions::addPythonToCppValueConversion(converter, VoidPtrToCpp, VoidPtrToCppIsConvertible); @@ -440,28 +406,28 @@ SbkConverter *createConverter() void setSize(PyObject *voidPtr, Py_ssize_t size) { - assert(voidPtr->ob_type == SbkVoidPtrTypeF()); + assert(voidPtr->ob_type == SbkVoidPtr_TypeF()); auto *voidPtrObj = reinterpret_cast<SbkVoidPtrObject *>(voidPtr); voidPtrObj->size = size; } Py_ssize_t getSize(PyObject *voidPtr) { - assert(voidPtr->ob_type == SbkVoidPtrTypeF()); + assert(voidPtr->ob_type == SbkVoidPtr_TypeF()); auto *voidPtrObj = reinterpret_cast<SbkVoidPtrObject *>(voidPtr); return voidPtrObj->size; } bool isWritable(PyObject *voidPtr) { - assert(voidPtr->ob_type == SbkVoidPtrTypeF()); + assert(voidPtr->ob_type == SbkVoidPtr_TypeF()); auto *voidPtrObj = reinterpret_cast<SbkVoidPtrObject *>(voidPtr); return voidPtrObj->isWritable; } void setWritable(PyObject *voidPtr, bool isWritable) { - assert(voidPtr->ob_type == SbkVoidPtrTypeF()); + assert(voidPtr->ob_type == SbkVoidPtr_TypeF()); auto *voidPtrObj = reinterpret_cast<SbkVoidPtrObject *>(voidPtr); voidPtrObj->isWritable = isWritable; } diff --git a/sources/shiboken6/libshiboken/voidptr.h b/sources/shiboken6/libshiboken/voidptr.h index 018f6124e..8360bf9c7 100644 --- a/sources/shiboken6/libshiboken/voidptr.h +++ b/sources/shiboken6/libshiboken/voidptr.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef VOIDPTR_H #define VOIDPTR_H @@ -48,7 +12,7 @@ extern "C" { // Void pointer type declaration. -extern LIBSHIBOKEN_API PyTypeObject *SbkVoidPtrTypeF(void); +extern LIBSHIBOKEN_API PyTypeObject *SbkVoidPtr_TypeF(void); } // extern "C" |