diff options
Diffstat (limited to 'sources/shiboken6/libshiboken/sbkconverter.cpp')
-rw-r--r-- | sources/shiboken6/libshiboken/sbkconverter.cpp | 910 |
1 files changed, 910 insertions, 0 deletions
diff --git a/sources/shiboken6/libshiboken/sbkconverter.cpp b/sources/shiboken6/libshiboken/sbkconverter.cpp new file mode 100644 index 000000000..9ab674415 --- /dev/null +++ b/sources/shiboken6/libshiboken/sbkconverter.cpp @@ -0,0 +1,910 @@ +// 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" +#include "helper.h" +#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::Conversions { + +void initArrayConverters(); + +void init() +{ + static SbkConverter *primitiveTypeConverters[] = { + Primitive<PY_LONG_LONG>::createConverter(), + Primitive<bool>::createConverter(), + Primitive<char>::createConverter(), + Primitive<const char *>::createConverter(), + Primitive<double>::createConverter(), + Primitive<float>::createConverter(), + Primitive<int>::createConverter(), + Primitive<long>::createConverter(), + 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(), + Primitive<unsigned long>::createConverter(), + Primitive<unsigned short>::createConverter(), + VoidPtr::createConverter(), + Primitive<std::nullptr_t>::createConverter() + }; + PrimitiveTypeConverters = primitiveTypeConverters; + + assert(converters.empty()); + converters["PY_LONG_LONG"] = primitiveTypeConverters[SBK_PY_LONG_LONG_IDX]; + converters["bool"] = primitiveTypeConverters[SBK_BOOL_IDX_1]; + converters["char"] = primitiveTypeConverters[SBK_CHAR_IDX]; + converters["const char *"] = primitiveTypeConverters[SBK_CONSTCHARPTR_IDX]; + converters["double"] = primitiveTypeConverters[SBK_DOUBLE_IDX]; + converters["float"] = primitiveTypeConverters[SBK_FLOAT_IDX]; + converters["int"] = primitiveTypeConverters[SBK_INT_IDX]; + converters["long"] = primitiveTypeConverters[SBK_LONG_IDX]; + converters["short"] = primitiveTypeConverters[SBK_SHORT_IDX]; + converters["signed char"] = primitiveTypeConverters[SBK_SIGNEDCHAR_IDX]; + converters["std::string"] = primitiveTypeConverters[SBK_STD_STRING_IDX]; + converters["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]; + converters["unsigned long"] = primitiveTypeConverters[SBK_UNSIGNEDLONG_IDX]; + converters["unsigned short"] = primitiveTypeConverters[SBK_UNSIGNEDSHORT_IDX]; + converters["void*"] = primitiveTypeConverters[SBK_VOIDPTR_IDX]; + converters["std::nullptr_t"] = primitiveTypeConverters[SBK_NULLPTR_T_IDX]; + + 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, + CppToPythonFunc pointerToPythonFunc, + CppToPythonFunc copyToPythonFunc) +{ + auto converter = new SbkConverter; + converter->pythonType = type; + // PYSIDE-595: All types are heaptypes now, so provide reference. + Py_XINCREF(type); + + converter->pointerToPython = pointerToPythonFunc; + converter->copyToPython = copyToPythonFunc; + + if (toCppPointerCheckFunc && toCppPointerConvFunc) + converter->toCppPointerConversion = std::make_pair(toCppPointerCheckFunc, toCppPointerConvFunc); + converter->toCppConversions.clear(); + + return converter; +} + +SbkConverter *createConverter(PyTypeObject *type, + PythonToCppFunc toCppPointerConvFunc, + IsConvertibleToCppFunc toCppPointerCheckFunc, + CppToPythonFunc pointerToPythonFunc, + CppToPythonFunc copyToPythonFunc) +{ + SbkConverter *converter = + createConverterObject(type, + toCppPointerConvFunc, toCppPointerCheckFunc, + pointerToPythonFunc, copyToPythonFunc); + PepType_SOTP(type)->converter = converter; + return converter; +} + +SbkConverter *createConverter(PyTypeObject *type, CppToPythonFunc toPythonFunc) +{ + return createConverterObject(type, nullptr, nullptr, nullptr, toPythonFunc); +} + +void deleteConverter(SbkConverter *converter) +{ + if (converter) { + converter->toCppConversions.clear(); + delete converter; + } +} + +void setCppPointerToPythonFunction(SbkConverter *converter, CppToPythonFunc pointerToPythonFunc) +{ + converter->pointerToPython = pointerToPythonFunc; +} + +void setPythonToCppPointerFunctions(SbkConverter *converter, + PythonToCppFunc toCppPointerConvFunc, + IsConvertibleToCppFunc toCppPointerCheckFunc) +{ + converter->toCppPointerConversion = std::make_pair(toCppPointerCheckFunc, toCppPointerConvFunc); +} + +void addPythonToCppValueConversion(SbkConverter *converter, + PythonToCppFunc pythonToCppFunc, + IsConvertibleToCppFunc isConvertibleToCppFunc) +{ + converter->toCppConversions.push_back(std::make_pair(isConvertibleToCppFunc, pythonToCppFunc)); +} + +void addPythonToCppValueConversion(PyTypeObject *type, + PythonToCppFunc pythonToCppFunc, + IsConvertibleToCppFunc isConvertibleToCppFunc) +{ + auto *sotp = PepType_SOTP(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); + return pointerToPython(sotp->converter, cppIn); +} + +PyObject *pointerToPython(const SbkConverter *converter, const void *cppIn) +{ + assert(converter); + if (!cppIn) + Py_RETURN_NONE; + if (!converter->pointerToPython) { + warning(PyExc_RuntimeWarning, 0, "pointerToPython(): SbkConverter::pointerToPython is null for \"%s\".", + converter->pythonType->tp_name); + Py_RETURN_NONE; + } + return converter->pointerToPython(cppIn); +} + +PyObject *referenceToPython(PyTypeObject *type, const void *cppIn) +{ + auto *sotp = PepType_SOTP(type); + return referenceToPython(sotp->converter, cppIn); +} + +PyObject *referenceToPython(const SbkConverter *converter, const void *cppIn) +{ + assert(cppIn); + + auto *pyOut = reinterpret_cast<PyObject *>(BindingManager::instance().retrieveWrapper(cppIn)); + if (pyOut) { + Py_INCREF(pyOut); + return pyOut; + } + if (!converter->pointerToPython) { + warning(PyExc_RuntimeWarning, 0, "referenceToPython(): SbkConverter::pointerToPython is null for \"%s\".", + converter->pythonType->tp_name); + Py_RETURN_NONE; + } + return converter->pointerToPython(cppIn); +} + +static inline PyObject *CopyCppToPython(const SbkConverter *converter, const void *cppIn) +{ + if (!cppIn) + Py_RETURN_NONE; + if (!converter->copyToPython) { + warning(PyExc_RuntimeWarning, 0, "CopyCppToPython(): SbkConverter::copyToPython is null for \"%s\".", + converter->pythonType->tp_name); + Py_RETURN_NONE; + } + return converter->copyToPython(cppIn); +} + +PyObject *copyToPython(PyTypeObject *type, const void *cppIn) +{ + auto *sotp = PepType_SOTP(type); + return CopyCppToPython(sotp->converter, cppIn); +} + +PyObject *copyToPython(const SbkConverter *converter, const void *cppIn) +{ + return CopyCppToPython(converter, cppIn); +} + +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); + for (const ToCppConversion &c : converter->toCppConversions) { + if (PythonToCppFunc toCppFunc = c.first(pyIn)) + return toCppFunc; + } + return nullptr; +} + +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) +{ + assert(pyIn); + for (IsArrayConvertibleToCppFunc f : converter->toCppConversions) { + if (PythonToCppFunc c = f(pyIn, dim1, dim2)) + return c; + } + return nullptr; +} + +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); + if (toCpp) + return toCpp; + } + 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); + *static_cast<void **>(cppOut) = nullptr; +} + +void *cppPointer(PyTypeObject *desiredType, SbkObject *pyIn) +{ + assert(pyIn); + if (!ObjectType::checkType(desiredType)) + return pyIn; + auto *inType = Py_TYPE(pyIn); + if (ObjectType::hasCast(inType)) + return ObjectType::cast(inType, pyIn, desiredType); + return Object::cppPointer(pyIn, desiredType); +} + +void pythonToCppPointer(PyTypeObject *type, PyObject *pyIn, void *cppOut) +{ + assert(type); + assert(pyIn); + assert(cppOut); + *reinterpret_cast<void **>(cppOut) = pyIn == Py_None + ? nullptr + : cppPointer(type, reinterpret_cast<SbkObject *>(pyIn)); +} + +void pythonToCppPointer(const SbkConverter *converter, PyObject *pyIn, void *cppOut) +{ + assert(converter); + assert(pyIn); + assert(cppOut); + *reinterpret_cast<void **>(cppOut) = pyIn == Py_None + ? nullptr + : cppPointer(converter->pythonType, reinterpret_cast<SbkObject *>(pyIn)); +} + +static void _pythonToCppCopy(const SbkConverter *converter, PyObject *pyIn, void *cppOut) +{ + assert(converter); + assert(pyIn); + assert(cppOut); + PythonToCppFunc toCpp = IsPythonToCppConvertible(converter, pyIn); + if (toCpp) + toCpp(pyIn, cppOut); +} + +void pythonToCppCopy(PyTypeObject *type, PyObject *pyIn, void *cppOut) +{ + assert(type); + auto *sotp = PepType_SOTP(type); + _pythonToCppCopy(sotp->converter, pyIn, cppOut); +} + +void pythonToCppCopy(const SbkConverter *converter, PyObject *pyIn, void *cppOut) +{ + _pythonToCppCopy(converter, pyIn, cppOut); +} + +bool isImplicitConversion(PyTypeObject *type, PythonToCppFunc toCppFunc) +{ + auto *sotp = PepType_SOTP(type); + // This is the Object Type or Value Type conversion that only + // retrieves the C++ pointer held in the Python wrapper. + if (toCppFunc == sotp->converter->toCppPointerConversion.second) + return false; + + // Object Types doesn't have any kind of value conversion, + // only C++ pointer retrieval. + if (sotp->converter->toCppConversions.empty()) + return false; + + // The first conversion of the non-pointer conversion list is + // a Value Type's copy to C++ function, which is not an implicit + // conversion. + // Otherwise it must be one of the implicit conversions. + // Note that we don't check if the Python to C++ conversion is in + // the list of the type's conversions, for it is expected that the + // caller knows what he's doing. + const auto conv = sotp->converter->toCppConversions.cbegin(); + return toCppFunc != (*conv).second; +} + +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 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) +{ + 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; + // 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); + } + 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); + assert(pyIn); + if (PySequence_Size(pyIn) < 0) { + // clear the error if < 0 which means no length at all + PyErr_Clear(); + return false; + } + const Py_ssize_t size = PySequence_Size(pyIn); + for (Py_ssize_t i = 0; i < size; ++i) { + if (!PyObject_TypeCheck(AutoDecRef(PySequence_GetItem(pyIn, i)), type)) + return false; + } + return true; +} + +bool 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); + assert(pyIn); + if (!PySequence_Check(pyIn)) + return false; + const Py_ssize_t size = PySequence_Size(pyIn); + for (Py_ssize_t i = 0; i < size; ++i) { + if (!isPythonToCppConvertible(converter, AutoDecRef(PySequence_GetItem(pyIn, i)))) + return false; + } + return true; +} +bool convertibleSequenceTypes(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); + assert(secondType); + assert(pyIn); + if (!PySequence_Check(pyIn)) + return false; + if (PySequence_Size(pyIn) != 2) + return false; + if (!PyObject_TypeCheck(AutoDecRef(PySequence_GetItem(pyIn, 0)), firstType)) + return false; + if (!PyObject_TypeCheck(AutoDecRef(PySequence_GetItem(pyIn, 1)), secondType)) + return false; + return true; +} +bool convertiblePairTypes(const SbkConverter *firstConverter, bool firstCheckExact, + const SbkConverter *secondConverter, bool secondCheckExact, + PyObject *pyIn) +{ + assert(firstConverter); + assert(secondConverter); + assert(pyIn); + if (!PySequence_Check(pyIn)) + return false; + if (PySequence_Size(pyIn) != 2) + return false; + AutoDecRef firstItem(PySequence_GetItem(pyIn, 0)); + if (firstCheckExact) { + if (!PyObject_TypeCheck(firstItem, firstConverter->pythonType)) + return false; + } else if (!isPythonToCppConvertible(firstConverter, firstItem)) { + return false; + } + AutoDecRef secondItem(PySequence_GetItem(pyIn, 1)); + if (secondCheckExact) { + if (!PyObject_TypeCheck(secondItem, secondConverter->pythonType)) + return false; + } else if (!isPythonToCppConvertible(secondConverter, secondItem)) { + return false; + } + + return true; +} + +bool checkDictTypes(PyTypeObject *keyType, PyTypeObject *valueType, PyObject *pyIn) +{ + assert(keyType); + assert(valueType); + assert(pyIn); + if (!PyDict_Check(pyIn)) + return false; + + PyObject *key; + PyObject *value; + Py_ssize_t pos = 0; + while (PyDict_Next(pyIn, &pos, &key, &value)) { + if (!PyObject_TypeCheck(key, keyType)) + return false; + if (!PyObject_TypeCheck(value, valueType)) + return false; + } + return true; +} + +bool checkMultiDictTypes(PyTypeObject *keyType, PyTypeObject *valueType, + PyObject *pyIn) +{ + assert(keyType); + assert(valueType); + assert(pyIn); + if (!PyDict_Check(pyIn)) + return false; + + PyObject *key; + PyObject *values; + Py_ssize_t pos = 0; + while (PyDict_Next(pyIn, &pos, &key, &values)) { + if (!PyObject_TypeCheck(key, keyType)) + return false; + if (!PySequence_Check(values)) + return false; + const Py_ssize_t size = PySequence_Size(values); + for (Py_ssize_t i = 0; i < size; ++i) { + AutoDecRef value(PySequence_GetItem(values, i)); + if (!PyObject_TypeCheck(value, valueType)) + return false; + } + } + return true; +} + +bool convertibleDictTypes(const SbkConverter *keyConverter, bool keyCheckExact, const SbkConverter *valueConverter, + bool valueCheckExact, PyObject *pyIn) +{ + assert(keyConverter); + assert(valueConverter); + assert(pyIn); + if (!PyDict_Check(pyIn)) + return false; + PyObject *key; + PyObject *value; + Py_ssize_t pos = 0; + while (PyDict_Next(pyIn, &pos, &key, &value)) { + if (keyCheckExact) { + if (!PyObject_TypeCheck(key, keyConverter->pythonType)) + return false; + } else if (!isPythonToCppConvertible(keyConverter, key)) { + return false; + } + if (valueCheckExact) { + if (!PyObject_TypeCheck(value, valueConverter->pythonType)) + return false; + } else if (!isPythonToCppConvertible(valueConverter, value)) { + return false; + } + } + return true; +} + +bool convertibleMultiDictTypes(const SbkConverter *keyConverter, bool keyCheckExact, + const SbkConverter *valueConverter, + bool valueCheckExact, PyObject *pyIn) +{ + assert(keyConverter); + assert(valueConverter); + assert(pyIn); + if (!PyDict_Check(pyIn)) + return false; + PyObject *key; + PyObject *values; + Py_ssize_t pos = 0; + while (PyDict_Next(pyIn, &pos, &key, &values)) { + if (keyCheckExact) { + if (!PyObject_TypeCheck(key, keyConverter->pythonType)) + return false; + } else if (!isPythonToCppConvertible(keyConverter, key)) { + return false; + } + if (!PySequence_Check(values)) + return false; + const Py_ssize_t size = PySequence_Size(values); + for (Py_ssize_t i = 0; i < size; ++i) { + AutoDecRef value(PySequence_GetItem(values, i)); + if (valueCheckExact) { + if (!PyObject_TypeCheck(value.object(), valueConverter->pythonType)) + return false; + } else if (!isPythonToCppConvertible(valueConverter, value.object())) { + return false; + } + } + } + return true; +} + +PyTypeObject *getPythonTypeObject(const SbkConverter *converter) +{ + if (converter) + return converter->pythonType; + return nullptr; +} + +PyTypeObject *getPythonTypeObject(const char *typeName) +{ + return getPythonTypeObject(getConverter(typeName)); +} + +bool pythonTypeIsValueType(const SbkConverter *converter) +{ + // Unlikely to happen but for multi-inheritance SbkObjs + // the converter is not defined, hence we need a default return. + if (!converter) + return false; + return converter->pointerToPython && converter->copyToPython; +} + +bool pythonTypeIsObjectType(const SbkConverter *converter) +{ + return converter->pointerToPython && !converter->copyToPython; +} + +bool pythonTypeIsWrapperType(const SbkConverter *converter) +{ + return converter->pointerToPython != nullptr; +} + +SpecificConverter::SpecificConverter(const char *typeName) + : m_type(InvalidConversion) +{ + m_converter = getConverter(typeName); + if (!m_converter) + return; + const Py_ssize_t len = strlen(typeName); + char lastChar = typeName[len -1]; + if (lastChar == '&') { + m_type = ReferenceConversion; + } else if (lastChar == '*' || pythonTypeIsObjectType(m_converter)) { + m_type = PointerConversion; + } else { + m_type = CopyConversion; + } +} + +PyObject *SpecificConverter::toPython(const void *cppIn) +{ + switch (m_type) { + case CopyConversion: + return copyToPython(m_converter, cppIn); + case PointerConversion: + return pointerToPython(m_converter, *static_cast<const void * const *>(cppIn)); + case ReferenceConversion: + return referenceToPython(m_converter, cppIn); + default: + PyErr_SetString(PyExc_RuntimeError, "tried to use invalid converter in 'C++ to Python' conversion"); + } + return nullptr; +} + +void SpecificConverter::toCpp(PyObject *pyIn, void *cppOut) +{ + switch (m_type) { + case CopyConversion: + pythonToCppCopy(m_converter, pyIn, cppOut); + break; + case PointerConversion: + pythonToCppPointer(m_converter, pyIn, cppOut); + break; + case ReferenceConversion: + pythonToCppPointer(m_converter, pyIn, &cppOut); + break; + default: + PyErr_SetString(PyExc_RuntimeError, "tried to use invalid converter in 'Python to C++' conversion"); + } +} + +} // namespace Shiboken::Conversions |