aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6')
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp108
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetatype.cpp1
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp7
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/clangparser.h4
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp5
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/compilersupport.h2
-rw-r--r--sources/shiboken6/ApiExtractor/messages.cpp15
-rw-r--r--sources/shiboken6/ApiExtractor/messages.h4
-rw-r--r--sources/shiboken6/ApiExtractor/typedatabase.cpp3
-rw-r--r--sources/shiboken6/doc/shibokenmodule.rst5
-rw-r--r--sources/shiboken6/doc/typediscovery.rst145
-rw-r--r--sources/shiboken6/doc/typesystem.rst1
-rw-r--r--sources/shiboken6/doc/typesystem_converters.rst58
-rw-r--r--sources/shiboken6/doc/typesystem_specifying_types.rst33
-rw-r--r--sources/shiboken6/generator/generator.cpp5
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator.cpp128
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator.h3
-rw-r--r--sources/shiboken6/generator/shiboken/headergenerator.cpp4
-rw-r--r--sources/shiboken6/libshiboken/basewrapper.cpp292
-rw-r--r--sources/shiboken6/libshiboken/basewrapper.h45
-rw-r--r--sources/shiboken6/libshiboken/basewrapper_p.h105
-rw-r--r--sources/shiboken6/libshiboken/bindingmanager.cpp73
-rw-r--r--sources/shiboken6/libshiboken/bindingmanager.h6
-rw-r--r--sources/shiboken6/libshiboken/pep384impl.cpp70
-rw-r--r--sources/shiboken6/libshiboken/pep384impl.h11
-rw-r--r--sources/shiboken6/libshiboken/sbkcontainer.h66
-rw-r--r--sources/shiboken6/libshiboken/sbkconverter.cpp147
-rw-r--r--sources/shiboken6/libshiboken/sbkconverter_p.h6
-rw-r--r--sources/shiboken6/libshiboken/sbkcpptonumpy.cpp6
-rw-r--r--sources/shiboken6/libshiboken/sbkerrors.cpp47
-rw-r--r--sources/shiboken6/libshiboken/sbkerrors.h5
-rw-r--r--sources/shiboken6/libshiboken/sbkmodule.cpp153
-rw-r--r--sources/shiboken6/libshiboken/sbkmodule.h12
-rw-r--r--sources/shiboken6/libshiboken/sbknumpy.cpp4
-rw-r--r--sources/shiboken6/libshiboken/sbkstring.cpp6
-rw-r--r--sources/shiboken6/libshiboken/sbkstring.h2
-rw-r--r--sources/shiboken6/libshiboken/voidptr.cpp7
-rw-r--r--sources/shiboken6/shibokenmodule/shibokenmodule.cpp4
-rw-r--r--sources/shiboken6/shibokenmodule/typesystem_shiboken.xml5
-rw-r--r--sources/shiboken6/tests/libother/othermultiplederived.h2
-rw-r--r--sources/shiboken6/tests/libsample/derived.h2
-rw-r--r--sources/shiboken6/tests/libsample/point.cpp2
-rw-r--r--sources/shiboken6/tests/libsample/point.h2
-rw-r--r--sources/shiboken6/tests/libsample/pointf.cpp2
-rw-r--r--sources/shiboken6/tests/libsample/pointf.h2
-rw-r--r--sources/shiboken6/tests/libsmart/stdsharedptrtestbench.cpp20
-rw-r--r--sources/shiboken6/tests/libsmart/stdsharedptrtestbench.h5
-rw-r--r--sources/shiboken6/tests/otherbinding/typediscovery_test.py17
-rw-r--r--sources/shiboken6/tests/smartbinding/CMakeLists.txt1
-rw-r--r--sources/shiboken6/tests/smartbinding/std_shared_ptr_test.py7
-rw-r--r--sources/shiboken6/tests/smartbinding/typesystem_smart.xml2
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-&gt;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-&gt;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>