diff options
Diffstat (limited to 'sources/shiboken6/libshiboken/sbkconverter.cpp')
-rw-r--r-- | sources/shiboken6/libshiboken/sbkconverter.cpp | 263 |
1 files changed, 218 insertions, 45 deletions
diff --git a/sources/shiboken6/libshiboken/sbkconverter.cpp b/sources/shiboken6/libshiboken/sbkconverter.cpp index 334fb4297..9ab674415 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" @@ -47,15 +12,19 @@ #include "voidptr.h" #include <string> +#include <cstring> +#include <iostream> #include <unordered_map> +#include <unordered_set> +#include <map> +#include <set> static SbkConverter **PrimitiveTypeConverters; using ConvertersMap = std::unordered_map<std::string, SbkConverter *>; static ConvertersMap converters; -namespace Shiboken { -namespace Conversions { +namespace Shiboken::Conversions { void initArrayConverters(); @@ -108,6 +77,103 @@ void init() initArrayConverters(); } +static void dumpPyTypeObject(std::ostream &str, PyTypeObject *t) +{ + str << "\nPython type "; + if (t == nullptr) { + str << "<None>"; + return; + } + str << '"' << t->tp_name << '"'; + if (t->tp_base != nullptr && t->tp_base != &PyBaseObject_Type) + str << '(' << t->tp_base->tp_name << ')'; +} + +static void dumpSbkConverter(std::ostream &str, const SbkConverter *c) +{ + str << "SbkConverter " << static_cast<const void *>(c) << ": "; + if (c->pointerToPython != nullptr) + str << ", C++ pointer->Python"; + if (c->copyToPython != nullptr) + str << ", copy->Python"; + if (c->toCppPointerConversion.second != nullptr) + str << ", Python->C++ pointer"; + if (!c->toCppConversions.empty()) + str << ", " << c->toCppConversions.size() << " Python->C++ conversions"; +} + +// Less than operator for a PyTypeObject for dumping the converter map +static bool pyTypeObjectLessThan(const PyTypeObject *t1, const PyTypeObject *t2) +{ + const bool isNull1 = t1 == nullptr; + const bool isNull2 = t2 == nullptr; + if (isNull1 || isNull2) + return isNull1 && !isNull2; + // Internal types (lower case) first + const bool isInternal1 = std::islower(t1->tp_name[0]); + const bool isInternal2 = std::islower(t2->tp_name[0]); + if (isInternal1 != isInternal2) + return !isInternal2; + return std::strcmp(t1->tp_name, t2->tp_name) < 0; +} + +void dumpConverters() +{ + struct PyTypeObjectLess { + + bool operator()(const PyTypeObject *t1, const PyTypeObject *t2) const { + return pyTypeObjectLessThan(t1, t2); + } + }; + + using StringSet = std::set<std::string>; + using SbkConverterNamesMap = std::unordered_map<SbkConverter *, StringSet>; + using PyTypeObjectConverterMap = std::map<PyTypeObject *, SbkConverterNamesMap, + PyTypeObjectLess>; + + auto &str = std::cerr; + + // Sort the entries by the associated PyTypeObjects and converters + PyTypeObjectConverterMap pyTypeObjectConverterMap; + for (const auto &converter : converters) { + auto *sbkConverter = converter.second; + if (sbkConverter == nullptr) { + str << "Non-existent: \"" << converter.first << "\"\n"; + continue; + } + auto *typeObject = sbkConverter->pythonType; + auto typeIt = pyTypeObjectConverterMap.find(typeObject); + if (typeIt == pyTypeObjectConverterMap.end()) + typeIt = pyTypeObjectConverterMap.insert(std::make_pair(typeObject, + SbkConverterNamesMap{})).first; + SbkConverterNamesMap &sbkConverterMap = typeIt->second; + auto convIt = sbkConverterMap.find(sbkConverter); + if (convIt == sbkConverterMap.end()) + convIt = sbkConverterMap.insert(std::make_pair(sbkConverter, + StringSet{})).first; + convIt->second.insert(converter.first); + } + + for (const auto &tc : pyTypeObjectConverterMap) { + dumpPyTypeObject(str, tc.first); + str << ", " << tc.second.size() << " converter(s):\n"; + for (const auto &cn : tc.second) { + str << " "; + dumpSbkConverter(str, cn.first); + str << ", " << cn.second.size() << " alias(es):"; + int i = 0; + for (const auto &name : cn.second) { + if ((i++ % 5) == 0) + str << "\n "; + str << " \"" << name << '"'; + } + str << '\n'; + } + } + + str << '\n'; +} + SbkConverter *createConverterObject(PyTypeObject *type, PythonToCppFunc toCppPointerConvFunc, IsConvertibleToCppFunc toCppPointerCheckFunc, @@ -183,6 +249,13 @@ void addPythonToCppValueConversion(PyTypeObject *type, addPythonToCppValueConversion(sotp->converter, pythonToCppFunc, isConvertibleToCppFunc); } +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); @@ -264,6 +337,11 @@ PythonToCppConversion pythonToCppPointerConversion(PyTypeObject *type, PyObject return {}; } +PythonToCppConversion pythonToCppPointerConversion(Module::TypeInitStruct typeStruct, PyObject *pyIn) +{ + return pythonToCppPointerConversion(typeStruct.type, pyIn); +} + static inline PythonToCppFunc IsPythonToCppConvertible(const SbkConverter *converter, PyObject *pyIn) { assert(pyIn); @@ -439,19 +517,55 @@ bool isImplicitConversion(PyTypeObject *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)); } -SbkConverter *getConverter(const char *typeName) +static std::string getRealTypeName(const std::string &typeName) +{ + auto size = typeName.size(); + if (std::isalnum(typeName[size - 1]) == 0) + return typeName.substr(0, size - 1); + return typeName; +} + +// PYSIDE-2404: Build a negative cache of already failed lookups. +// The resulting list must be reset after each new import, +// because that can change results. Also clear the cache after +// reaching some threashold. +static std::unordered_set<std::string> nonExistingTypeNames{}; + +// Arbitrary size limit to prevent random name overflows. +static constexpr std::size_t negativeCacheLimit = 50; + +static void rememberAsNonexistent(const std::string &typeName) +{ + if (nonExistingTypeNames.size() > negativeCacheLimit) + clearNegativeLazyCache(); + converters.insert(std::make_pair(typeName, nullptr)); + nonExistingTypeNames.insert(typeName); +} + +SbkConverter *getConverter(const char *typeNameC) { - ConvertersMap::const_iterator it = converters.find(typeName); + std::string typeName = typeNameC; + auto it = converters.find(typeName); + // PYSIDE-2404: This can also contain explicit nullptr as a negative cache. + if (it != converters.end()) + return it->second; + // 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 (Py_VerboseFlag > 0) { + // Cache the negative result. Don't forget to clear the cache for new modules. + rememberAsNonexistent(typeName); + + 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); @@ -459,11 +573,41 @@ SbkConverter *getConverter(const char *typeName) return nullptr; } +void clearNegativeLazyCache() +{ + for (const auto &typeName : nonExistingTypeNames) { + auto it = converters.find(typeName); + converters.erase(it); + } + nonExistingTypeNames.clear(); +} + 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); @@ -480,6 +624,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); @@ -500,6 +666,13 @@ bool convertibleSequenceTypes(PyTypeObject *type, PyObject *pyIn) 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); @@ -734,4 +907,4 @@ void SpecificConverter::toCpp(PyObject *pyIn, void *cppOut) } } -} } // namespace Shiboken::Conversions +} // namespace Shiboken::Conversions |