diff options
Diffstat (limited to 'sources/shiboken6')
51 files changed, 1075 insertions, 592 deletions
diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp index 89d636964..9831f911b 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp @@ -461,7 +461,7 @@ FileModelItem AbstractMetaBuilderPrivate::buildDom(QByteArrayList arguments, + clang::languageLevelOption(level)); } FileModelItem result = clang::parse(arguments, addCompilerSupportArguments, - clangFlags, builder) + level, clangFlags, builder) ? builder.dom() : FileModelItem(); const clang::BaseVisitor::Diagnostics &diagnostics = builder.diagnostics(); if (const auto diagnosticsCount = diagnostics.size()) { @@ -1574,7 +1574,8 @@ bool AbstractMetaBuilderPrivate::setupInheritance(const AbstractMetaClassPtr &me &info, &baseContainerType); if (templ) { setupInheritance(templ); - inheritTemplate(metaClass, templ, info); + if (!inheritTemplate(metaClass, templ, info)) + return false; metaClass->typeEntry()->setBaseContainerType(templ->typeEntry()); return true; } @@ -3143,54 +3144,81 @@ AbstractMetaClassPtr return result; } + +static std::optional<AbstractMetaType> + inheritTemplateParameter(const AbstractMetaClassPtr &subclass, + const AbstractMetaClassCPtr &templateClass, + const TypeInfo &info, QString *errorMessage) +{ + QString typeName = info.qualifiedName().join("::"_L1); + TypeDatabase *typeDb = TypeDatabase::instance(); + TypeEntryPtr t; + // Check for a non-type template integer parameter, that is, for a base + // "template <int R, int C> Matrix<R, C>" and subclass + // "typedef Matrix<2,3> Matrix2x3;". If so, create dummy entries of + // EnumValueTypeEntry for the integer values encountered on the fly. + if (isNumber(typeName)) { + t = typeDb->findType(typeName); + if (!t) { + auto parent = typeSystemTypeEntry(subclass->typeEntry()); + t = TypeDatabase::instance()->addConstantValueTypeEntry(typeName, parent); + } + } else { + QStringList possibleNames; + possibleNames << subclass->qualifiedCppName() + "::"_L1 + typeName; + possibleNames << templateClass->qualifiedCppName() + "::"_L1 + typeName; + if (subclass->enclosingClass()) + possibleNames << subclass->enclosingClass()->qualifiedCppName() + "::"_L1 + typeName; + possibleNames << typeName; + + for (const QString &possibleName : std::as_const(possibleNames)) { + t = typeDb->findType(possibleName); + if (t) + break; + } + } + + if (!t) { + *errorMessage = msgIgnoringTemplateParameter(typeName, + "The corresponding type was not found in the typesystem."); + return std::nullopt; + } + + if (t->isContainer()) { + *errorMessage = msgIgnoringTemplateParameter(typeName, + "Template inheritance from nested containers is not supported"); + return std::nullopt; + } + AbstractMetaType result(t); + result.setConstant(info.isConstant()); + result.setReferenceType(info.referenceType()); + result.setIndirectionsV(info.indirectionsV()); + result.decideUsagePattern(); + return result; +} + bool AbstractMetaBuilderPrivate::inheritTemplate(const AbstractMetaClassPtr &subclass, const AbstractMetaClassCPtr &templateClass, const TypeInfo &info) { AbstractMetaTypeList templateTypes; + QString errorMessage; for (const TypeInfo &i : info.instantiations()) { - QString typeName = i.qualifiedName().join(u"::"_s); - TypeDatabase *typeDb = TypeDatabase::instance(); - TypeEntryPtr t; - // Check for a non-type template integer parameter, that is, for a base - // "template <int R, int C> Matrix<R, C>" and subclass - // "typedef Matrix<2,3> Matrix2x3;". If so, create dummy entries of - // EnumValueTypeEntry for the integer values encountered on the fly. - if (isNumber(typeName)) { - t = typeDb->findType(typeName); - if (!t) { - auto parent = typeSystemTypeEntry(subclass->typeEntry()); - t = TypeDatabase::instance()->addConstantValueTypeEntry(typeName, parent); - } - } else { - QStringList possibleNames; - possibleNames << subclass->qualifiedCppName() + u"::"_s + typeName; - possibleNames << templateClass->qualifiedCppName() + u"::"_s + typeName; - if (subclass->enclosingClass()) - possibleNames << subclass->enclosingClass()->qualifiedCppName() + u"::"_s + typeName; - possibleNames << typeName; - - for (const QString &possibleName : std::as_const(possibleNames)) { - t = typeDb->findType(possibleName); - if (t) - break; - } - } - - if (t) { - AbstractMetaType temporaryType(t); - temporaryType.setConstant(i.isConstant()); - temporaryType.setReferenceType(i.referenceType()); - temporaryType.setIndirectionsV(i.indirectionsV()); - temporaryType.decideUsagePattern(); - templateTypes << temporaryType; + const auto typeO = inheritTemplateParameter(subclass, templateClass, i, &errorMessage); + if (typeO.has_value()) { + templateTypes.append(typeO.value()); } else { - qCWarning(lcShiboken).noquote().nospace() - << "Ignoring template parameter " << typeName << " from " - << info.toString() << ". The corresponding type was not found in the typesystem."; + errorMessage = msgInheritTemplateIssue(subclass, info, errorMessage); + qCWarning(lcShiboken, "%s", qPrintable(errorMessage)); } } + if (templateTypes.isEmpty()) { + errorMessage = msgInheritTemplateIssue(subclass, info, + "No template parameters could be inherited"_L1); + qCWarning(lcShiboken, "%s", qPrintable(errorMessage)); + return false; + } return inheritTemplate(subclass, templateClass, templateTypes); } diff --git a/sources/shiboken6/ApiExtractor/abstractmetatype.cpp b/sources/shiboken6/ApiExtractor/abstractmetatype.cpp index dcfc74bbb..3ec07509d 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetatype.cpp +++ b/sources/shiboken6/ApiExtractor/abstractmetatype.cpp @@ -543,6 +543,7 @@ void AbstractMetaType::decideUsagePattern() pattern = ObjectPattern; } setTypeUsagePattern(pattern); + Q_ASSERT(pattern != ContainerPattern || !d->m_instantiations.isEmpty()); } bool AbstractMetaTypeData::hasTemplateChildren() const diff --git a/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp b/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp index da6930476..6c0cf3ae2 100644 --- a/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp +++ b/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp @@ -235,6 +235,7 @@ static QByteArray msgCreateTranslationUnit(const QByteArrayList &clangArgs, unsi static CXTranslationUnit createTranslationUnit(CXIndex index, const QByteArrayList &args, bool addCompilerSupportArguments, + LanguageLevel level, unsigned flags = 0) { // courtesy qdoc @@ -255,7 +256,7 @@ static CXTranslationUnit createTranslationUnit(CXIndex index, QByteArrayList clangArgs; if (addCompilerSupportArguments) { - clangArgs += emulatedCompilerOptions(); + clangArgs += emulatedCompilerOptions(level); clangArgs += defaultArgs; } clangArgs += detectVulkan(); @@ -280,7 +281,7 @@ static CXTranslationUnit createTranslationUnit(CXIndex index, */ bool parse(const QByteArrayList &clangArgs, bool addCompilerSupportArguments, - unsigned clangFlags, BaseVisitor &bv) + LanguageLevel level, unsigned clangFlags, BaseVisitor &bv) { CXIndex index = clang_createIndex(0 /* excludeDeclarationsFromPCH */, 1 /* displayDiagnostics */); @@ -291,7 +292,7 @@ bool parse(const QByteArrayList &clangArgs, bool addCompilerSupportArguments, CXTranslationUnit translationUnit = createTranslationUnit(index, clangArgs, addCompilerSupportArguments, - clangFlags); + level, clangFlags); if (!translationUnit) return false; diff --git a/sources/shiboken6/ApiExtractor/clangparser/clangparser.h b/sources/shiboken6/ApiExtractor/clangparser/clangparser.h index 4a46248e4..22e0a50cd 100644 --- a/sources/shiboken6/ApiExtractor/clangparser/clangparser.h +++ b/sources/shiboken6/ApiExtractor/clangparser/clangparser.h @@ -14,6 +14,8 @@ #include <string_view> #include <utility> +enum class LanguageLevel; + namespace clang { struct Diagnostic; @@ -79,7 +81,7 @@ private: bool parse(const QByteArrayList &clangArgs, bool addCompilerSupportArguments, - unsigned clangFlags, BaseVisitor &ctx); + LanguageLevel level, unsigned clangFlags, BaseVisitor &ctx); } // namespace clang diff --git a/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp index 4c13b141f..2460ca9d9 100644 --- a/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp +++ b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp @@ -365,14 +365,15 @@ static void appendClangBuiltinIncludes(HeaderPaths *p) } // Returns clang options needed for emulating the host compiler -QByteArrayList emulatedCompilerOptions() +QByteArrayList emulatedCompilerOptions(LanguageLevel level) { QByteArrayList result; HeaderPaths headerPaths; switch (compiler()) { case Compiler::Msvc: result.append("-fms-compatibility-version="_ba + msvcCompatVersion()); - result.append(QByteArrayLiteral("-fdelayed-template-parsing")); + if (level < LanguageLevel::Cpp20) + result.append("-fdelayed-template-parsing"_ba); result.append(QByteArrayLiteral("-Wno-microsoft-enum-value")); result.append("/Zc:__cplusplus"_ba); // Fix yvals_core.h: STL1000: Unexpected compiler version, expected Clang 7 or newer (MSVC2017 update) diff --git a/sources/shiboken6/ApiExtractor/clangparser/compilersupport.h b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.h index 462e8f205..f1d63b7c3 100644 --- a/sources/shiboken6/ApiExtractor/clangparser/compilersupport.h +++ b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.h @@ -33,7 +33,7 @@ enum class Platform { namespace clang { QVersionNumber libClangVersion(); -QByteArrayList emulatedCompilerOptions(); +QByteArrayList emulatedCompilerOptions(LanguageLevel level); LanguageLevel emulatedCompilerLanguageLevel(); const char *languageLevelOption(LanguageLevel l); diff --git a/sources/shiboken6/ApiExtractor/messages.cpp b/sources/shiboken6/ApiExtractor/messages.cpp index f9f46f520..b1f0b240e 100644 --- a/sources/shiboken6/ApiExtractor/messages.cpp +++ b/sources/shiboken6/ApiExtractor/messages.cpp @@ -481,6 +481,21 @@ QString msgCannotFindTypeEntryForSmartPointer(const QString &t, const QString &s + u"\" for instantiation of \""_s +smartPointerType + u"\"."_s; } +QString msgInheritTemplateIssue(const AbstractMetaClassPtr &subclass, + const TypeInfo &info, + const QString &what) +{ + return "While inheriting template "_L1 + subclass->name() + + " from "_L1 + info.toString() + ": "_L1 + what; +} + +QString msgIgnoringTemplateParameter(const QString &typeName, + const char *why) +{ + return "Ignoring template parameter "_L1 + typeName + + ": "_L1 + QLatin1StringView(why); +} + QString msgInvalidSmartPointerType(const TypeInfo &i) { return u"Invalid smart pointer type \""_s +i.toString() + u"\"."_s; diff --git a/sources/shiboken6/ApiExtractor/messages.h b/sources/shiboken6/ApiExtractor/messages.h index 2899cbdfa..e3f582b49 100644 --- a/sources/shiboken6/ApiExtractor/messages.h +++ b/sources/shiboken6/ApiExtractor/messages.h @@ -126,6 +126,10 @@ QString msgUnableToTranslateType(const TypeInfo &typeInfo, QString msgCannotFindTypeEntry(const QString &t); QString msgCannotFindTypeEntryForSmartPointer(const QString &t, const QString &smartPointerType); +QString msgInheritTemplateIssue(const AbstractMetaClassPtr &subclass, + const TypeInfo &info, const QString &what); +QString msgIgnoringTemplateParameter(const QString &typeName, + const char *why); QString msgInvalidSmartPointerType(const TypeInfo &i); QString msgCannotFindSmartPointerInstantion(const TypeInfo &i); diff --git a/sources/shiboken6/ApiExtractor/typedatabase.cpp b/sources/shiboken6/ApiExtractor/typedatabase.cpp index 749c4baa3..61fd22418 100644 --- a/sources/shiboken6/ApiExtractor/typedatabase.cpp +++ b/sources/shiboken6/ApiExtractor/typedatabase.cpp @@ -88,7 +88,8 @@ static const PythonTypes &builtinPythonTypes() {u"PyObject"_s, u"true"_s, TypeSystem::CPythonType::Other}, // shiboken-specific {u"PyPathLike"_s, u"Shiboken::String::checkPath"_s, TypeSystem::CPythonType::Other}, - {u"PySequence"_s, u"Shiboken::String::checkIterable"_s, TypeSystem::CPythonType::Other}, + {u"PySequence"_s, u"Shiboken::String::checkIterableArgument"_s, + TypeSystem::CPythonType::Other}, {u"PyUnicode"_s, u"PyUnicode_Check"_s, TypeSystem::CPythonType::String}, {u"PyTypeObject"_s, u"PyType_Check"_s, TypeSystem::CPythonType::Other}, {u"str"_s, u"Shiboken::String::check"_s, TypeSystem::CPythonType::String}, diff --git a/sources/shiboken6/doc/shibokenmodule.rst b/sources/shiboken6/doc/shibokenmodule.rst index 2f1c6d166..3bc4fa6ba 100644 --- a/sources/shiboken6/doc/shibokenmodule.rst +++ b/sources/shiboken6/doc/shibokenmodule.rst @@ -125,6 +125,11 @@ To import the module: Dumps the map of wrappers existing in libshiboken to standard error. +.. function:: dumpConverters() + + Dumps the map of named converters existing in libshiboken to standard + error. + .. py:class:: VoidPtr(address, size = -1, writeable = 0) :param address: (PyBuffer, SbkObject, int, VoidPtr) diff --git a/sources/shiboken6/doc/typediscovery.rst b/sources/shiboken6/doc/typediscovery.rst new file mode 100644 index 000000000..76d3adf7b --- /dev/null +++ b/sources/shiboken6/doc/typediscovery.rst @@ -0,0 +1,145 @@ +.. _typediscovery: + +************** +Type Discovery +************** + +When converting objects which are part of a class hierarchy from a pointer to a +base class, it is expected to get the Python type of the actual, most derived +type, as opposed to C++ which requires a cast for this: + +.. code-block:: python + + def event(self, event): + if event.type() == QEvent.Type.MousePress: + self.do_things(event.position()) + ... + + +.. code-block:: c++ + + bool event(QEvent *event) override + { + if (event->type() == QEvent::MousePress) { + auto *mouseEvent = static_cast<QMouseEvent *>(event); + doThings(mouseEvent->position()); + ... + } + +The process of determining the type of the event is called `type discovery`. + +Shiboken generates code to automatically detect the type. First, it tries to +find a converter for the name obtained by ``typeid(*pointer).name()``. This +should normally work as this name is registered by the binding. If that fails, +it starts walking a type inheritance graph built up in libshiboken to find the +most derived class by using a cast function (``dynamic_cast<>`` by default) to +check. + +For normal class hierarchies with virtual destructors, no special handling +is required since ``typeid()`` usually detects the proper class name. + +Multiple inheritance +==================== + +In case of multiple inheritance in C++, the conversion to the derived class is +not done in case it is not a single-line direct inheritance. For example, in +Qt, the class ``QWidget`` inherits both ``QObject`` (base of the ``QObject`` +hierarchy) and ``QPaintDevice``. + +When calling a function returning a ``QPaintDevice *``, for example +``QPainter.device()``, a Python type representing ``QPaintDevice`` is returned +instead of the underlying widget type. This restriction exists because the +underlying pointer in C++ is a pointer to a ``QPaintDevice *`` and differs from +the pointer to the ``QWidget``. + +Hierarchies of classes with non-virtual destructors +=================================================== + +There are some hierarchies of value-ish C++ classes that do not have virtual +destructors. This makes type discovery based on ``typeid()`` and +``dynamic_cast<>`` impossible. + +Examples in Qt are the ``QStyleOption``-derived or the ``QGradient`` +-derived classes. + +For such classes, some attributes need to be specified on the type entries: + +Primarily, a :ref:`polymorphic-id-expression` attribute +must be specified to be used as a check replacing ``dynamic_cast<>``. + +In addition, a :ref:`polymorphic-name-function` attribute can be specified. +This replaces the type name guess obtained by ``typeid()`` and is mainly a hint +to speed things up by skipping the checks for each type in the inheritance +graph. + +A :ref:`polymorphic-base` attribute identifies the base class of a hierarchy. +It should be given in case the base class inherits from another class to +prevent the logic from going below the base class. + +Using type discovery attributes for class hierarchies with virtual destructors +============================================================================== + +It is possible to use :ref:`polymorphic-id-expression` and +:ref:`polymorphic-name-function` for normal class hierarchies with virtual +destructors as well since they basically replace ``typeid()`` and +``dynamic_cast<>``. This makes sense if expressions can be specified that are +faster than the checks on virtual tables. + +Specifying :ref:`polymorphic-base` can also make sense for generating special +cast functions in case of multiple inheritance. For example, in Qt, +``QWindow``, ``QLayout``, ``QWidget`` are base classes of hierarchies. Since +they all inherit from ``QObject``, indicating the base classes prevents +the logic from using ``QObject`` as a base class. + +.. _typediscovery-attributes: + +Type discovery attributes reference +=================================== + +The following attributes related to type discovery may be be specified on the +:ref:`object-type` or :ref:`value-type` elements: + +.. _polymorphic-id-expression: + +polymorphic-id-expression ++++++++++++++++++++++++++ + +The **polymorphic-id-expression** attribute specifies an expression checking +whether a base class pointer is of the matching type. For example, in a +``virtual eventHandler(BaseEvent *e)`` function, this is used to construct a +Python wrapper matching the derived class (for example, a ``MouseEvent`` or +similar). The attribute value may contain placeholders: + +%1 + Fully qualified class name + +%B + Fully qualified name of the base class (found by base class + search or as indicated by **polymorphic-base**). + +To check for a class inheriting ``BaseEvent``, specify: + +.. code-block:: xml + + <object-type name="MouseEvent" + polymorphic-id-expression="%B->type() == BaseEvent::MouseEvent"/> + +.. _polymorphic-name-function: + +polymorphic-name-function ++++++++++++++++++++++++++ + +The **polymorphic-name-function** attribute specifies the name of a function +returning the type name of a derived class on the base class type entry. +Normally, ``typeid(ptr).name()`` is used for this. + +The function is expected to return ``const char *``. + +.. _polymorphic-base: + +polymorphic-base +++++++++++++++++ + +The boolean **polymorphic-base** attribute indicates whether the class is the +base class of a class hierarchy. It is used for the *%B* placeholder in +**polymorphic-id-expression** and for cast operations in multiple inheritance. diff --git a/sources/shiboken6/doc/typesystem.rst b/sources/shiboken6/doc/typesystem.rst index e1e4fdda2..26f929801 100644 --- a/sources/shiboken6/doc/typesystem.rst +++ b/sources/shiboken6/doc/typesystem.rst @@ -65,3 +65,4 @@ Extra options and Python caveats typesystem_solving_compilation.rst typesystem_specialfunctions.rst + typediscovery.rst diff --git a/sources/shiboken6/doc/typesystem_converters.rst b/sources/shiboken6/doc/typesystem_converters.rst index 7bdabc49c..ab6fba930 100644 --- a/sources/shiboken6/doc/typesystem_converters.rst +++ b/sources/shiboken6/doc/typesystem_converters.rst @@ -233,61 +233,3 @@ Variables & Functions **%CHECKTYPE[CPPTYPE]** Replaced by a |project| type checking function for a Python variable. The C++ type is indicated by ``CPPTYPE``. - - -.. _oldconverters: - -Converting The Old Converters -============================= - -If you use |project| for your bindings, and has defined some type conversions -using the ``Shiboken::Converter`` template, then you must update your converters -to the new scheme. - -Previously your conversion rules were declared in one line, like this: - - -.. code-block:: xml - - <primitive-type name="Complex" target-lang-api-name="PyComplex"> - <include file-name="complex.h" location="global"/> - <conversion-rule file="complex_conversions.h"/> - </primitive-type> - - -And implemented in a separate C++ file, like this: - - -.. code-block:: c++ - - namespace Shiboken { - template<> struct Converter<Complex> - { - static inline bool checkType(PyObject* pyObj) { - return PyComplex_Check(pyObj); - } - static inline bool isConvertible(PyObject* pyObj) { - return PyComplex_Check(pyObj); - } - static inline PyObject* toPython(void* cppobj) { - return toPython(*reinterpret_cast<Complex*>(cppobj)); - } - static inline PyObject* toPython(const Complex& cpx) { - return PyComplex_FromDoubles(cpx.real(), cpx.imag()); - } - static inline Complex toCpp(PyObject* pyobj) { - double real = PyComplex_RealAsDouble(pyobj); - double imag = PyComplex_ImagAsDouble(pyobj); - return Complex(real, imag); - } - }; - } - - -In this case, the parts of the implementation that will be used in the new -conversion-rule are the ones in the two last method -``static inline PyObject* toPython(const Complex& cpx)`` and -``static inline Complex toCpp(PyObject* pyobj)``. The ``isConvertible`` method -is gone, and the ``checkType`` is now an attribute of the :ref:`add-conversion <add-conversion>` -tag. Refer back to the first example in this page and you will be able to -correlate the above template with the new scheme of conversion rule definition. diff --git a/sources/shiboken6/doc/typesystem_specifying_types.rst b/sources/shiboken6/doc/typesystem_specifying_types.rst index 66e68ae2b..f65b79bb4 100644 --- a/sources/shiboken6/doc/typesystem_specifying_types.rst +++ b/sources/shiboken6/doc/typesystem_specifying_types.rst @@ -536,37 +536,8 @@ type system has this attribute set, the heuristics will be applied to all classes. In shiboken 7, it will be mandatory to set the attribute. -The *optional* **polymorphic-id-expression** attribute specifies an -expression checking whether a base class pointer is of the matching -type. For example, in a ``virtual eventHandler(BaseEvent *e)`` -function, this is used to construct a Python wrapper matching -the derived class (for example, a ``MouseEvent`` or similar). -The attribute value may contain placeholders: - -%1 - Fully qualified class name - -%B - Fully qualified name of the base class (found by base class - search or as indicated by **polymorphic-base**). - -To check for a class inheriting ``BaseEvent``, specify: - -.. code-block:: xml - - <object-type name="MouseEvent" - polymorphic-id-expression="%B->type() == BaseEvent::MouseEvent"/> - -The *optional* **polymorphic-name-function** specifies the name of a -function returning the type name of a derived class on the base class -type entry. Normally, ``typeid(ptr).name()`` is used for this. -However, this fails if the type hierarchy does not have virtual functions. -In this case, a function is required which typically decides depending -on some type enumeration. - -The *optional* **polymorphic-base** attribute indicates -whether the class is the base class of a class hierarchy -(used for the *%B* placeholder in **polymorphic-id-expression**). +For the *optional* **polymorphic-id-expression**, **polymorphic-name-function** +and **polymorphic-base** attributes, see :ref:`typediscovery-attributes`. interface-type ^^^^^^^^^^^^^^ diff --git a/sources/shiboken6/generator/generator.cpp b/sources/shiboken6/generator/generator.cpp index b224858c5..a01326530 100644 --- a/sources/shiboken6/generator/generator.cpp +++ b/sources/shiboken6/generator/generator.cpp @@ -231,10 +231,9 @@ QString Generator::getFileNameBaseForSmartPointer(const AbstractMetaType &smartP const AbstractMetaType innerType = smartPointerType.getSmartPointerInnerType(); smartPointerType.typeEntry()->qualifiedCppName(); QString fileName = smartPointerType.typeEntry()->qualifiedCppName().toLower(); - fileName.replace(u"::"_s, u"_"_s); - fileName.append(u"_"_s); + fileName.append(u'_'); fileName.append(innerType.name().toLower()); - + fileName.replace(u"::"_s, u"_"_s); // std::shared_ptr<std::string> return fileName; } diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp index ad4071c35..0eeeec6f9 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp +++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp @@ -61,6 +61,7 @@ static const char shibokenErrorsOccurred[] = "Shiboken::Errors::occurred() != nu static constexpr auto virtualMethodStaticReturnVar = "result"_L1; static constexpr auto sbkObjectTypeF = "SbkObject_TypeF()"_L1; +static const char initInheritanceFunction[] = "initInheritance"; static QString mangleName(QString name) { @@ -1336,7 +1337,8 @@ void CppGenerator::writeVirtualMethodPythonOverride(TextStream &s, s << "if (" << PYTHON_RETURN_VAR << ".isNull()) {\n" << indent << "// An error happened in python code!\n" - << "Shiboken::Errors::storeErrorOrPrint();\n" + << "Shiboken::Errors::storePythonOverrideErrorOrPrint(\"" + << func->ownerClass()->name() << "\", funcName);\n" << returnStatement.statement << "\n" << outdent << "}\n"; @@ -1589,6 +1591,33 @@ void CppGenerator::writeEnumConverterFunctions(TextStream &s, const AbstractMeta s << '\n'; } +static void writePointerToPythonConverter(TextStream &c, + const AbstractMetaClassCPtr &metaClass, + const QString &typeName, + const QString &cpythonType) +{ + c << "auto *pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));\n" + << "if (pyOut) {\n" << indent + << "Py_INCREF(pyOut);\nreturn pyOut;\n" << outdent + << "}\n"; + + const QString nameFunc = metaClass->typeEntry()->polymorphicNameFunction(); + if (nameFunc.isEmpty() && !metaClass->hasVirtualDestructor()) { + c << "return Shiboken::Object::newObjectWithHeuristics(" + << cpythonType << ", const_cast<void *>(cppIn), false);\n"; + return; + } + + c << "auto *tCppIn = reinterpret_cast<const " << typeName << R"( *>(cppIn); +const char *typeName = )"; + if (nameFunc.isEmpty()) + c << "typeid(*tCppIn).name();\n"; + else + c << nameFunc << "(tCppIn);\n"; + c << "return Shiboken::Object::newObjectForPointer(" + << cpythonType << ", const_cast<void *>(cppIn), false, typeName);\n"; +} + void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext) const { @@ -1634,30 +1663,7 @@ void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClas c << "return PySide::getWrapperForQObject(reinterpret_cast<" << typeName << " *>(const_cast<void *>(cppIn)), " << cpythonType << ");\n"; } else { - c << "auto *pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));\n" - << "if (pyOut) {\n" << indent - << "Py_INCREF(pyOut);\nreturn pyOut;\n" << outdent - << "}\n" - << "bool changedTypeName = false;\n" - << "auto *tCppIn = reinterpret_cast<const " << typeName << R"( *>(cppIn); -const char *typeName = )"; - - const QString nameFunc = metaClass->typeEntry()->polymorphicNameFunction(); - if (nameFunc.isEmpty()) - c << "typeid(*tCppIn).name();\n"; - else - c << nameFunc << "(tCppIn);\n"; - c << R"(auto *sbkType = Shiboken::ObjectType::typeForTypeName(typeName); -if (sbkType != nullptr && Shiboken::ObjectType::hasSpecialCastFunction(sbkType)) { - typeName = Shiboken::typeNameOf(typeid(*tCppIn).name()); - changedTypeName = true; -} -)" - << "PyObject *result = Shiboken::Object::newObject(" << cpythonType - << R"(, const_cast<void *>(cppIn), false, /* exactType */ changedTypeName, typeName); -if (changedTypeName) - delete [] typeName; -return result;)"; + writePointerToPythonConverter(c, metaClass, typeName, cpythonType); } std::swap(targetTypeName, sourceTypeName); writeCppToPythonFunction(s, c.toString(), sourceTypeName, targetTypeName); @@ -4260,6 +4266,12 @@ QString CppGenerator::writeContainerConverterInitialization(TextStream &s, return converter; } +QString CppGenerator::typeInitStruct(const TypeEntryCPtr &te) +{ + return cppApiVariableName(te->targetLangPackage()) + u'[' + + getTypeIndexVariableName(te) + u']'; +} + void CppGenerator::writeExtendedConverterInitialization(TextStream &s, const TypeEntryCPtr &externalType, const AbstractMetaClassCList &conversions) @@ -4267,15 +4279,13 @@ void CppGenerator::writeExtendedConverterInitialization(TextStream &s, s << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName() << ".\n"; for (const auto &sourceClass : conversions) { - const QString converterVar = cppApiVariableName(externalType->targetLangPackage()) + u'[' - + getTypeIndexVariableName(externalType) + u']'; QString sourceTypeName = fixedCppTypeName(sourceClass->typeEntry()); QString targetTypeName = fixedCppTypeName(externalType); QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName); if (!externalType->isPrimitive()) s << cpythonTypeNameExt(externalType) << ";\n"; - writeAddPythonToCppConversion(s, converterVar, toCpp, isConv); + writeAddPythonToCppConversion(s, typeInitStruct(externalType), toCpp, isConv); } } @@ -5113,10 +5123,15 @@ QString CppGenerator::signatureParameter(const AbstractMetaArgument &arg) const const AbstractMetaFunctionCList conversions = api().implicitConversions(metaType); for (const auto &f : conversions) { - if (f->isConstructor() && !f->arguments().isEmpty()) - signatures << f->arguments().constFirst().type().pythonSignature(); - else if (f->isConversionOperator()) + if (f->isConstructor() && !f->arguments().isEmpty()) { + // PYSIDE-2712: modified types from converting constructors are not always correct + // candidates if they are modified by the type system reference + if (!f->arguments().constFirst().isTypeModified()) { + signatures << f->arguments().constFirst().type().pythonSignature(); + } + } else if (f->isConversionOperator()) { signatures << f->ownerClass()->fullName(); + } } const qsizetype size = signatures.size(); @@ -5473,6 +5488,27 @@ QStringList CppGenerator::pyBaseTypes(const AbstractMetaClassCPtr &metaClass) return result; } +void CppGenerator::writeInitInheritance(TextStream &s) const +{ + s << "static void " << initInheritanceFunction << "()\n{\n" << indent + << "auto &bm = Shiboken::BindingManager::instance();\n" + << sbkUnusedVariableCast("bm"); + for (const auto &cls : api().classes()){ + auto te = cls->typeEntry(); + if (shouldGenerate(te)) { + const auto &baseEntries = pyBaseTypeEntries(cls); + if (!baseEntries.isEmpty()) { + const QString childTypeInitStruct = typeInitStruct(cls->typeEntry()); + for (const auto &baseEntry : baseEntries) { + s << "bm.addClassInheritance(&" << typeInitStruct(baseEntry) << ",\n" + << Pad(' ', 23) << '&' << childTypeInitStruct << ");\n"; + } + } + } + } + s << outdent << "}\n\n"; +} + void CppGenerator::writeClassRegister(TextStream &s, const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext, @@ -5804,7 +5840,7 @@ void CppGenerator::writeTypeDiscoveryFunction(TextStream &s, } else if (metaClass->isPolymorphic()) { const auto &ancestors = metaClass->allTypeSystemAncestors(); for (const auto &ancestor : ancestors) { - if (ancestor->baseClass()) + if (ancestor->baseClass() && !ancestor->typeEntry()->isPolymorphicBase()) continue; if (ancestor->isPolymorphic()) { s << "if (instanceType == Shiboken::SbkType< " << m_gsp @@ -6024,18 +6060,14 @@ void CppGenerator::writeInitFunc(TextStream &declStr, TextStream &callStr, } else if (hasParent) { const QString &enclosingName = enclosingEntry->name(); const auto parts = QStringView{enclosingName}.split(u"::", Qt::SkipEmptyParts); + const QString namePathPrefix = enclosingEntry->name().replace("::"_L1, "."_L1); callStr << "Shiboken::Module::AddTypeCreationFunction(" - << "module, \"" << pythonName << "\", " << functionName << ", \""; - for (qsizetype i = 0; i < parts.size(); ++i) { - if (i > 0) - callStr << "\", \""; - callStr << parts.at(i); - } - callStr << "\");\n"; + << "module, \"" << parts[0] << "\", " + << functionName << ", \"" << namePathPrefix << '.' << pythonName << "\");\n"; } else { callStr << "Shiboken::Module::AddTypeCreationFunction(" << "module, \"" << pythonName << "\", " - << "init_" << initFunctionName << ");\n"; + << functionName << ");\n"; } } @@ -6337,6 +6369,8 @@ bool CppGenerator::finishGeneration() // PYSIDE-510: Create a signatures string for the introspection feature. writeSignatureStrings(s, signatureStream.toString(), moduleName(), "global functions"); + writeInitInheritance(s); + // Write module init function const QString globalModuleVar = pythonModuleObjectName(); s << "extern \"C\" LIBSHIBOKEN_EXPORT PyObject *PyInit_" @@ -6379,9 +6413,14 @@ bool CppGenerator::finishGeneration() s << "{nullptr, nullptr}\n" << outdent << "};\n" << "// The new global structure consisting of (type, name) pairs.\n" - << cppApiVariableName() << " = cppApi;\n" - << "// The backward compatible alias with upper case indexes.\n" - << cppApiVariableNameOld() << " = reinterpret_cast<PyTypeObject **>(cppApi);\n\n"; + << cppApiVariableName() << " = cppApi;\n"; + if (usePySideExtensions()) + s << "QT_WARNING_PUSH\nQT_WARNING_DISABLE_DEPRECATED\n"; + s << "// The backward compatible alias with upper case indexes.\n" + << cppApiVariableNameOld() << " = reinterpret_cast<PyTypeObject **>(cppApi);\n"; + if (usePySideExtensions()) + s << "QT_WARNING_POP\n"; + s << '\n'; } s << "// Create an array of primitive type converters for the current module.\n" @@ -6474,7 +6513,8 @@ bool CppGenerator::finishGeneration() } } - s << "\nif (" << shibokenErrorsOccurred << ") {\n" << indent + s << '\n' << initInheritanceFunction << "();\n" + << "\nif (" << shibokenErrorsOccurred << ") {\n" << indent << "PyErr_Print();\n" << "Py_FatalError(\"can't initialize module " << moduleName() << "\");\n" << outdent << "}\n"; diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.h b/sources/shiboken6/generator/shiboken/cppgenerator.h index 1ad576895..a31c2ca14 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator.h +++ b/sources/shiboken6/generator/shiboken/cppgenerator.h @@ -395,6 +395,7 @@ private: static void writeSignatureStrings(TextStream &s, const QString &signatures, const QString &arrayName, const char *comment); + void writeInitInheritance(TextStream &s) const; void writeClassRegister(TextStream &s, const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext, @@ -480,6 +481,8 @@ private: const AbstractMetaType &type, const ApiExtractorResult &api); void writeSmartPointerConverterInitialization(TextStream &s, const AbstractMetaType &ype) const; + + static QString typeInitStruct(const TypeEntryCPtr &te); static void writeExtendedConverterInitialization(TextStream &s, const TypeEntryCPtr &externalType, const AbstractMetaClassCList &conversions); diff --git a/sources/shiboken6/generator/shiboken/headergenerator.cpp b/sources/shiboken6/generator/shiboken/headergenerator.cpp index 1f574b47c..35d0d114f 100644 --- a/sources/shiboken6/generator/shiboken/headergenerator.cpp +++ b/sources/shiboken6/generator/shiboken/headergenerator.cpp @@ -706,8 +706,12 @@ bool HeaderGenerator::finishGeneration() macrosStream << ti; macrosStream << "};\n\n"; + // FIXME: Remove backwards compatible variable in PySide 7. macrosStream << "// This variable stores all Python types exported by this module.\n"; macrosStream << "extern Shiboken::Module::TypeInitStruct *" << cppApiVariableName() << ";\n\n"; + macrosStream << "// This variable stores all Python types exported by this module "; + macrosStream << "in a backwards compatible way with identical indexing.\n"; + macrosStream << "[[deprecated]] extern PyTypeObject **" << cppApiVariableNameOld() << ";\n\n"; macrosStream << "// This variable stores the Python module object exported by this module.\n"; macrosStream << "extern PyObject *" << pythonModuleObjectName() << ";\n\n"; macrosStream << "// This variable stores all type converters exported by this module.\n"; diff --git a/sources/shiboken6/libshiboken/basewrapper.cpp b/sources/shiboken6/libshiboken/basewrapper.cpp index f34e053ca..0ce80d0c6 100644 --- a/sources/shiboken6/libshiboken/basewrapper.cpp +++ b/sources/shiboken6/libshiboken/basewrapper.cpp @@ -27,7 +27,9 @@ #include "signature_p.h" #include "voidptr.h" +#include <string> #include <iostream> +#include <sstream> #if defined(__APPLE__) #include <dlfcn.h> @@ -37,7 +39,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; @@ -46,6 +114,8 @@ static void callDestructor(const Shiboken::DtorAccumulatorVisitor::DestructorEnt } } +} // namespace Shiboken + extern "C" { @@ -129,31 +199,6 @@ static PyGetSetDef SbkObjectType_tp_getset[] = { 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)}, @@ -191,14 +236,9 @@ static PyTypeObject *createObjectTypeType() 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); + return SbkType_FromSpec(_PepRuntimeVersion() >= 0x030C00 ? + &SbkObjectType_Type_spec_312 : + &SbkObjectType_Type_spec); } PyTypeObject *SbkObjectType_TypeF(void) @@ -370,9 +410,8 @@ static void SbkDeallocWrapperCommon(PyObject *pyObj, bool 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]}; @@ -390,10 +429,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); @@ -730,54 +768,24 @@ 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()))) { - result = PepType_SOTP(type)->is_user_type - ? walkThroughClassHierarchy(type, visitor) : visitor->visit(type); - } - } - return result; -} // Wrapper metatype and base type ---------------------------------------------------------- -HierarchyVisitor::HierarchyVisitor() = default; -HierarchyVisitor::~HierarchyVisitor() = default; - -bool BaseCountVisitor::visit(PyTypeObject *) -{ - m_count++; - return false; -} - -bool BaseAccumulatorVisitor::visit(PyTypeObject *node) -{ - m_bases.push_back(node); - return false; -} - -bool GetIndexVisitor::visit(PyTypeObject *node) -{ - m_index++; - return PyType_IsSubtype(node, m_desiredType); -} +void _initMainThreadId(); // helper.cpp -bool DtorAccumulatorVisitor::visit(PyTypeObject *node) +static std::string msgFailedToInitializeType(const char *description) { - auto *sotp = PepType_SOTP(node); - m_entries.push_back(DestructorEntry{sotp->cpp_dtor, - m_pyObject->d->cptr[m_entries.size()]}); - return false; + std::ostringstream stream; + stream << "[libshiboken] Failed to initialize " << description; + if (auto *error = PepErr_GetRaisedException()) { + if (auto *str = PyObject_Str(error)) + stream << ": " << Shiboken::String::toCString(str); + Py_DECREF(error); + } + stream << '.'; + return stream.str(); } -void _initMainThreadId(); // helper.cpp - namespace Conversions { void init(); } void init() @@ -793,11 +801,13 @@ void init() //Init private data Pep384_Init(); - if (PyType_Ready(SbkObjectType_TypeF()) < 0) - Py_FatalError("[libshiboken] Failed to initialize Shiboken.BaseWrapperType metatype."); + auto *type = SbkObjectType_TypeF(); + if (type == nullptr || PyType_Ready(type) < 0) + Py_FatalError(msgFailedToInitializeType("Shiboken.BaseWrapperType metatype").c_str()); - if (PyType_Ready(SbkObject_TypeF()) < 0) - Py_FatalError("[libshiboken] Failed to initialize Shiboken.BaseWrapper type."); + type = SbkObject_TypeF(); + if (type == nullptr || PyType_Ready(type) < 0) + Py_FatalError(msgFailedToInitializeType("Shiboken.BaseWrapper type").c_str()); VoidPtr::init(); @@ -871,20 +881,6 @@ PyObject *checkInvalidArgumentCount(Py_ssize_t numArgs, Py_ssize_t minArgs, Py_s return result; } -class FindBaseTypeVisitor : public HierarchyVisitor -{ -public: - explicit FindBaseTypeVisitor(PyTypeObject *typeToFind) : m_typeToFind(typeToFind) {} - - bool visit(PyTypeObject *node) override - { - return node == m_typeToFind; - } - -private: - PyTypeObject *m_typeToFind; -}; - std::vector<SbkObject *> splitPyObject(PyObject *pyObj) { std::vector<SbkObject *> result; @@ -925,8 +921,8 @@ bool isUserType(PyTypeObject *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; } @@ -1001,17 +997,11 @@ introduceWrapperType(PyObject *enclosingObject, PyObject *bases, unsigned wrapperFlags) { - const auto basesSize = PySequence_Fast_GET_SIZE(bases); - assert(basesSize > 0); + assert(PySequence_Fast_GET_SIZE(bases) > 0); typeSpec->slots[0].pfunc = PySequence_Fast_GET_ITEM(bases, 0); auto *type = SbkType_FromSpecBasesMeta(typeSpec, bases, SbkObjectType_TypeF()); - for (Py_ssize_t i = 0; i < basesSize; ++i) { - auto *st = reinterpret_cast<PyTypeObject *>(PySequence_Fast_GET_ITEM(bases, i)); - BindingManager::instance().addClassInheritance(st, type); - } - auto sotp = PepType_SOTP(type); if (wrapperFlags & DeleteInMainThread) sotp->delete_in_main_thread = 1; @@ -1081,6 +1071,26 @@ bool hasSpecialCastFunction(PyTypeObject *sbkType) return d != nullptr && d->mi_specialcast != nullptr; } +// Find whether base is a direct single line base class of type +// (no multiple inheritance), that is, a C++ pointer cast can safely be done. +static bool isDirectAncestor(PyTypeObject *type, PyTypeObject *base) +{ + if (type == base) + return true; + if (PyTuple_Size(type->tp_bases) == 0) + return false; + auto *sbkObjectType = SbkObject_TypeF(); + auto *firstBase = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(type->tp_bases, 0)); + return firstBase != sbkObjectType + && PyType_IsSubtype(type, sbkObjectType) != 0 + && isDirectAncestor(firstBase, base); +} + +bool canDowncastTo(PyTypeObject *baseType, PyTypeObject *targetType) +{ + return isDirectAncestor(targetType, baseType); +} + } // namespace ObjectType @@ -1169,9 +1179,7 @@ void callCppDestructors(SbkObject *pyObj) PyTypeObject *type = Py_TYPE(pyObj); 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(); @@ -1452,25 +1460,67 @@ SbkObject *findColocatedChild(SbkObject *wrapper, return nullptr; } +// Legacy, for compatibility only. PyObject *newObject(PyTypeObject *instanceType, void *cptr, bool hasOwnership, bool isExactType, const char *typeName) { - // Try to find the exact type of cptr. - if (!isExactType) { - if (PyTypeObject *exactType = ObjectType::typeForTypeName(typeName)) { - instanceType = exactType; - } else { - auto resolved = BindingManager::instance().findDerivedType(cptr, instanceType); - if (resolved.first != nullptr) { - instanceType = resolved.first; - cptr = resolved.second; - } + return isExactType + ? newObjectForType(instanceType, cptr, hasOwnership) + : newObjectWithHeuristics(instanceType, cptr, hasOwnership, typeName); +} + +static PyObject *newObjectWithHeuristicsHelper(PyTypeObject *instanceType, + PyTypeObject *exactType, + void *cptr, + bool hasOwnership) +{ + // Try to find the exact type of cptr. For hierarchies with + // non-virtual destructors, typeid() will return the base name. + // Try type discovery in these cases. + if (exactType == nullptr || exactType == instanceType) { + auto resolved = BindingManager::instance().findDerivedType(cptr, instanceType); + if (resolved.first != nullptr + && Shiboken::ObjectType::canDowncastTo(instanceType, resolved.first)) { + exactType = resolved.first; + cptr = resolved.second; } } + return newObjectForType(exactType != nullptr ? exactType : instanceType, + cptr, hasOwnership); +} + +PyObject *newObjectForPointer(PyTypeObject *instanceType, + void *cptr, + bool hasOwnership, + const char *typeName) +{ + // Try to find the exact type of cptr. + PyTypeObject *exactType = ObjectType::typeForTypeName(typeName); + // PYSIDE-868: In case of multiple inheritance, (for example, + // a function returning a QPaintDevice * from a QWidget *), + // use instance type to avoid pointer offset errors. + return exactType != nullptr && !Shiboken::ObjectType::canDowncastTo(instanceType, exactType) + ? newObjectForType(instanceType, cptr, hasOwnership) + : newObjectWithHeuristicsHelper(instanceType, exactType, cptr, hasOwnership); +} + + +PyObject *newObjectWithHeuristics(PyTypeObject *instanceType, + void *cptr, + bool hasOwnership, + const char *typeName) +{ + return newObjectWithHeuristicsHelper(instanceType, + ObjectType::typeForTypeName(typeName), + cptr, hasOwnership); +} + +PyObject *newObjectForType(PyTypeObject *instanceType, void *cptr, bool hasOwnership) +{ bool shouldCreate = true; bool shouldRegister = true; SbkObject *self = nullptr; diff --git a/sources/shiboken6/libshiboken/basewrapper.h b/sources/shiboken6/libshiboken/basewrapper.h index 4835c4810..ec5545aea 100644 --- a/sources/shiboken6/libshiboken/basewrapper.h +++ b/sources/shiboken6/libshiboken/basewrapper.h @@ -265,6 +265,14 @@ LIBSHIBOKEN_API PyTypeObject *typeForTypeName(const char *typeName); * \since 5.12 */ LIBSHIBOKEN_API bool hasSpecialCastFunction(PyTypeObject *sbkType); + +/// Returns whether a C++ pointer of \p baseType can be safely downcast +/// to \p targetType (base is a direct, single line base class of targetType). +/// (is a direct, single-line inheritance) +/// \param baseType Python type of base class +/// \param targetType Python type of derived class +/// \since 6.8 +LIBSHIBOKEN_API bool canDowncastTo(PyTypeObject *baseType, PyTypeObject *targetType); } namespace Object { @@ -297,7 +305,8 @@ LIBSHIBOKEN_API SbkObject *findColocatedChild(SbkObject *wrapper, const PyTypeObject *instanceType); /** - * Bind a C++ object to Python. + * Bind a C++ object to Python. Forwards to + * newObjectWithHeuristics(), newObjectForType() depending on \p isExactType. * \param instanceType equivalent Python type for the C++ object. * \param hasOwnership if true, Python will try to delete the underlying C++ object when there's no more refs. * \param isExactType if false, Shiboken will use some heuristics to detect the correct Python type of this C++ @@ -311,6 +320,40 @@ LIBSHIBOKEN_API PyObject *newObject(PyTypeObject *instanceType, bool isExactType = false, const char *typeName = nullptr); +/// Bind a C++ object to Python for polymorphic pointers. Calls +/// newObjectWithHeuristics() with an additional check for multiple +/// inheritance, in which case it will fall back to instanceType. +/// \param instanceType Equivalent Python type for the C++ object. +/// \param hasOwnership if true, Python will try to delete the underlying C++ object +/// when there's no more refs. +/// \param typeName If non-null, this will be used as helper to find the correct +/// Python type for this object (obtained by typeid().name(). +LIBSHIBOKEN_API PyObject *newObjectForPointer(PyTypeObject *instanceType, + void *cptr, + bool hasOwnership = true, + const char *typeName = nullptr); + +/// Bind a C++ object to Python using some heuristics to detect the correct +/// Python type of this C++ object. In any case \p instanceType must be provided; +/// it'll be used as search starting point and as fallback. +/// \param instanceType Equivalent Python type for the C++ object. +/// \param hasOwnership if true, Python will try to delete the underlying C++ object +/// C++ object when there are no more references. +/// when there's no more refs. +/// \param typeName If non-null, this will be used as helper to find the correct +/// Python type for this object (obtained by typeid().name(). +LIBSHIBOKEN_API PyObject *newObjectWithHeuristics(PyTypeObject *instanceType, + void *cptr, + bool hasOwnership = true, + const char *typeName = nullptr); + +/// Bind a C++ object to Python using the given type. +/// \param instanceType Equivalent Python type for the C++ object. +/// \param hasOwnership if true, Python will try to delete the underlying +/// C++ object when there are no more references. +LIBSHIBOKEN_API PyObject *newObjectForType(PyTypeObject *instanceType, + void *cptr, bool hasOwnership = true); + /** * Changes the valid flag of a PyObject, invalid objects will raise an exception when someone tries to access it. */ diff --git a/sources/shiboken6/libshiboken/basewrapper_p.h b/sources/shiboken6/libshiboken/basewrapper_p.h index 526bf9fa3..fb9140793 100644 --- a/sources/shiboken6/libshiboken/basewrapper_p.h +++ b/sources/shiboken6/libshiboken/basewrapper_p.h @@ -114,7 +114,8 @@ struct SbkObjectTypePrivate /// 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. + /// 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; @@ -142,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(PyTypeObject *node) = 0; // return true to terminate -}; - -class BaseCountVisitor : public HierarchyVisitor -{ -public: - bool visit(PyTypeObject *) override; - - int count() const { return m_count; } - -private: - int m_count = 0; -}; - -class BaseAccumulatorVisitor : public HierarchyVisitor -{ -public: - using Result = std::vector<PyTypeObject *>; - - bool visit(PyTypeObject *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(PyTypeObject *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(PyTypeObject *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, HierarchyVisitor::visit is called and the algorithm -* considers 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<PyTypeObject *> getCppBaseClasses(PyTypeObject *baseType) -{ - BaseAccumulatorVisitor visitor; - walkThroughClassHierarchy(baseType, &visitor); - return visitor.bases(); -} +int getNumberOfCppBaseClasses(PyTypeObject *baseType); namespace Object { diff --git a/sources/shiboken6/libshiboken/bindingmanager.cpp b/sources/shiboken6/libshiboken/bindingmanager.cpp index 60460c6ab..83c927ae5 100644 --- a/sources/shiboken6/libshiboken/bindingmanager.cpp +++ b/sources/shiboken6/libshiboken/bindingmanager.cpp @@ -7,6 +7,7 @@ #include "bindingmanager.h" #include "gilstate.h" #include "helper.h" +#include "sbkmodule.h" #include "sbkstring.h" #include "sbkstaticstrings.h" #include "sbkfeature_base.h" @@ -21,6 +22,29 @@ #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 { @@ -56,28 +80,45 @@ public: } }; -class Graph : public BaseGraph<PyTypeObject *> +class Graph : public BaseGraph<GraphNode> { public: - Graph() = default; + using TypeCptrPair = BindingManager::TypeCptrPair; - BindingManager::TypeCptrPair identifyType(void *cptr, PyTypeObject *type, PyTypeObject *baseType) const; + TypeCptrPair identifyType(void *cptr, PyTypeObject *type, PyTypeObject *baseType) const + { + return identifyType(cptr, GraphNode(type->tp_name), type, baseType); + } bool dumpTypeGraph(const char *fileName) const; + +private: + TypeCptrPair identifyType(void *cptr, const GraphNode &typeNode, PyTypeObject *type, + PyTypeObject *baseType) const; }; -BindingManager::TypeCptrPair Graph::identifyType(void *cptr, PyTypeObject *type, PyTypeObject *baseType) const +Graph::TypeCptrPair Graph::identifyType(void *cptr, + const GraphNode &typeNode, PyTypeObject *type, + PyTypeObject *baseType) const { - auto edgesIt = m_edges.find(type); + assert(typeNode.initStruct != nullptr || type != nullptr); + auto edgesIt = m_edges.find(typeNode); if (edgesIt != m_edges.end()) { const NodeList &adjNodes = edgesIt->second; - for (PyTypeObject *node : adjNodes) { - auto newType = identifyType(cptr, node, baseType); + for (const auto &node : adjNodes) { + auto newType = identifyType(cptr, node, nullptr, baseType); if (newType.first != nullptr) return newType; } } + 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)) @@ -86,9 +127,8 @@ BindingManager::TypeCptrPair Graph::identifyType(void *cptr, PyTypeObject *type, return {nullptr, nullptr}; } -static void formatDotNode(const char *nameC, std::ostream &file) +static void formatDotNode(std::string_view name, std::ostream &file) { - std::string_view name(nameC); auto lastDot = name.rfind('.'); file << " \"" << name << "\" [ label="; if (lastDot != std::string::npos) { @@ -109,15 +149,15 @@ bool Graph::dumpTypeGraph(const char *fileName) const file << "digraph D {\n"; // Define nodes with short names - for (const auto *node : nodeSet()) - formatDotNode(node->tp_name, file); + for (const auto &node : nodeSet()) + formatDotNode(node.name, file); // Write edges for (const auto &p : m_edges) { - auto *node1 = p.first; + const auto &node1 = p.first; const NodeList &nodeList = p.second; - for (const PyTypeObject *node2 : nodeList) - file << " \"" << node2->tp_name << "\" -> \"" << node1->tp_name << "\"\n"; + for (const auto &node2 : nodeList) + file << " \"" << node2.name << "\" -> \"" << node1.name << "\"\n"; } file << "}\n"; return true; @@ -380,9 +420,10 @@ PyObject *BindingManager::getOverride(const void *cptr, return nullptr; } -void BindingManager::addClassInheritance(PyTypeObject *parent, PyTypeObject *child) +void BindingManager::addClassInheritance(Module::TypeInitStruct *parent, + Module::TypeInitStruct *child) { - m_d->classHierarchy.addEdge(parent, child); + m_d->classHierarchy.addEdge(GraphNode(parent), GraphNode(child)); } BindingManager::TypeCptrPair BindingManager::findDerivedType(void *cptr, PyTypeObject *type) const diff --git a/sources/shiboken6/libshiboken/bindingmanager.h b/sources/shiboken6/libshiboken/bindingmanager.h index 281de2c73..54c4e486a 100644 --- a/sources/shiboken6/libshiboken/bindingmanager.h +++ b/sources/shiboken6/libshiboken/bindingmanager.h @@ -15,6 +15,10 @@ struct SbkObject; namespace Shiboken { +namespace Module { +struct TypeInitStruct; +} + struct DestructorEntry; using ObjectVisitor = void (*)(SbkObject *, void *); @@ -40,7 +44,7 @@ public: SbkObject *retrieveWrapper(const void *cptr); PyObject *getOverride(const void *cptr, PyObject *nameCache[], const char *methodName); - void addClassInheritance(PyTypeObject *parent, PyTypeObject *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. diff --git a/sources/shiboken6/libshiboken/pep384impl.cpp b/sources/shiboken6/libshiboken/pep384impl.cpp index 4b3759456..5310207a3 100644 --- a/sources/shiboken6/libshiboken/pep384impl.cpp +++ b/sources/shiboken6/libshiboken/pep384impl.cpp @@ -105,13 +105,13 @@ static PyType_Spec typeprobe_spec = { static void check_PyTypeObject_valid() { - auto *obtype = reinterpret_cast<PyObject *>(&PyType_Type); - auto *probe_tp_base = reinterpret_cast<PyTypeObject *>( - PyObject_GetAttr(obtype, Shiboken::PyMagicName::base())); + auto *typetype = &PyType_Type; + auto *obtype = reinterpret_cast<PyObject *>(typetype); + auto *probe_tp_base_obj = PyObject_GetAttr(obtype, Shiboken::PyMagicName::base()); + auto *probe_tp_base = reinterpret_cast<PyTypeObject *>(probe_tp_base_obj); auto *probe_tp_bases = PyObject_GetAttr(obtype, Shiboken::PyMagicName::bases()); - auto *check = reinterpret_cast<PyTypeObject *>( - PyType_FromSpecWithBases(&typeprobe_spec, probe_tp_bases)); - auto *typetype = reinterpret_cast<PyTypeObject *>(obtype); + auto *checkObj = PyType_FromSpecWithBases(&typeprobe_spec, probe_tp_bases); + auto *check = reinterpret_cast<PyTypeObject *>(checkObj); PyObject *w = PyObject_GetAttr(obtype, Shiboken::PyMagicName::weakrefoffset()); long probe_tp_weakrefoffset = PyLong_AsLong(w); PyObject *d = PyObject_GetAttr(obtype, Shiboken::PyMagicName::dictoffset()); @@ -149,8 +149,8 @@ check_PyTypeObject_valid() || probe_tp_mro != typetype->tp_mro || Py_TPFLAGS_DEFAULT != (check->tp_flags & Py_TPFLAGS_DEFAULT)) Py_FatalError("The structure of type objects has changed!"); - Py_DECREF(check); - Py_DECREF(probe_tp_base); + Py_DECREF(checkObj); + Py_DECREF(probe_tp_base_obj); Py_DECREF(w); Py_DECREF(d); Py_DECREF(probe_tp_bases); @@ -482,6 +482,47 @@ Pep_GetVerboseFlag() } #endif // Py_LIMITED_API +// Support for pyerrors.h + +#if defined(Py_LIMITED_API) || PY_VERSION_HEX < 0x030C0000 +// Emulate PyErr_GetRaisedException() using the deprecated PyErr_Fetch()/PyErr_Store() +PyObject *PepErr_GetRaisedException() +{ + PyObject *type{}; + PyObject *value{}; + PyObject *traceback{}; + PyErr_Fetch(&type, &value, &traceback); + Py_XINCREF(value); + PyErr_Restore(type, value, traceback); + return value; +} + +struct PepException_HEAD +{ + PyObject_HEAD + PyObject *x1; // dict + PyObject *args; +}; + +// PyException_GetArgs/PyException_SetArgs were added to the stable API in 3.12 +PyObject *PepException_GetArgs(PyObject *ex) +{ + auto *h = reinterpret_cast<PepException_HEAD *>(ex); + Py_XINCREF(h->args); + return h->args; +} + +LIBSHIBOKEN_API void PepException_SetArgs(PyObject *ex, PyObject *args) +{ + auto *h = reinterpret_cast<PepException_HEAD *>(ex); + Py_XINCREF(args); + auto *old = h->args; // Py_XSETREF() + h->args = args; + Py_XDECREF(old); + +} +#endif // Limited or < 3.12 + /***************************************************************************** * * Support for code.h @@ -722,11 +763,8 @@ PyTypeObject *PepStaticMethod_TypePtr = nullptr; static PyTypeObject * getStaticMethodType(void) { - // this works for Python 3, only - // "StaticMethodType = type(str.__dict__['maketrans'])\n"; static const char prog[] = - "from xxsubtype import spamlist\n" - "result = type(spamlist.__dict__['staticmeth'])\n"; + "result = type(str.__dict__['maketrans'])\n"; return reinterpret_cast<PyTypeObject *>(PepRun_GetResult(prog)); } @@ -1009,9 +1047,12 @@ long _PepRuntimeVersion() SbkObjectTypePrivate *PepType_SOTP(PyTypeObject *type) { + // PYSIDE-2676: Use the meta type explicitly. + // A derived type would fail the offset calculation. + static auto *meta = SbkObjectType_TypeF(); assert(SbkObjectType_Check(type)); auto *obType = reinterpret_cast<PyObject *>(type); - void *data = PyObject_GetTypeData(obType, Py_TYPE(obType)); + void *data = PyObject_GetTypeData(obType, meta); return reinterpret_cast<SbkObjectTypePrivate *>(data); } @@ -1061,11 +1102,12 @@ static thread_local SbkObjectTypePrivate *SOTP_value{}; SbkObjectTypePrivate *PepType_SOTP(PyTypeObject *type) { + static auto *meta = SbkObjectType_TypeF(); 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)); + void *data = PepObject_GetTypeData(obType, meta); return reinterpret_cast<SbkObjectTypePrivate *>(data); } if (type == SOTP_key) diff --git a/sources/shiboken6/libshiboken/pep384impl.h b/sources/shiboken6/libshiboken/pep384impl.h index ec58aac81..7188366e2 100644 --- a/sources/shiboken6/libshiboken/pep384impl.h +++ b/sources/shiboken6/libshiboken/pep384impl.h @@ -188,6 +188,17 @@ LIBSHIBOKEN_API int Pep_GetFlag(const char *name); LIBSHIBOKEN_API int Pep_GetVerboseFlag(void); #endif +// pyerrors.h +#if defined(Py_LIMITED_API) || PY_VERSION_HEX < 0x030C0000 +LIBSHIBOKEN_API PyObject *PepErr_GetRaisedException(); +LIBSHIBOKEN_API PyObject *PepException_GetArgs(PyObject *ex); +LIBSHIBOKEN_API void PepException_SetArgs(PyObject *ex, PyObject *args); +#else +# define PepErr_GetRaisedException PyErr_GetRaisedException +# define PepException_GetArgs PyException_GetArgs +# define PepException_SetArgs PyException_SetArgs +#endif + /***************************************************************************** * * RESOLVED: unicodeobject.h diff --git a/sources/shiboken6/libshiboken/sbkcontainer.h b/sources/shiboken6/libshiboken/sbkcontainer.h index 240c772a9..8ad5aadc6 100644 --- a/sources/shiboken6/libshiboken/sbkcontainer.h +++ b/sources/shiboken6/libshiboken/sbkcontainer.h @@ -74,10 +74,9 @@ public: static PyObject *tpNewInvalid(PyTypeObject * /* subtype */, PyObject * /* args */, PyObject * /* kwds */) { - PyErr_Format(PyExc_NotImplementedError, + return 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 */) @@ -105,10 +104,8 @@ public: 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; - } + if (i < 0 || i >= Py_ssize_t(d->m_list->size())) + return PyErr_Format(PyExc_IndexError, "index out of bounds"); auto it = std::cbegin(*d->m_list); std::advance(it, i); return ShibokenContainerValueConverter<value_type>::convertValueToPython(*it); @@ -133,14 +130,10 @@ public: 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; - } + if (!ShibokenContainerValueConverter<value_type>::checkValue(pyArg)) + return PyErr_Format(PyExc_TypeError, "wrong type passed to append."); + if (d->m_const) + return PyErr_Format(PyExc_TypeError, msgModifyConstContainer); OptionalValue value = ShibokenContainerValueConverter<value_type>::convertValueToCpp(pyArg); if (!value.has_value()) @@ -152,14 +145,10 @@ public: 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; - } + if (!ShibokenContainerValueConverter<value_type>::checkValue(pyArg)) + return PyErr_Format(PyExc_TypeError, "wrong type passed to append."); + if (d->m_const) + return PyErr_Format(PyExc_TypeError, msgModifyConstContainer); OptionalValue value = ShibokenContainerValueConverter<value_type>::convertValueToCpp(pyArg); if (!value.has_value()) @@ -171,10 +160,8 @@ public: static PyObject *clear(PyObject *self) { auto *d = get(self); - if (d->m_const) { - PyErr_SetString(PyExc_TypeError, msgModifyConstContainer); - return nullptr; - } + if (d->m_const) + return PyErr_Format(PyExc_TypeError, msgModifyConstContainer); d->m_list->clear(); Py_RETURN_NONE; @@ -183,10 +170,8 @@ public: static PyObject *pop_back(PyObject *self) { auto *d = get(self); - if (d->m_const) { - PyErr_SetString(PyExc_TypeError, msgModifyConstContainer); - return nullptr; - } + if (d->m_const) + return PyErr_Format(PyExc_TypeError, msgModifyConstContainer); d->m_list->pop_back(); Py_RETURN_NONE; @@ -195,10 +180,8 @@ public: static PyObject *pop_front(PyObject *self) { auto *d = get(self); - if (d->m_const) { - PyErr_SetString(PyExc_TypeError, msgModifyConstContainer); - return nullptr; - } + if (d->m_const) + return PyErr_Format(PyExc_TypeError, msgModifyConstContainer); d->m_list->pop_front(); Py_RETURN_NONE; @@ -208,21 +191,16 @@ public: 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 (PyLong_Check(pyArg) == 0) + return PyErr_Format(PyExc_TypeError, "wrong type passed to reserve()."); + if (d->m_const) + return PyErr_Format(PyExc_TypeError, msgModifyConstContainer); 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; + return PyErr_Format(PyExc_TypeError, "Container does not support reserve()."); } Py_RETURN_NONE; diff --git a/sources/shiboken6/libshiboken/sbkconverter.cpp b/sources/shiboken6/libshiboken/sbkconverter.cpp index 358827aa8..9ab674415 100644 --- a/sources/shiboken6/libshiboken/sbkconverter.cpp +++ b/sources/shiboken6/libshiboken/sbkconverter.cpp @@ -12,7 +12,12 @@ #include "voidptr.h" #include <string> +#include <cstring> +#include <iostream> #include <unordered_map> +#include <unordered_set> +#include <map> +#include <set> static SbkConverter **PrimitiveTypeConverters; @@ -72,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, @@ -422,18 +524,36 @@ void registerConverterName(SbkConverter *converter, const char *typeName) converters.insert(std::make_pair(typeName, converter)); } -static std::string getRealTypeName(const char *name) +static std::string getRealTypeName(const std::string &typeName) { - 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) +// 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 @@ -442,6 +562,9 @@ SbkConverter *getConverter(const char *typeName) 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 + "'."; @@ -450,6 +573,15 @@ 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]; @@ -704,14 +836,7 @@ PyTypeObject *getPythonTypeObject(const SbkConverter *converter) PyTypeObject *getPythonTypeObject(const char *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; + return getPythonTypeObject(getConverter(typeName)); } bool pythonTypeIsValueType(const SbkConverter *converter) diff --git a/sources/shiboken6/libshiboken/sbkconverter_p.h b/sources/shiboken6/libshiboken/sbkconverter_p.h index c886c9b9f..08fc4c8e1 100644 --- a/sources/shiboken6/libshiboken/sbkconverter_p.h +++ b/sources/shiboken6/libshiboken/sbkconverter_p.h @@ -531,6 +531,12 @@ SbkConverter *createConverterObject(PyTypeObject *type, IsConvertibleToCppFunc toCppPointerCheckFunc, CppToPythonFunc pointerToPythonFunc, CppToPythonFunc copyToPythonFunc); + +LIBSHIBOKEN_API void dumpConverters(); + +/// Interface for sbkmodule which must reset cache when new module is loaded. +LIBSHIBOKEN_API void clearNegativeLazyCache(); + } // namespace Shiboken::Conversions #endif // SBK_CONVERTER_P_H diff --git a/sources/shiboken6/libshiboken/sbkcpptonumpy.cpp b/sources/shiboken6/libshiboken/sbkcpptonumpy.cpp index 44e900f01..7637efa70 100644 --- a/sources/shiboken6/libshiboken/sbkcpptonumpy.cpp +++ b/sources/shiboken6/libshiboken/sbkcpptonumpy.cpp @@ -49,17 +49,17 @@ PyObject *createByteArray1(Py_ssize_t, const uint8_t *) PyObject *createDoubleArray1(Py_ssize_t, const double *) { - return Py_None; + Py_RETURN_NONE; } PyObject *createFloatArray1(Py_ssize_t, const float *) { - return Py_None; + Py_RETURN_NONE; } PyObject *createIntArray1(Py_ssize_t, const int *) { - return Py_None; + Py_RETURN_NONE; } #endif // !HAVE_NUMPY diff --git a/sources/shiboken6/libshiboken/sbkerrors.cpp b/sources/shiboken6/libshiboken/sbkerrors.cpp index 1832624d5..84c080f8d 100644 --- a/sources/shiboken6/libshiboken/sbkerrors.cpp +++ b/sources/shiboken6/libshiboken/sbkerrors.cpp @@ -6,6 +6,11 @@ #include "helper.h" #include "gilstate.h" +#include <cstdio> +#include <string> + +using namespace std::literals::string_literals; + namespace Shiboken { @@ -93,6 +98,21 @@ void setWrongContainerType() PyErr_SetString(PyExc_TypeError, "Wrong type passed to container conversion."); } +// Prepend something to an exception message provided it is a single string +// argument. +static bool prependToExceptionMessage(PyObject *exc, const char *context) +{ + Shiboken::AutoDecRef args(PepException_GetArgs(exc)); + if (args.isNull() || PyTuple_Check(args.object()) == 0 || PyTuple_Size(args) != 1) + return false; + auto *oldMessage = PyTuple_GetItem(args, 0); + if (oldMessage == nullptr || PyUnicode_CheckExact(oldMessage) == 0) + return false; + auto *newMessage = PyUnicode_FromFormat("%s%U", context, oldMessage); + PepException_SetArgs(exc, PyTuple_Pack(1, newMessage)); + return true; +} + struct ErrorStore { PyObject *type; PyObject *exc; @@ -101,17 +121,42 @@ struct ErrorStore { static thread_local ErrorStore savedError{}; +static bool hasPythonContext() +{ + return _pythonContextStack & 1; +} + 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) + if (hasPythonContext()) PyErr_Fetch(&savedError.type, &savedError.exc, &savedError.traceback); else PyErr_Print(); } +// Like storeErrorOrPrint() with additional context info that is prepended +// to the exception message or printed. +static void storeErrorOrPrintWithContext(const char *context) +{ + if (hasPythonContext()) { + PyErr_Fetch(&savedError.type, &savedError.exc, &savedError.traceback); + prependToExceptionMessage(savedError.exc, context); + } else { + std::fputs(context, stderr); + PyErr_Print(); + } +} + +void storePythonOverrideErrorOrPrint(const char *className, const char *funcName) +{ + const std::string context = "Error calling Python override of "s + + className + "::"s + funcName + "(): "s; + storeErrorOrPrintWithContext(context.c_str()); +} + PyObject *occurred() { if (savedError.type) { diff --git a/sources/shiboken6/libshiboken/sbkerrors.h b/sources/shiboken6/libshiboken/sbkerrors.h index 6ff85f8e1..18ce701e7 100644 --- a/sources/shiboken6/libshiboken/sbkerrors.h +++ b/sources/shiboken6/libshiboken/sbkerrors.h @@ -50,6 +50,11 @@ LIBSHIBOKEN_API void setWrongContainerType(); /// This replaces `PyErr_Print`, which cannot report errors as exception. /// To be used in contexts where raising errors is impossible. LIBSHIBOKEN_API void storeErrorOrPrint(); + +/// Call storeErrorOrPrint() and print the context to report +/// errors when calling Python overrides of virtual functions. +LIBSHIBOKEN_API void storePythonOverrideErrorOrPrint(const char *className, const char *funcName); + /// Handle an error as in PyErr_Occurred(), but also check for errors which /// were captured by `storeErrorOrPrint`. /// To be used in normal error checks. diff --git a/sources/shiboken6/libshiboken/sbkmodule.cpp b/sources/shiboken6/libshiboken/sbkmodule.cpp index 4153df27f..76087fbb5 100644 --- a/sources/shiboken6/libshiboken/sbkmodule.cpp +++ b/sources/shiboken6/libshiboken/sbkmodule.cpp @@ -7,21 +7,27 @@ #include "bindingmanager.h" #include "sbkstring.h" #include "sbkcppstring.h" +#include "sbkconverter_p.h" #include <unordered_map> #include <unordered_set> +#include <vector> #include <cstring> +/// This hash maps module objects to arrays of converters. +using ModuleConvertersMap = std::unordered_map<PyObject *, SbkConverter **> ; + /// This hash maps module objects to arrays of Python types. 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 **>; +struct TypeCreationStruct +{ + Shiboken::Module::TypeCreationFunction func; + std::vector<std::string> subtypeNames; +}; -/// 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 type names to type creation structs. +using NameToTypeFunctionMap = std::unordered_map<std::string, TypeCreationStruct> ; /// This hash maps module objects to maps of names to functions. using ModuleToFuncsMap = std::unordered_map<PyObject *, NameToTypeFunctionMap> ; @@ -56,8 +62,8 @@ LIBSHIBOKEN_API PyTypeObject *get(TypeInitStruct &typeStruct) 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)); + PyErr_Format(PyExc_SystemError, "Module \"%U\" should already be in sys.modules", + modName.object()); return nullptr; } @@ -74,6 +80,39 @@ LIBSHIBOKEN_API PyTypeObject *get(TypeInitStruct &typeStruct) return typeStruct.type; } +static void incarnateHelper(PyObject *module, const std::string_view names, + const NameToTypeFunctionMap &nameToFunc) +{ + auto dotPos = names.find('.'); + std::string::size_type startPos = 0; + auto *modOrType{module}; + while (dotPos != std::string::npos) { + auto typeName = names.substr(startPos, dotPos - startPos); + AutoDecRef obTypeName(String::fromCppStringView(typeName)); + modOrType = PyObject_GetAttr(modOrType, obTypeName); + startPos = dotPos + 1; + dotPos = names.find('.', startPos); + } + // now we have the type to create. + auto funcIter = nameToFunc.find(std::string(names)); + // - call this function that returns a PyTypeObject + auto tcStruct = funcIter->second; + auto initFunc = tcStruct.func; + PyTypeObject *type = initFunc(modOrType); + auto name = names.substr(startPos); + PyObject_SetAttrString(modOrType, name.data(), reinterpret_cast<PyObject *>(type)); +} + +static void incarnateSubtypes(PyObject *module, + const std::vector<std::string> &nameList, + NameToTypeFunctionMap &nameToFunc) +{ + for (auto const & tableIter : nameList) { + std::string_view names(tableIter); + incarnateHelper(module, names, nameToFunc); + } +} + static PyTypeObject *incarnateType(PyObject *module, const char *name, NameToTypeFunctionMap &nameToFunc) { @@ -85,13 +124,15 @@ static PyTypeObject *incarnateType(PyObject *module, const char *name, return nullptr; } // - call this function that returns a PyTypeObject - auto pair = funcIter->second; - auto initFunc = pair.first; - auto *modOrType = pair.second; + auto tcStruct = funcIter->second; + auto initFunc = tcStruct.func; + auto *modOrType{module}; // PYSIDE-2404: Make sure that no switching happens during type creation. auto saveFeature = initSelectableFeature(nullptr); PyTypeObject *type = initFunc(modOrType); + if (!tcStruct.subtypeNames.empty()) + incarnateSubtypes(module, tcStruct.subtypeNames, nameToFunc); initSelectableFeature(saveFeature); // - assign this object to the name in the module @@ -164,7 +205,7 @@ static PyObject *PyModule_lazyGetAttro(PyObject *module, PyObject *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) + // - create the real type and handle subtypes auto *type = incarnateType(module, attrNameStr, nameToFunc); auto *ret = reinterpret_cast<PyObject *>(type); // - if attribute does really not exist use the original @@ -172,7 +213,6 @@ static PyObject *PyModule_lazyGetAttro(PyObject *module, PyObject *name) PyErr_Clear(); return origModuleGetattro(module, name); } - return ret; } @@ -292,25 +332,12 @@ static bool shouldLazyLoad(PyObject *module) return std::strncmp(modName, "PySide6.", 8) == 0; } -void AddTypeCreationFunction(PyObject *module, - const char *name, - TypeCreationFunction func) +void checkIfShouldLoadImmediately(PyObject *module, const std::string &name, + const NameToTypeFunctionMap &nameToFunc) { 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: @@ -319,56 +346,56 @@ void AddTypeCreationFunction(PyObject *module, // 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 + incarnateHelper(module, name, nameToFunc); } } void AddTypeCreationFunction(PyObject *module, const char *name, - TypeCreationFunction func, - const char *containerName) + TypeCreationFunction func) { - // 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 -} + // - locate the module in the moduleTofuncs mapping + auto tableIter = moduleToFuncs.find(module); + assert(tableIter != moduleToFuncs.end()); + // - Assign the name/generating function tcStruct. + auto &nameToFunc = tableIter->second; + TypeCreationStruct tcStruct{func, {}}; + auto nit = nameToFunc.find(name); + if (nit == nameToFunc.end()) + nameToFunc.insert(std::make_pair(name, tcStruct)); + else + nit->second = tcStruct; -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 + checkIfShouldLoadImmediately(module, name, nameToFunc); } void AddTypeCreationFunction(PyObject *module, - const char *name, + const char *containerName, TypeCreationFunction func, - const char *containerName3, - const char *containerName2, - const char *containerName) + const char *namePath) { - // 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 + // - locate the module in the moduleTofuncs mapping + auto tableIter = moduleToFuncs.find(module); + assert(tableIter != moduleToFuncs.end()); + // - Assign the name/generating function tcStruct. + auto &nameToFunc = tableIter->second; + auto nit = nameToFunc.find(containerName); + + // - insert namePath into the subtype vector of the main type. + nit->second.subtypeNames.push_back(namePath); + // - insert it also as its own entry. + nit = nameToFunc.find(namePath); + TypeCreationStruct tcStruct{func, {}}; + if (nit == nameToFunc.end()) + nameToFunc.insert(std::make_pair(namePath, tcStruct)); + else + nit->second = tcStruct; + + checkIfShouldLoadImmediately(module, namePath, nameToFunc); } PyObject *import(const char *moduleName) @@ -455,6 +482,8 @@ PyObject *create(const char * /* modName */, void *moduleData) // 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); + // Clear the non-existing name cache because we have a new module. + Shiboken::Conversions::clearNegativeLazyCache(); return module; } diff --git a/sources/shiboken6/libshiboken/sbkmodule.h b/sources/shiboken6/libshiboken/sbkmodule.h index 1b3de33b7..2c407e09d 100644 --- a/sources/shiboken6/libshiboken/sbkmodule.h +++ b/sources/shiboken6/libshiboken/sbkmodule.h @@ -56,18 +56,6 @@ LIBSHIBOKEN_API void AddTypeCreationFunction(PyObject *module, 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. diff --git a/sources/shiboken6/libshiboken/sbknumpy.cpp b/sources/shiboken6/libshiboken/sbknumpy.cpp index 2e1c64d73..b6422e73f 100644 --- a/sources/shiboken6/libshiboken/sbknumpy.cpp +++ b/sources/shiboken6/libshiboken/sbknumpy.cpp @@ -29,10 +29,8 @@ static void initNumPy() // 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 (_import_array() < 0) PyErr_Print(); - PyErr_Clear(); - } } #endif // HAVE_NUMPY diff --git a/sources/shiboken6/libshiboken/sbkstring.cpp b/sources/shiboken6/libshiboken/sbkstring.cpp index 1471cd7fe..b5e87ca5a 100644 --- a/sources/shiboken6/libshiboken/sbkstring.cpp +++ b/sources/shiboken6/libshiboken/sbkstring.cpp @@ -2,6 +2,7 @@ // 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 "sbkenum.h" #include "sbkstaticstrings_p.h" #include "autodecref.h" @@ -14,6 +15,11 @@ bool checkIterable(PyObject *obj) return PyObject_HasAttr(obj, Shiboken::PyMagicName::iter()); } +bool checkIterableArgument(PyObject *obj) +{ + return checkIterable(obj) && !Shiboken::Enum::check(obj); +} + static PyObject *initPathLike() { PyObject *PathLike{}; diff --git a/sources/shiboken6/libshiboken/sbkstring.h b/sources/shiboken6/libshiboken/sbkstring.h index f91847c11..ebc5428c7 100644 --- a/sources/shiboken6/libshiboken/sbkstring.h +++ b/sources/shiboken6/libshiboken/sbkstring.h @@ -13,6 +13,8 @@ namespace String { LIBSHIBOKEN_API bool check(PyObject *obj); LIBSHIBOKEN_API bool checkIterable(PyObject *obj); + /// Check for iterable function arguments (excluding enumerations) + LIBSHIBOKEN_API bool checkIterableArgument(PyObject *obj); LIBSHIBOKEN_API bool checkPath(PyObject *path); LIBSHIBOKEN_API bool checkType(PyTypeObject *obj); LIBSHIBOKEN_API bool checkChar(PyObject *obj); diff --git a/sources/shiboken6/libshiboken/voidptr.cpp b/sources/shiboken6/libshiboken/voidptr.cpp index 7045b08b1..8bb3f6ac8 100644 --- a/sources/shiboken6/libshiboken/voidptr.cpp +++ b/sources/shiboken6/libshiboken/voidptr.cpp @@ -156,10 +156,9 @@ PyObject *SbkVoidPtrObject_int(PyObject *v) PyObject *toBytes(PyObject *self, PyObject * /* args */) { auto *sbkObject = reinterpret_cast<SbkVoidPtrObject *>(self); - if (sbkObject->size < 0) { - PyErr_SetString(PyExc_IndexError, "VoidPtr does not have a size set."); - return nullptr; - } + if (sbkObject->size < 0) + return PyErr_Format(PyExc_IndexError, "VoidPtr does not have a size set."); + PyObject *bytes = PyBytes_FromStringAndSize(reinterpret_cast<const char *>(sbkObject->cptr), sbkObject->size); Py_XINCREF(bytes); diff --git a/sources/shiboken6/shibokenmodule/shibokenmodule.cpp b/sources/shiboken6/shibokenmodule/shibokenmodule.cpp index b3adfe78b..5c6219885 100644 --- a/sources/shiboken6/shibokenmodule/shibokenmodule.cpp +++ b/sources/shiboken6/shibokenmodule/shibokenmodule.cpp @@ -100,6 +100,10 @@ const bool ok = Shiboken::BindingManager::instance().dumpTypeGraph(%1); Shiboken::BindingManager::instance().dumpWrapperMap(); // @snippet dumpwrappermap +// @snippet dumpconverters +Shiboken::Conversions::dumpConverters(); +// @snippet dumpconverters + // @snippet init // Add __version__ and __version_info__ attributes to the module PyObject* version = PyTuple_New(5); diff --git a/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml b/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml index aa08a8bbf..acb522ecc 100644 --- a/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml +++ b/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml @@ -57,9 +57,14 @@ <inject-code file="shibokenmodule.cpp" snippet="dumpwrappermap"/> </add-function> + <add-function signature="dumpConverters()"> + <inject-code file="shibokenmodule.cpp" snippet="dumpconverters"/> + </add-function> + <extra-includes> <include file-name="sbkversion.h" location="local"/> <include file-name="voidptr.h" location="local"/> + <include file-name="sbkconverter_p.h" location="local"/> </extra-includes> <inject-code position="end" file="shibokenmodule.cpp" snippet="init"/> </typesystem> diff --git a/sources/shiboken6/tests/libother/othermultiplederived.h b/sources/shiboken6/tests/libother/othermultiplederived.h index a8e265388..cd9910687 100644 --- a/sources/shiboken6/tests/libother/othermultiplederived.h +++ b/sources/shiboken6/tests/libother/othermultiplederived.h @@ -10,7 +10,7 @@ class ObjectType; -class LIBOTHER_API OtherMultipleDerived : public MDerived1 +class LIBOTHER_API OtherMultipleDerived : public OtherBase, public MDerived1 { public: // this will use CppCopier from other module (bug#142) diff --git a/sources/shiboken6/tests/libsample/derived.h b/sources/shiboken6/tests/libsample/derived.h index b7736c37a..cf95cb601 100644 --- a/sources/shiboken6/tests/libsample/derived.h +++ b/sources/shiboken6/tests/libsample/derived.h @@ -26,7 +26,7 @@ public: public: void uselessMethod() {} SomeInnerClass operator+(const SomeInnerClass &other) { return other; } - bool operator==(const SomeInnerClass &) { return true; } + bool operator==(const SomeInnerClass &) const { return true; } }; explicit Derived(int id = -1) noexcept; diff --git a/sources/shiboken6/tests/libsample/point.cpp b/sources/shiboken6/tests/libsample/point.cpp index b8630eb1e..0a28e877f 100644 --- a/sources/shiboken6/tests/libsample/point.cpp +++ b/sources/shiboken6/tests/libsample/point.cpp @@ -34,7 +34,7 @@ void Point::show() const std::cout << "(x: " << m_x << ", y: " << m_y << ")"; } -bool Point::operator==(const Point &other) +bool Point::operator==(const Point &other) const { return m_x == other.m_x && m_y == other.m_y; } diff --git a/sources/shiboken6/tests/libsample/point.h b/sources/shiboken6/tests/libsample/point.h index 59e0236d5..7e5d128ab 100644 --- a/sources/shiboken6/tests/libsample/point.h +++ b/sources/shiboken6/tests/libsample/point.h @@ -38,7 +38,7 @@ public: // The != operator is not implemented for the purpose of testing // for the absense of the __ne__ method in the Python binding. - bool operator==(const Point &other); + bool operator==(const Point &other) const; Point operator+(const Point &other); Point operator-(const Point &other); diff --git a/sources/shiboken6/tests/libsample/pointf.cpp b/sources/shiboken6/tests/libsample/pointf.cpp index 6b39f73a9..736a5c6b5 100644 --- a/sources/shiboken6/tests/libsample/pointf.cpp +++ b/sources/shiboken6/tests/libsample/pointf.cpp @@ -26,7 +26,7 @@ void PointF::show() const std::cout << "(x: " << m_x << ", y: " << m_y << ")"; } -bool PointF::operator==(const PointF &other) +bool PointF::operator==(const PointF &other) const { return m_x == other.m_x && m_y == other.m_y; } diff --git a/sources/shiboken6/tests/libsample/pointf.h b/sources/shiboken6/tests/libsample/pointf.h index bb50b5c6d..49e009467 100644 --- a/sources/shiboken6/tests/libsample/pointf.h +++ b/sources/shiboken6/tests/libsample/pointf.h @@ -31,7 +31,7 @@ public: // The != operator is not implemented for the purpose of testing // for the absence of the __ne__ method in the Python binding. - bool operator==(const PointF &other); + bool operator==(const PointF &other) const; PointF operator+(const PointF &other); PointF operator-(const PointF &other); diff --git a/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.cpp b/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.cpp index a7b73cc81..1be93db66 100644 --- a/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.cpp +++ b/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.cpp @@ -51,6 +51,26 @@ void StdSharedPtrTestBench::printInt(const std::shared_ptr<int> &p) std::cerr << '\n'; } +std::shared_ptr<std::string> StdSharedPtrTestBench::createString(const char *text) +{ + return std::make_shared<std::string>(text); +} + +std::shared_ptr<std::string> StdSharedPtrTestBench::createNullString() +{ + return {}; +} + +void StdSharedPtrTestBench::printString(const std::shared_ptr<std::string> &p) +{ + std::cerr << __FUNCTION__ << ' '; + if (p.get()) + std::cerr << '"' << *p << '"'; + else + std::cerr << "nullptr"; + std::cerr << '\n'; +} + StdSharedPtrVirtualMethodTester::StdSharedPtrVirtualMethodTester() = default; StdSharedPtrVirtualMethodTester::~StdSharedPtrVirtualMethodTester() = default; diff --git a/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.h b/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.h index 8991cded6..04d75d5ef 100644 --- a/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.h +++ b/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.h @@ -7,6 +7,7 @@ #include "libsmartmacros.h" #include <memory> +#include <string> class Integer; @@ -23,6 +24,10 @@ public: static std::shared_ptr<int> createInt(int v = 42); static std::shared_ptr<int> createNullInt(); static void printInt(const std::shared_ptr<int> &); + + static std::shared_ptr<std::string> createString(const char *text); + static std::shared_ptr<std::string> createNullString(); + static void printString(const std::shared_ptr<std::string> &); }; class LIB_SMART_API StdSharedPtrVirtualMethodTester diff --git a/sources/shiboken6/tests/otherbinding/typediscovery_test.py b/sources/shiboken6/tests/otherbinding/typediscovery_test.py index 791d3bdce..39dc5cf0f 100644 --- a/sources/shiboken6/tests/otherbinding/typediscovery_test.py +++ b/sources/shiboken6/tests/otherbinding/typediscovery_test.py @@ -13,7 +13,8 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from shiboken_paths import init_paths init_paths() -from sample import Abstract, Base1, Derived +from sample import (Abstract, Base1, Derived, + MDerived1, SonOfMDerived1, MDerived3) from other import OtherMultipleDerived @@ -32,14 +33,18 @@ class TypeDiscoveryTest(unittest.TestCase): def testMultipleInheritance(self): obj = OtherMultipleDerived.createObject("Base1") self.assertEqual(type(obj), Base1) - # PYSIDE-868: In case of multiple inheritance, a factory - # function will return the base class wrapper. + # PYSIDE-868: In case of single line direct inheritance, + # a factory function will return the class wrapper + # of the derived class. obj = OtherMultipleDerived.createObject("MDerived1") - self.assertEqual(type(obj), Base1) + self.assertEqual(type(obj), MDerived1) obj = OtherMultipleDerived.createObject("SonOfMDerived1") - self.assertEqual(type(obj), Base1) + self.assertEqual(type(obj), SonOfMDerived1) obj = OtherMultipleDerived.createObject("MDerived3") - self.assertEqual(type(obj), Base1) + self.assertEqual(type(obj), MDerived3) + # PYSIDE-868: OtherMultipleDerived inherits + # OtherBase, Base1. In this case, a factory + # function will return the base class wrapper. obj = OtherMultipleDerived.createObject("OtherMultipleDerived") self.assertEqual(type(obj), Base1) diff --git a/sources/shiboken6/tests/smartbinding/CMakeLists.txt b/sources/shiboken6/tests/smartbinding/CMakeLists.txt index 2e729321e..c10d7ab6b 100644 --- a/sources/shiboken6/tests/smartbinding/CMakeLists.txt +++ b/sources/shiboken6/tests/smartbinding/CMakeLists.txt @@ -20,6 +20,7 @@ ${CMAKE_CURRENT_BINARY_DIR}/smart/stdsharedptrtestbench_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/smart/stdsharedptrvirtualmethodtester_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_integer_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_int_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_std_string_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/smart/std_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/smart/std_optional_int_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/smart/std_optional_integer_wrapper.cpp diff --git a/sources/shiboken6/tests/smartbinding/std_shared_ptr_test.py b/sources/shiboken6/tests/smartbinding/std_shared_ptr_test.py index 2e6aea3d9..c87a6f3e2 100644 --- a/sources/shiboken6/tests/smartbinding/std_shared_ptr_test.py +++ b/sources/shiboken6/tests/smartbinding/std_shared_ptr_test.py @@ -50,6 +50,13 @@ class StdSharedPtrTests(unittest.TestCase): p = StdSharedPtrTestBench.createInt() StdSharedPtrTestBench.printInt(p) + def testString(self): + np = StdSharedPtrTestBench.createNullString() + StdSharedPtrTestBench.printString(np) + self.assertFalse(np) + p = StdSharedPtrTestBench.createString("bla") + StdSharedPtrTestBench.printString(p) + def testVirtuals(self): """Test whether code generating virtual function overrides is generated correctly.""" diff --git a/sources/shiboken6/tests/smartbinding/typesystem_smart.xml b/sources/shiboken6/tests/smartbinding/typesystem_smart.xml index 261d5f15d..8831629f8 100644 --- a/sources/shiboken6/tests/smartbinding/typesystem_smart.xml +++ b/sources/shiboken6/tests/smartbinding/typesystem_smart.xml @@ -50,7 +50,7 @@ value-check-method="operator bool" ref-count-method="use_count" reset-method="reset" - instantiations="Integer,int"> + instantiations="Integer,int,std::string"> <include file-name="memory" location="global"/> </smart-pointer-type> |