aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/generator
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/generator')
-rw-r--r--sources/shiboken6/generator/CMakeLists.txt121
-rw-r--r--sources/shiboken6/generator/__init__.py.in2
-rw-r--r--sources/shiboken6/generator/_config.py.in10
-rw-r--r--sources/shiboken6/generator/defaultvalue.cpp120
-rw-r--r--sources/shiboken6/generator/defaultvalue.h46
-rw-r--r--sources/shiboken6/generator/generator.cpp700
-rw-r--r--sources/shiboken6/generator/generator.h233
-rw-r--r--sources/shiboken6/generator/generatorcontext.cpp38
-rw-r--r--sources/shiboken6/generator/generatorcontext.h56
-rw-r--r--sources/shiboken6/generator/main.cpp435
-rw-r--r--sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp1591
-rw-r--r--sources/shiboken6/generator/qtdoc/qtdocgenerator.h130
-rw-r--r--sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp1643
-rw-r--r--sources/shiboken6/generator/qtdoc/qtxmltosphinx.h216
-rw-r--r--sources/shiboken6/generator/qtdoc/qtxmltosphinxinterface.h68
-rw-r--r--sources/shiboken6/generator/qtdoc/rstformat.h99
-rw-r--r--sources/shiboken6/generator/shiboken/configurablescope.h33
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator.cpp6819
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator.h565
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator_container.cpp272
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp476
-rw-r--r--sources/shiboken6/generator/shiboken/ctypenames.h31
-rw-r--r--sources/shiboken6/generator/shiboken/generatorargument.cpp110
-rw-r--r--sources/shiboken6/generator/shiboken/generatorargument.h60
-rw-r--r--sources/shiboken6/generator/shiboken/generatorstrings.h39
-rw-r--r--sources/shiboken6/generator/shiboken/headergenerator.cpp961
-rw-r--r--sources/shiboken6/generator/shiboken/headergenerator.h76
-rw-r--r--sources/shiboken6/generator/shiboken/overloaddata.cpp1011
-rw-r--r--sources/shiboken6/generator/shiboken/overloaddata.h183
-rw-r--r--sources/shiboken6/generator/shiboken/pytypenames.h29
-rw-r--r--sources/shiboken6/generator/shiboken/shibokengenerator.cpp2650
-rw-r--r--sources/shiboken6/generator/shiboken/shibokengenerator.h491
-rw-r--r--sources/shiboken6/generator/shibokenconfig.h.in6
33 files changed, 19320 insertions, 0 deletions
diff --git a/sources/shiboken6/generator/CMakeLists.txt b/sources/shiboken6/generator/CMakeLists.txt
new file mode 100644
index 000000000..aebe2cd5e
--- /dev/null
+++ b/sources/shiboken6/generator/CMakeLists.txt
@@ -0,0 +1,121 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+project(shibokengenerator)
+set(package_name "Shiboken6Tools")
+
+set(CMAKE_AUTOMOC ON)
+
+if(NOT (Qt${QT_MAJOR_VERSION}Core_FOUND AND Python_Interpreter_FOUND))
+ message(WARNING "Some dependencies were not found: shiboken6 generator compilation disabled!")
+ return()
+endif()
+
+set(shiboken6_SRC
+defaultvalue.cpp defaultvalue.h
+generator.cpp generator.h
+generatorcontext.cpp generatorcontext.h
+main.cpp
+shiboken/configurablescope.h
+shiboken/cppgenerator.cpp shiboken/cppgenerator.h
+shiboken/cppgenerator_container.cpp
+shiboken/cppgenerator_smartpointer.cpp
+shiboken/ctypenames.h
+shiboken/generatorargument.cpp shiboken/generatorargument.h shiboken/generatorstrings.h
+shiboken/headergenerator.cpp shiboken/headergenerator.h
+shiboken/overloaddata.cpp shiboken/overloaddata.h
+shiboken/pytypenames.h
+shiboken/shibokengenerator.cpp shiboken/shibokengenerator.h
+)
+
+find_libclang()
+
+if(${STANDALONE})
+ list(APPEND CMAKE_INSTALL_RPATH ${base}/Qt/lib)
+else()
+ list(APPEND CMAKE_INSTALL_RPATH ${QT6_INSTALL_PREFIX}/${QT6_INSTALL_LIBS}
+ ${libclang_lib_dir})
+endif()
+
+add_executable(shiboken6 ${shiboken6_SRC})
+add_executable(Shiboken6::shiboken6 ALIAS shiboken6)
+add_dependencies(shiboken6 apiextractor)
+
+set_target_properties(shiboken6 PROPERTIES OUTPUT_NAME shiboken6${shiboken6_SUFFIX})
+target_include_directories(shiboken6 PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}/shiboken
+ ${CMAKE_CURRENT_SOURCE_DIR}/qtdoc
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${apiextractor_SOURCE_DIR}
+ )
+target_link_libraries(shiboken6 apiextractor Qt::Core)
+if (NOT DISABLE_DOCSTRINGS)
+ target_sources(shiboken6 PRIVATE
+ qtdoc/qtdocgenerator.cpp qtdoc/qtdocgenerator.h
+ qtdoc/qtxmltosphinx.cpp qtdoc/qtxmltosphinx.h
+ qtdoc/qtxmltosphinxinterface.h
+ qtdoc/rstformat.h)
+ target_compile_definitions(shiboken6 PUBLIC DOCSTRINGS_ENABLED QT_LEAN_HEADERS=1)
+endif()
+
+configure_file(shibokenconfig.h.in "${CMAKE_CURRENT_BINARY_DIR}/shibokenconfig.h" @ONLY)
+
+install(TARGETS shiboken6
+ EXPORT "${package_name}Targets"
+ DESTINATION "bin")
+install(EXPORT "${package_name}Targets"
+ NAMESPACE "Shiboken6::"
+ DESTINATION ${LIB_INSTALL_DIR}/cmake/${package_name})
+
+set(shiboken_generator_package_name "shiboken6_generator")
+
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/_config.py.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/_config.py" @ONLY)
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_config.py"
+ DESTINATION "${PYTHON_SITE_PACKAGES}/${shiboken_generator_package_name}")
+
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/__init__.py" @ONLY)
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/__init__.py"
+ DESTINATION "${PYTHON_SITE_PACKAGES}/${shiboken_generator_package_name}")
+
+# shiboken6 setuptools entry point
+install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../shiboken_tool.py
+ DESTINATION bin
+ PERMISSIONS
+ OWNER_EXECUTE OWNER_WRITE OWNER_READ
+ GROUP_EXECUTE GROUP_READ
+ WORLD_EXECUTE WORLD_READ)
+
+# Use absolute path instead of relative path, to avoid ninja build errors due to
+# duplicate file dependency inconsistency.
+set(shiboken_version_relative_path "${CMAKE_CURRENT_SOURCE_DIR}/../shiboken_version.py")
+get_filename_component(shiboken_version_path ${shiboken_version_relative_path} ABSOLUTE)
+configure_file("${shiboken_version_path}"
+ "${CMAKE_CURRENT_BINARY_DIR}/_git_shiboken_generator_version.py" @ONLY)
+
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_git_shiboken_generator_version.py"
+ DESTINATION "${PYTHON_SITE_PACKAGES}/${shiboken_generator_package_name}")
+
+include(CMakePackageConfigHelpers)
+
+# Single build-tree and install-tree Config file. There's no need for separate ones because we
+# don't specify any PATH_VARS, so the relative path of PACKAGE_PREFIX_DIR does not really matter.
+configure_package_config_file(
+ "${CMAKE_CURRENT_SOURCE_DIR}/../data/${package_name}Config.cmake.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/${package_name}Config.cmake"
+ INSTALL_DESTINATION "${LIB_INSTALL_DIR}/cmake/${package_name}"
+)
+write_basic_package_version_file(
+ "${CMAKE_CURRENT_BINARY_DIR}/${package_name}ConfigVersion.cmake"
+ VERSION "${shiboken6_VERSION}"
+ COMPATIBILITY AnyNewerVersion
+ ARCH_INDEPENDENT
+)
+
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${package_name}Config.cmake"
+ DESTINATION "${LIB_INSTALL_DIR}/cmake/${package_name}")
+
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${package_name}ConfigVersion.cmake"
+ DESTINATION "${LIB_INSTALL_DIR}/cmake/${package_name}")
diff --git a/sources/shiboken6/generator/__init__.py.in b/sources/shiboken6/generator/__init__.py.in
new file mode 100644
index 000000000..4be6a833b
--- /dev/null
+++ b/sources/shiboken6/generator/__init__.py.in
@@ -0,0 +1,2 @@
+__version__ = "@FINAL_PACKAGE_VERSION@"
+__version_info__ = (@shiboken_MAJOR_VERSION@, @shiboken_MINOR_VERSION@, @shiboken_MICRO_VERSION@, "@shiboken_PRE_RELEASE_VERSION_TYPE@", "@shiboken_PRE_RELEASE_VERSION@")
diff --git a/sources/shiboken6/generator/_config.py.in b/sources/shiboken6/generator/_config.py.in
new file mode 100644
index 000000000..ed7e67098
--- /dev/null
+++ b/sources/shiboken6/generator/_config.py.in
@@ -0,0 +1,10 @@
+version = "@FINAL_PACKAGE_VERSION@"
+version_info = (@shiboken_MAJOR_VERSION@, @shiboken_MINOR_VERSION@, @shiboken_MICRO_VERSION@, "@shiboken_PRE_RELEASE_VERSION_TYPE@", "@shiboken_PRE_RELEASE_VERSION@")
+
+@PACKAGE_BUILD_DATE@
+@PACKAGE_BUILD_COMMIT_DATE@
+@PACKAGE_BUILD_COMMIT_HASH@
+@PACKAGE_BUILD_COMMIT_HASH_DESCRIBED@
+@PACKAGE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT@
+@PACKAGE_SETUP_PY_PACKAGE_VERSION_ASSIGNMENT@
+@QT_MACOS_DEPLOYMENT_TARGET@
diff --git a/sources/shiboken6/generator/defaultvalue.cpp b/sources/shiboken6/generator/defaultvalue.cpp
new file mode 100644
index 000000000..89cc9fa77
--- /dev/null
+++ b/sources/shiboken6/generator/defaultvalue.cpp
@@ -0,0 +1,120 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "defaultvalue.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QDebug>
+
+using namespace Qt::StringLiterals;
+
+// DefaultValue is used for storing default values of types for which code is
+// generated in different contexts:
+//
+// Context | Example: "Class *" | Example: "Class" with default Constructor
+// --------------------+-------------------------------+------------------------------------------
+// Variable | var{nullptr}; | var;
+// initializations | |
+// --------------------+-------------------------------+------------------------------------------
+// Return values | return nullptr; | return {}
+// --------------------+-------------------------------+------------------------------------------
+// constructor | static_cast<Class *>(nullptr) | Class()
+// arguments lists | |
+// (recursive, precise | |
+// matching). | |
+
+DefaultValue::DefaultValue(Type t, QString value) :
+ m_type(t), m_value(std::move(value))
+{
+}
+
+DefaultValue::DefaultValue(QString customValue) :
+ m_type(Custom), m_value(std::move(customValue))
+{
+}
+
+QString DefaultValue::returnValue() const
+{
+ switch (m_type) {
+ case DefaultValue::Boolean:
+ return u"false"_s;
+ case DefaultValue::CppScalar:
+ return u"0"_s;
+ case DefaultValue::Custom:
+ case DefaultValue::Enum:
+ return m_value;
+ case DefaultValue::Pointer:
+ return u"nullptr"_s;
+ case DefaultValue::Void:
+ return {};
+ case DefaultValue::DefaultConstructorWithDefaultValues:
+ return m_value + u"()"_s;
+ case DefaultValue::DefaultConstructor:
+ break;
+ }
+ return u"{}"_s;
+}
+
+QString DefaultValue::initialization() const
+{
+ switch (m_type) {
+ case DefaultValue::Boolean:
+ return u"{false}"_s;
+ case DefaultValue::CppScalar:
+ return u"{0}"_s;
+ case DefaultValue::Custom:
+ return u" = "_s + m_value;
+ case DefaultValue::Enum:
+ return u'{' + m_value + u'}';
+ case DefaultValue::Pointer:
+ return u"{nullptr}"_s;
+ case DefaultValue::Void:
+ Q_ASSERT(false);
+ break;
+ case DefaultValue::DefaultConstructor:
+ case DefaultValue::DefaultConstructorWithDefaultValues:
+ break;
+ }
+ return {};
+}
+
+QString DefaultValue::constructorParameter() const
+{
+ switch (m_type) {
+ case DefaultValue::Boolean:
+ return u"false"_s;
+ case DefaultValue::CppScalar: {
+ // PYSIDE-846: Use static_cast in case of "unsigned long" and similar
+ const QString cast = m_value.contains(u' ')
+ ? u"static_cast<"_s + m_value + u'>'
+ : m_value;
+ return cast + u"(0)"_s;
+ }
+ case DefaultValue::Custom:
+ case DefaultValue::Enum:
+ return m_value;
+ case DefaultValue::Pointer:
+ // Be precise here to be able to differentiate between constructors
+ // taking different pointer types, cf
+ // QTreeWidgetItemIterator(QTreeWidget *) and
+ // QTreeWidgetItemIterator(QTreeWidgetItemIterator *).
+ return u"static_cast<"_s + m_value + u"*>(nullptr)"_s;
+ case DefaultValue::Void:
+ Q_ASSERT(false);
+ break;
+ case DefaultValue::DefaultConstructor:
+ case DefaultValue::DefaultConstructorWithDefaultValues:
+ break;
+ }
+ return m_value + u"()"_s;
+}
+
+QDebug operator<<(QDebug debug, const DefaultValue &v)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "DefaultValue(" << v.type() << ", \"" << v.value() << "\")";
+ return debug;
+}
diff --git a/sources/shiboken6/generator/defaultvalue.h b/sources/shiboken6/generator/defaultvalue.h
new file mode 100644
index 000000000..d518d134f
--- /dev/null
+++ b/sources/shiboken6/generator/defaultvalue.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef DEFAULTVALUE_H
+#define DEFAULTVALUE_H
+
+#include <QtCore/QString>
+
+QT_FORWARD_DECLARE_CLASS(QDebug);
+
+class DefaultValue
+{
+public:
+ enum Type
+ {
+ Boolean,
+ CppScalar, // A C++ scalar type (int,..) specified by value()
+ Custom, // A custom constructor/expression, uses value() as is
+ DefaultConstructor, // For classes named value()
+ DefaultConstructorWithDefaultValues, // as DefaultConstructor, but can't return {} though.
+ Enum, // Enum value as specified by value()
+ Pointer, // Pointer of type value()
+ Void // "", for return values only
+ };
+
+ explicit DefaultValue(Type t, QString value = QString());
+ explicit DefaultValue(QString customValue);
+
+ QString returnValue() const;
+ QString initialization() const;
+ QString constructorParameter() const;
+
+ QString value() const { return m_value; }
+ void setValue(const QString &value) { m_value = value; }
+
+ Type type() const { return m_type; }
+ void setType(Type type) { m_type = type; }
+
+private:
+ Type m_type;
+ QString m_value;
+};
+
+QDebug operator<<(QDebug debug, const DefaultValue &v);
+
+#endif // DEFAULTVALUE_H
diff --git a/sources/shiboken6/generator/generator.cpp b/sources/shiboken6/generator/generator.cpp
new file mode 100644
index 000000000..a01326530
--- /dev/null
+++ b/sources/shiboken6/generator/generator.cpp
@@ -0,0 +1,700 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "generator.h"
+#include "defaultvalue.h"
+#include "generatorcontext.h"
+#include "apiextractorresult.h"
+#include "abstractmetaargument.h"
+#include "abstractmetaenum.h"
+#include "abstractmetafunction.h"
+#include "abstractmetalang.h"
+#include "messages.h"
+#include <optionsparser.h>
+#include "reporthandler.h"
+#include "fileout.h"
+#include "arraytypeentry.h"
+#include "enumtypeentry.h"
+#include "enumvaluetypeentry.h"
+#include "namespacetypeentry.h"
+#include "primitivetypeentry.h"
+#include "typesystemtypeentry.h"
+#include <typedatabase.h>
+
+#include "qtcompat.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QRegularExpression>
+
+using namespace Qt::StringLiterals;
+
+static constexpr auto ENABLE_PYSIDE_EXTENSIONS = "enable-pyside-extensions"_L1;
+static constexpr auto AVOID_PROTECTED_HACK = "avoid-protected-hack"_L1;
+
+struct GeneratorOptions
+{
+ bool usePySideExtensions = false;
+ bool avoidProtectedHack = false;
+};
+
+struct Generator::GeneratorPrivate
+{
+ ApiExtractorResult api;
+ QString outDir;
+ // License comment
+ QString licenseComment;
+ AbstractMetaClassCList m_invisibleTopNamespaces;
+ bool m_hasPrivateClasses = false;
+ static GeneratorOptions m_options;
+};
+
+GeneratorOptions Generator::GeneratorPrivate::m_options;
+
+// Kept as a variable for a potential Qt-in-namespace support
+QString Generator::m_gsp = "::"_L1;
+
+Generator::Generator() : m_d(new GeneratorPrivate)
+{
+}
+
+Generator::~Generator()
+{
+ delete m_d;
+}
+
+bool Generator::setup(const ApiExtractorResult &api)
+{
+ m_d->api = api;
+ const auto moduleEntry = TypeDatabase::instance()->defaultTypeSystemType();
+ if (!moduleEntry) {
+ qCWarning(lcShiboken,"Couldn't find the package name!!");
+ return false;
+ }
+ if (!moduleEntry->generateCode()) {
+ qCWarning(lcShiboken, "Code generation of root typesystem is disabled!!");
+ return false;
+ }
+
+ for (const auto &c : api.classes()) {
+ if (c->enclosingClass() == nullptr && c->isInvisibleNamespace()) {
+ m_d->m_invisibleTopNamespaces.append(c);
+ c->invisibleNamespaceRecursion([&](const AbstractMetaClassCPtr &ic) {
+ m_d->m_invisibleTopNamespaces.append(ic);
+ });
+ }
+ }
+
+ return doSetup();
+}
+
+QList<OptionDescription> Generator::options()
+{
+ return {
+ {AVOID_PROTECTED_HACK,
+ u"Avoid the use of the '#define protected public' hack."_s},
+ {ENABLE_PYSIDE_EXTENSIONS,
+ u"Enable PySide extensions, such as support for signal/slots,\n"
+ "use this if you are creating a binding for a Qt-based library."_s}
+ };
+}
+
+class GeneratorOptionsParser : public OptionsParser
+{
+public:
+ explicit GeneratorOptionsParser(GeneratorOptions *o) : m_options(o) {}
+
+ bool handleBoolOption(const QString &key, OptionSource source) override;
+
+private:
+ GeneratorOptions *m_options;
+};
+
+bool GeneratorOptionsParser::handleBoolOption(const QString & key, OptionSource source)
+{
+ if (source == OptionSource::CommandLineSingleDash)
+ return false;
+ if (key == ENABLE_PYSIDE_EXTENSIONS)
+ return ( m_options->usePySideExtensions = true);
+ if (key == AVOID_PROTECTED_HACK)
+ return ( m_options->avoidProtectedHack = true);
+ return false;
+}
+
+std::shared_ptr<OptionsParser> Generator::createOptionsParser()
+{
+ return std::make_shared<GeneratorOptionsParser>(&GeneratorPrivate::m_options);
+}
+
+QString Generator::fileNameForContextHelper(const GeneratorContext &context,
+ const QString &suffix,
+ FileNameFlags flags)
+
+{
+ if (!context.forSmartPointer()) {
+ const auto metaClass = context.metaClass();
+ QString fileNameBase = flags.testFlag(FileNameFlag::UnqualifiedName)
+ ? metaClass->name() : metaClass->qualifiedCppName();
+ if (!flags.testFlag(FileNameFlag::KeepCase))
+ fileNameBase = fileNameBase.toLower();
+ fileNameBase.replace(u"::"_s, u"_"_s);
+ return fileNameBase + suffix;
+ }
+
+ // FIXME: PYSIDE7: Use the above code path for all types. Note the file
+ // names will then change to reflect the namespaces of the pointee
+ // (smart/integer2).
+ const AbstractMetaType &smartPointerType = context.preciseType();
+ QString fileNameBase = getFileNameBaseForSmartPointer(smartPointerType);
+ return fileNameBase + suffix;
+}
+
+const AbstractMetaClassCList &Generator::invisibleTopNamespaces() const
+{
+ return m_d->m_invisibleTopNamespaces;
+}
+
+PrimitiveTypeEntryCList Generator::primitiveTypes()
+{
+ return TypeDatabase::instance()->primitiveTypes();
+}
+
+ContainerTypeEntryCList Generator::containerTypes()
+{
+ return TypeDatabase::instance()->containerTypes();
+}
+
+QString Generator::licenseComment() const
+{
+ return m_d->licenseComment;
+}
+
+void Generator::setLicenseComment(const QString &licenseComment)
+{
+ m_d->licenseComment = licenseComment;
+}
+
+QString Generator::packageName()
+{
+ return TypeDatabase::instance()->defaultPackageName();
+}
+
+static QString getModuleName()
+{
+ QString result = TypeDatabase::instance()->defaultPackageName();
+ result.remove(0, result.lastIndexOf(u'.') + 1);
+ return result;
+}
+
+QString Generator::moduleName()
+{
+ static const QString result = getModuleName();
+ return result;
+}
+
+QString Generator::outputDirectory() const
+{
+ return m_d->outDir;
+}
+
+void Generator::setOutputDirectory(const QString &outDir)
+{
+ m_d->outDir = outDir;
+}
+
+bool Generator::generateFileForContext(const GeneratorContext &context)
+{
+ const auto cls = context.metaClass();
+ auto typeEntry = cls->typeEntry();
+
+ if (!shouldGenerate(typeEntry))
+ return true;
+
+ const QString fileName = fileNameForContext(context);
+ if (fileName.isEmpty())
+ return true;
+
+ QString filePath = outputDirectory() + u'/'
+ + subDirectoryForPackage(typeEntry->targetLangPackage())
+ + u'/' + fileName;
+ FileOut fileOut(filePath);
+
+ generateClass(fileOut.stream, context);
+
+ fileOut.done();
+ return true;
+}
+
+QString Generator::getFileNameBaseForSmartPointer(const AbstractMetaType &smartPointerType)
+{
+ const AbstractMetaType innerType = smartPointerType.getSmartPointerInnerType();
+ smartPointerType.typeEntry()->qualifiedCppName();
+ QString fileName = smartPointerType.typeEntry()->qualifiedCppName().toLower();
+ fileName.append(u'_');
+ fileName.append(innerType.name().toLower());
+ fileName.replace(u"::"_s, u"_"_s); // std::shared_ptr<std::string>
+ return fileName;
+}
+
+GeneratorContext Generator::contextForClass(const AbstractMetaClassCPtr &c) const
+{
+ GeneratorContext result;
+ result.m_metaClass = c;
+ return result;
+}
+
+GeneratorContext
+ Generator::contextForSmartPointer(const AbstractMetaClassCPtr &c,
+ const AbstractMetaType &t,
+ const AbstractMetaClassCPtr &pointeeClass)
+{
+ GeneratorContext result;
+ result.m_metaClass = c;
+ result.m_preciseClassType = t;
+ result.m_type = GeneratorContext::SmartPointer;
+ result.m_pointeeClass = pointeeClass;
+ return result;
+}
+
+bool Generator::generate()
+{
+ for (const auto &cls : m_d->api.classes()) {
+ if (!generateFileForContext(contextForClass(cls)))
+ return false;
+ auto te = cls->typeEntry();
+ if (shouldGenerate(te) && te->isPrivate())
+ m_d->m_hasPrivateClasses = true;
+ }
+
+ for (const auto &smp: m_d->api.instantiatedSmartPointers()) {
+ AbstractMetaClassCPtr pointeeClass;
+ const auto instantiatedType = smp.type.instantiations().constFirst().typeEntry();
+ if (instantiatedType->isComplex()) // not a C++ primitive
+ pointeeClass = AbstractMetaClass::findClass(m_d->api.classes(), instantiatedType);
+ if (!generateFileForContext(contextForSmartPointer(smp.specialized, smp.type,
+ pointeeClass))) {
+ return false;
+ }
+ }
+ return finishGeneration();
+}
+
+bool Generator::shouldGenerate(const TypeEntryCPtr &typeEntry) const
+{
+ return typeEntry->shouldGenerate();
+}
+
+const ApiExtractorResult &Generator::api() const
+{
+ return m_d->api;
+}
+
+bool Generator::hasPrivateClasses() const
+{
+ return m_d->m_hasPrivateClasses;
+}
+
+bool Generator::usePySideExtensions()
+{
+ return GeneratorPrivate::m_options.usePySideExtensions;
+}
+
+bool Generator::avoidProtectedHack()
+{
+ return GeneratorPrivate::m_options.avoidProtectedHack;
+}
+
+QString Generator::getFullTypeName(TypeEntryCPtr type)
+{
+ QString result = type->qualifiedCppName();
+ if (type->isArray())
+ type = std::static_pointer_cast<const ArrayTypeEntry>(type)->nestedTypeEntry();
+ return isCppPrimitive(type) ? result : addGlobalScopePrefix(result);
+}
+
+QString Generator::getFullTypeName(const AbstractMetaType &type)
+{
+ if (type.isCString())
+ return u"const char*"_s;
+ if (type.isVoidPointer())
+ return u"void*"_s;
+ if (type.typeEntry()->isContainer())
+ return addGlobalScopePrefix(type.cppSignature());
+ QString typeName;
+ if (type.typeEntry()->isComplex() && type.hasInstantiations())
+ typeName = getFullTypeNameWithoutModifiers(type);
+ else
+ typeName = getFullTypeName(type.typeEntry());
+ return typeName + QString::fromLatin1("*").repeated(type.indirections());
+}
+
+QString Generator::getFullTypeName(const AbstractMetaClassCPtr &metaClass)
+{
+ const QString &qualName = metaClass->qualifiedCppName();
+ // Typedefs are generated into the global namespace
+ return metaClass->isTypeDef() ? qualName : addGlobalScopePrefix(qualName);
+}
+
+QString Generator::getFullTypeNameWithoutModifiers(const AbstractMetaType &type)
+{
+ if (type.isCString())
+ return u"const char*"_s;
+ if (type.isVoidPointer())
+ return u"void*"_s;
+ if (!type.hasInstantiations())
+ return getFullTypeName(type.typeEntry());
+ QString typeName = type.cppSignature();
+ if (type.isConstant())
+ typeName.remove(0, sizeof("const ") / sizeof(char) - 1);
+ switch (type.referenceType()) {
+ case NoReference:
+ break;
+ case LValueReference:
+ typeName.chop(1);
+ break;
+ case RValueReference:
+ typeName.chop(2);
+ break;
+ }
+ while (typeName.endsWith(u'*') || typeName.endsWith(u' '))
+ typeName.chop(1);
+ return addGlobalScopePrefix(typeName);
+}
+
+std::optional<DefaultValue>
+ Generator::minimalConstructor(const ApiExtractorResult &api,
+ const AbstractMetaType &type,
+ QString *errorString)
+{
+ if (type.referenceType() == LValueReference && type.isObjectType())
+ return {};
+
+ if (type.isContainer()) {
+ QString ctor = type.cppSignature();
+ if (ctor.endsWith(u'*')) {
+ ctor.chop(1);
+ return DefaultValue(DefaultValue::Pointer, ctor.trimmed());
+ }
+ if (ctor.startsWith(u"const "))
+ ctor.remove(0, sizeof("const ") / sizeof(char) - 1);
+ if (ctor.endsWith(u'&')) {
+ ctor.chop(1);
+ ctor = ctor.trimmed();
+ }
+ return DefaultValue(DefaultValue::DefaultConstructor, u"::"_s + ctor);
+ }
+
+ if (type.isNativePointer())
+ return DefaultValue(DefaultValue::Pointer, type.typeEntry()->qualifiedCppName());
+ if (type.isPointer())
+ return DefaultValue(DefaultValue::Pointer, getFullTypeName(type.typeEntry()));
+
+ if (type.typeEntry()->isSmartPointer())
+ return minimalConstructor(api, type.typeEntry());
+
+ if (type.typeEntry()->isComplex()) {
+ auto cType = std::static_pointer_cast<const ComplexTypeEntry>(type.typeEntry());
+ if (cType->hasDefaultConstructor())
+ return DefaultValue(DefaultValue::Custom, cType->defaultConstructor());
+ auto klass = AbstractMetaClass::findClass(api.classes(), cType);
+ if (!klass) {
+ if (errorString != nullptr)
+ *errorString = msgClassNotFound(cType);
+ return {};
+ }
+ auto ctorO = minimalConstructor(api, klass);
+ if (ctorO.has_value() && type.hasInstantiations()) {
+ auto ctor = ctorO.value();
+ QString v = ctor.value();
+ v.replace(getFullTypeName(cType), getFullTypeNameWithoutModifiers(type));
+ ctor.setValue(v);
+ return ctor;
+ }
+ return ctorO;
+ }
+
+ return minimalConstructor(api, type.typeEntry(), errorString);
+}
+
+std::optional<DefaultValue>
+ Generator::minimalConstructor(const ApiExtractorResult &api,
+ const TypeEntryCPtr &type,
+ QString *errorString)
+{
+ if (!type)
+ return {};
+
+ if (isCppPrimitive(type)) {
+ const QString &name = type->qualifiedCppName();
+ return name == u"bool"
+ ? DefaultValue(DefaultValue::Boolean)
+ : DefaultValue(DefaultValue::CppScalar, name);
+ }
+
+ if (type->isEnum()) {
+ const auto enumEntry = std::static_pointer_cast<const EnumTypeEntry>(type);
+ if (const auto nullValue = enumEntry->nullValue())
+ return DefaultValue(DefaultValue::Enum, nullValue->name());
+ return DefaultValue(DefaultValue::Custom,
+ "static_cast< "_L1 + getFullTypeName(type) + ">(0)"_L1);
+ }
+
+ if (type->isFlags()) {
+ return DefaultValue(DefaultValue::Custom,
+ type->qualifiedCppName() + u"(0)"_s);
+ }
+
+ if (type->isPrimitive()) {
+ QString ctor = std::static_pointer_cast<const PrimitiveTypeEntry>(type)->defaultConstructor();
+ // If a non-C++ (i.e. defined by the user) primitive type does not have
+ // a default constructor defined by the user, the empty constructor is
+ // heuristically returned. If this is wrong the build of the generated
+ // bindings will tell.
+ return ctor.isEmpty()
+ ? DefaultValue(DefaultValue::DefaultConstructorWithDefaultValues, u"::"_s
+ + type->qualifiedCppName())
+ : DefaultValue(DefaultValue::Custom, ctor);
+ }
+
+ if (type->isSmartPointer())
+ return DefaultValue(DefaultValue::DefaultConstructor, type->qualifiedCppName());
+
+ if (type->isComplex()) {
+ auto klass = AbstractMetaClass::findClass(api.classes(), type);
+ if (!klass) {
+ if (errorString != nullptr)
+ *errorString = msgClassNotFound(type);
+ return {};
+ }
+ return minimalConstructor(api, klass, errorString);
+ }
+
+ if (errorString != nullptr)
+ *errorString = u"No default value could be determined."_s;
+ return {};
+}
+
+static QString constructorCall(const QString &qualifiedCppName, const QStringList &args)
+{
+ return u"::"_s + qualifiedCppName + u'('
+ + args.join(u", "_s) + u')';
+}
+
+std::optional<DefaultValue>
+ Generator::minimalConstructor(const ApiExtractorResult &api,
+ const AbstractMetaClassCPtr &metaClass,
+ QString *errorString)
+{
+ if (!metaClass)
+ return {};
+
+ auto cType = std::static_pointer_cast<const ComplexTypeEntry>(metaClass->typeEntry());
+ if (cType->hasDefaultConstructor())
+ return DefaultValue(DefaultValue::Custom, cType->defaultConstructor());
+
+ const QString qualifiedCppName = cType->qualifiedCppName();
+ // Obtain a list of constructors sorted by complexity and number of arguments
+ QMultiMap<int, const AbstractMetaFunctionCPtr> candidates;
+ const auto &constructors = metaClass->queryFunctions(FunctionQueryOption::Constructors);
+ for (const auto &ctor : constructors) {
+ if (!ctor->isUserAdded() && !ctor->isPrivate()
+ && (ctor->isPublic() || !api.flags().testFlag(ApiExtractorFlag::AvoidProtectedHack))) {
+ // No arguments: Default constructible
+ const auto &arguments = ctor->arguments();
+ if (arguments.isEmpty()) {
+ return DefaultValue(DefaultValue::DefaultConstructor,
+ u"::"_s + qualifiedCppName);
+ }
+ // First argument has unmodified default: Default constructible with values
+ if (arguments.constFirst().hasUnmodifiedDefaultValueExpression()) {
+ return DefaultValue(DefaultValue::DefaultConstructorWithDefaultValues,
+ u"::"_s + qualifiedCppName);
+ }
+ // Examine arguments, exclude functions taking a self parameter
+ bool simple = true;
+ bool suitable = true;
+ for (qsizetype i = 0, size = arguments.size();
+ suitable && i < size && !arguments.at(i).hasOriginalDefaultValueExpression(); ++i) {
+ const AbstractMetaArgument &arg = arguments.at(i);
+ TypeEntryCPtr aType = arg.type().typeEntry();
+ suitable &= aType != cType;
+ simple &= isCppPrimitive(aType) || aType->isEnum() || arg.type().isPointer();
+ }
+ if (suitable)
+ candidates.insert(arguments.size() + (simple ? 0 : 100), ctor);
+ }
+ }
+
+ for (auto it = candidates.cbegin(), end = candidates.cend(); it != end; ++it) {
+ const AbstractMetaArgumentList &arguments = it.value()->arguments();
+ QStringList args;
+ for (const auto &arg : arguments) {
+ if (arg.hasModifiedDefaultValueExpression()) {
+ args << arg.defaultValueExpression(); // Spell out modified values
+ break;
+ }
+ if (arg.hasOriginalDefaultValueExpression())
+ break;
+ auto argValue = minimalConstructor(api, arg.type(), errorString);
+ if (!argValue.has_value())
+ return {};
+ args << argValue->constructorParameter();
+ }
+ return DefaultValue(DefaultValue::Custom, constructorCall(qualifiedCppName, args));
+ }
+
+ return {};
+}
+
+QString Generator::translateType(AbstractMetaType cType,
+ const AbstractMetaClassCPtr &context,
+ Options options) const
+{
+ QString s;
+
+ if (context &&
+ context->typeEntry()->isGenericClass() &&
+ cType.originalTemplateType()) {
+ cType = *cType.originalTemplateType();
+ }
+
+ if (cType.isVoid()) {
+ s = u"void"_s;
+ } else if (cType.isArray()) {
+ s = translateType(*cType.arrayElementType(), context, options) + u"[]"_s;
+ } else {
+ AbstractMetaType copyType = cType;
+ if (options & Generator::ExcludeConst || options & Generator::ExcludeReference) {
+ if (options & Generator::ExcludeConst)
+ copyType.setConstant(false);
+ if (options & Generator::ExcludeReference)
+ copyType.setReferenceType(NoReference);
+ }
+
+ s = copyType.cppSignature();
+ const auto te = copyType.typeEntry();
+ if (!te->isVoid() && !isCppPrimitive(te)) { // Add scope resolution
+ const auto pos = s.indexOf(te->qualifiedCppName()); // Skip const/volatile
+ Q_ASSERT(pos >= 0);
+ s.insert(pos, u"::"_s);
+ }
+ }
+
+ return s;
+}
+
+static const QHash<QString, QString> &pythonOperators()
+{
+ static const QHash<QString, QString> result = {
+ // call operator
+ {u"operator()"_s, u"__call__"_s},
+ // Arithmetic operators
+ {u"operator+"_s, u"__add__"_s},
+ {u"operator-"_s, u"__sub__"_s},
+ {u"operator*"_s, u"__mul__"_s},
+ {u"operator/"_s, u"__div__"_s},
+ {u"operator%"_s, u"__mod__"_s},
+ // Inplace arithmetic operators
+ {u"operator+="_s, u"__iadd__"_s},
+ {u"operator-="_s, u"__isub__"_s},
+ {u"operator++"_s, u"__iadd__"_s},
+ {u"operator--"_s, u"__isub__"_s},
+ {u"operator*="_s, u"__imul__"_s},
+ {u"operator%="_s, u"__imod__"_s},
+ // Bitwise operators
+ {u"operator&"_s, u"__and__"_s},
+ {u"operator^"_s, u"__xor__"_s},
+ {u"operator|"_s, u"__or__"_s},
+ {u"operator<<"_s, u"__lshift__"_s},
+ {u"operator>>"_s, u"__rshift__"_s},
+ {u"operator~"_s, u"__invert__"_s},
+ // Inplace bitwise operators
+ {u"operator&="_s, u"__iand__"_s},
+ {u"operator^="_s, u"__ixor__"_s},
+ {u"operator|="_s, u"__ior__"_s},
+ {u"operator<<="_s, u"__ilshift__"_s},
+ {u"operator>>="_s, u"__irshift__"_s},
+ // Comparison operators
+ {u"operator=="_s, u"__eq__"_s},
+ {u"operator!="_s, u"__ne__"_s},
+ {u"operator<"_s, u"__lt__"_s},
+ {u"operator>"_s, u"__gt__"_s},
+ {u"operator<="_s, u"__le__"_s},
+ {u"operator>="_s, u"__ge__"_s},
+ // Conversion (note bool has special handling with heuristics)
+ {u"operator int"_s, u"__int__"_s},
+ {u"operator double"_s, u"__float__"_s}
+ };
+ return result;
+}
+
+QString Generator::pythonOperatorFunctionName(const QString &cppOpFuncName)
+{
+ return pythonOperators().value(cppOpFuncName);
+}
+
+bool Generator::isPythonOperatorFunctionName(const QString &cppOpFuncName)
+{
+ return pythonOperators().contains(cppOpFuncName);
+}
+
+QString Generator::subDirectoryForPackage(QString packageNameIn) const
+{
+ if (packageNameIn.isEmpty())
+ packageNameIn = packageName();
+ packageNameIn.replace(u'.', QDir::separator());
+ return packageNameIn;
+}
+
+QString Generator::addGlobalScopePrefix(const QString &t)
+{
+ return t.startsWith("std::"_L1) ? t : m_gsp + t;
+}
+
+QString Generator::globalScopePrefix(const GeneratorContext &classContext)
+{
+ return classContext.useWrapper() ? QString{} : m_gsp;
+}
+
+template<typename T>
+static QString getClassTargetFullName_(T t, bool includePackageName)
+{
+ QString name = t->name();
+ AbstractMetaClassCPtr context = t->enclosingClass();
+ while (context) {
+ // If the type was marked as 'visible=false' we should not use it in
+ // the type name
+ if (NamespaceTypeEntry::isVisibleScope(context->typeEntry())) {
+ name.prepend(u'.');
+ name.prepend(context->name());
+ }
+ context = context->enclosingClass();
+ }
+ if (includePackageName) {
+ name.prepend(u'.');
+ name.prepend(t->package());
+ }
+ return name;
+}
+
+QString getClassTargetFullName(const AbstractMetaClassCPtr &metaClass,
+ bool includePackageName)
+{
+ return getClassTargetFullName_(metaClass, includePackageName);
+}
+
+QString getClassTargetFullName(const AbstractMetaEnum &metaEnum,
+ bool includePackageName)
+{
+ return getClassTargetFullName_(&metaEnum, includePackageName);
+}
+
+QString getFilteredCppSignatureString(QString signature)
+{
+ signature.replace(u"::"_s, u"_"_s);
+ signature.replace(u'<', u'_');
+ signature.replace(u'>', u'_');
+ signature.replace(u' ', u'_');
+ return signature;
+}
diff --git a/sources/shiboken6/generator/generator.h b/sources/shiboken6/generator/generator.h
new file mode 100644
index 000000000..5b051b599
--- /dev/null
+++ b/sources/shiboken6/generator/generator.h
@@ -0,0 +1,233 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef GENERATOR_H
+#define GENERATOR_H
+
+#include <abstractmetalang_typedefs.h>
+#include <typedatabase_typedefs.h>
+#include <QtCore/QList>
+
+#include <memory>
+#include <optional>
+
+
+class ApiExtractorResult;
+class GeneratorContext;
+class DefaultValue;
+struct OptionDescription;
+class OptionsParser;
+class TextStream;
+
+QString getClassTargetFullName(const AbstractMetaClassCPtr &metaClass,
+ bool includePackageName = true);
+QString getClassTargetFullName(const AbstractMetaEnum &metaEnum,
+ bool includePackageName = true);
+QString getFilteredCppSignatureString(QString signature);
+
+/**
+ * Base class for all generators. The default implementations does nothing,
+ * you must subclass this to create your own generators.
+ */
+class Generator
+{;
+public:
+ Q_DISABLE_COPY_MOVE(Generator)
+
+ /// Options used around the generator code
+ enum Option {
+ NoOption = 0x00000000,
+ ExcludeConst = 0x00000001,
+ ExcludeReference = 0x00000002,
+
+ SkipReturnType = 0x00000010,
+ VirtualCall = 0x00000040,
+ OriginalTypeDescription = 0x00000080,
+ SkipRemovedArguments = 0x00000100,
+
+ SkipDefaultValues = 0x00000200,
+ };
+ Q_DECLARE_FLAGS(Options, Option)
+
+ enum FileNameFlag {
+ UnqualifiedName = 0x1,
+ KeepCase = 0x2
+ };
+ Q_DECLARE_FLAGS(FileNameFlags, FileNameFlag)
+
+ Generator();
+ virtual ~Generator();
+
+ bool setup(const ApiExtractorResult &api);
+
+ static QList<OptionDescription> options();
+ static std::shared_ptr<OptionsParser> createOptionsParser();
+
+ /// Returns the top namespace made invisible
+ const AbstractMetaClassCList &invisibleTopNamespaces() const;
+
+ /// Returns the output directory
+ QString outputDirectory() const;
+
+ /// Set the output directory
+ void setOutputDirectory(const QString &outDir);
+
+ /**
+ * Start the code generation, be sure to call setClasses before callign this method.
+ * For each class it creates a QTextStream, call the write method with the current
+ * class and the associated text stream, then write the text stream contents if needed.
+ * \see #write
+ */
+ bool generate();
+
+ /// Returns the license comment to be prepended to each source file generated.
+ QString licenseComment() const;
+
+ /// Sets the license comment to be prepended to each source file generated.
+ void setLicenseComment(const QString &licenseComment);
+
+ /// Returns the generator's name. Used for cosmetic purposes.
+ virtual const char *name() const = 0;
+
+ /// Returns the API as determined by ApiExtractor
+ const ApiExtractorResult &api() const;
+
+ bool hasPrivateClasses() const;
+
+ /// Returns true if the user enabled PySide extensions (command line option)
+ static bool usePySideExtensions();
+ /// Returns true if the generated code should not use the
+ /// "#define protected public" hack.
+ static bool avoidProtectedHack();
+
+ /**
+ * Retrieves the name of the currently processed module.
+ * While package name is a complete package idetification, e.g. 'PySide.QtCore',
+ * a module name represents the last part of the package, e.g. 'QtCore'.
+ * If the target language separates the modules with characters other than
+ * dots ('.') the generator subclass must overload this method.
+ * \return a string representing the last part of a package name
+ */
+ static QString moduleName();
+
+ static QString pythonOperatorFunctionName(const QString &cppOpFuncName);
+ static bool isPythonOperatorFunctionName(const QString &cppOpFuncName);
+
+protected:
+ /// Helper for determining the file name
+ static QString fileNameForContextHelper(const GeneratorContext &context,
+ const QString &suffix,
+ FileNameFlags flags = {});
+
+ /// Returns all primitive types found by APIExtractor
+ static PrimitiveTypeEntryCList primitiveTypes();
+
+ /// Returns all container types found by APIExtractor
+ static ContainerTypeEntryCList containerTypes();
+
+ virtual GeneratorContext contextForClass(const AbstractMetaClassCPtr &c) const;
+ static GeneratorContext
+ contextForSmartPointer(const AbstractMetaClassCPtr &c, const AbstractMetaType &t,
+ const AbstractMetaClassCPtr &pointeeClass = {});
+
+ /// Generates a file for given AbstractMetaClass or AbstractMetaType (smart pointer case).
+ bool generateFileForContext(const GeneratorContext &context);
+
+ /// Returns the file base name for a smart pointer.
+ static QString getFileNameBaseForSmartPointer(const AbstractMetaType &smartPointerType);
+
+ /// Returns true if the generator should generate any code for the AbstractMetaClass.
+ virtual bool shouldGenerate(const TypeEntryCPtr &t) const;
+
+ /**
+ * Translate metatypes to binding source format.
+ * \param metatype a pointer to metatype
+ * \param context the current meta class
+ * \param option some extra options
+ * \return the metatype translated to binding source format
+ */
+ QString translateType(AbstractMetaType metatype,
+ const AbstractMetaClassCPtr &context,
+ Options options = NoOption) const;
+
+ /**
+ * Returns the package name.
+ */
+ static QString packageName();
+
+ // Returns the full name of the type.
+ static QString getFullTypeName(TypeEntryCPtr type);
+ static QString getFullTypeName(const AbstractMetaType &type);
+ static QString getFullTypeName(const AbstractMetaClassCPtr &metaClass);
+
+ /**
+ * Returns the full qualified C++ name for an AbstractMetaType, but removing modifiers
+ * as 'const', '&', and '*' (except if the class is not derived from a template).
+ * This is useful for instantiated templates.
+ */
+ static QString getFullTypeNameWithoutModifiers(const AbstractMetaType &type);
+
+ /**
+ * Tries to build a minimal constructor for the type.
+ * It will check first for a user defined default constructor.
+ * Returns a null string if it fails.
+ */
+ static std::optional<DefaultValue>
+ minimalConstructor(const ApiExtractorResult &api, const TypeEntryCPtr &type,
+ QString *errorString = nullptr);
+ static std::optional<DefaultValue>
+ minimalConstructor(const ApiExtractorResult &api, const AbstractMetaType &type,
+ QString *errorString = nullptr);
+ static std::optional<DefaultValue>
+ minimalConstructor(const ApiExtractorResult &api,
+ const AbstractMetaClassCPtr &metaClass,
+ QString *errorString = nullptr);
+
+ /**
+ * Returns the file name used to write the binding code of an AbstractMetaClass/Type.
+ * \param context the GeneratorContext which contains an AbstractMetaClass or AbstractMetaType
+ * for which the file name must be returned
+ * \return the file name used to write the binding code for the class
+ */
+ virtual QString fileNameForContext(const GeneratorContext &context) const = 0;
+
+
+ virtual bool doSetup() = 0;
+
+ /**
+ * Write the bindding code for an AbstractMetaClass.
+ * This is called by generate method.
+ * \param s text stream to write the generated output
+ * \param metaClass the class that should be generated
+ */
+ virtual void generateClass(TextStream &s, const GeneratorContext &classContext) = 0;
+ virtual bool finishGeneration() = 0;
+
+ /**
+ * Returns the subdirectory path for a given package
+ * (aka module, aka library) name.
+ * If the target language separates the package modules with characters other
+ * than dots ('.') the generator subclass must overload this method.
+ * /param packageName complete package name for which to return the subdirectory path
+ * or nothing the use the name of the currently processed package
+ * /return a string representing the subdirectory path for the given package
+ */
+ virtual QString subDirectoryForPackage(QString packageName = QString()) const;
+
+ static QString addGlobalScopePrefix(const QString &t);
+ static QString globalScopePrefix(const GeneratorContext &classContext);
+
+ static QString m_gsp;
+
+private:
+ struct GeneratorPrivate;
+ GeneratorPrivate *m_d;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(Generator::Options)
+Q_DECLARE_OPERATORS_FOR_FLAGS(Generator::FileNameFlags)
+
+using GeneratorPtr = std::shared_ptr<Generator>;
+using Generators = QList<GeneratorPtr>;
+
+#endif // GENERATOR_H
diff --git a/sources/shiboken6/generator/generatorcontext.cpp b/sources/shiboken6/generator/generatorcontext.cpp
new file mode 100644
index 000000000..b50c2effb
--- /dev/null
+++ b/sources/shiboken6/generator/generatorcontext.cpp
@@ -0,0 +1,38 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "generatorcontext.h"
+#include <abstractmetalang.h>
+
+#include <QtCore/QDebug>
+
+using namespace Qt::StringLiterals;
+
+QString GeneratorContext::wrapperName() const
+{
+ Q_ASSERT(m_type == WrappedClass);
+ return m_wrappername;
+}
+
+QString GeneratorContext::effectiveClassName() const
+{
+ if (m_type == SmartPointer)
+ return m_preciseClassType.cppSignature();
+ return m_type == WrappedClass ? m_wrappername : m_metaClass->qualifiedCppName();
+}
+
+QDebug operator<<(QDebug debug, const GeneratorContext &c)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "GeneratorContext(\"" << c.metaClass()->name() << "\" ";
+ if (c.useWrapper())
+ debug << "[wrapper]";
+ else if (c.forSmartPointer())
+ debug << "[smart pointer] \"" << c.preciseType().cppSignature() << '"';
+ else
+ debug << "[class]";
+ debug << ')';
+ return debug;
+}
diff --git a/sources/shiboken6/generator/generatorcontext.h b/sources/shiboken6/generator/generatorcontext.h
new file mode 100644
index 000000000..2e58d4346
--- /dev/null
+++ b/sources/shiboken6/generator/generatorcontext.h
@@ -0,0 +1,56 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef GENERATORCONTEXT_H
+#define GENERATORCONTEXT_H
+
+#include <abstractmetalang_typedefs.h>
+#include <abstractmetatype.h>
+#include <QtCore/QList>
+
+QT_FORWARD_DECLARE_CLASS(QDebug);
+
+// A GeneratorContext object contains a pointer to an AbstractMetaClass and/or a specialized
+// AbstractMetaType, for which code is currently being generated.
+//
+// The main case is when the context contains only an AbstractMetaClass pointer, which is used
+// by different methods to generate appropriate expressions, functions, type names, etc.
+//
+// The second case is for generation of code for smart pointers. In this case the m_metaClass
+// member contains the generic template class of the smart pointer, and the m_preciseClassType
+// member contains the instantiated template type, e.g. a concrete shared_ptr<int>. To
+// distinguish this case, the member m_forSmartPointer is set to true.
+//
+// In the future the second case might be generalized for all template type instantiations.
+
+class GeneratorContext {
+ friend class ShibokenGenerator;
+ friend class Generator;
+public:
+ enum Type { Class, WrappedClass, SmartPointer };
+
+ GeneratorContext() = default;
+
+ AbstractMetaClassCPtr metaClass() const { return m_metaClass; }
+ const AbstractMetaType &preciseType() const { return m_preciseClassType; }
+ AbstractMetaClassCPtr pointeeClass() const { return m_pointeeClass; }
+
+ bool forSmartPointer() const { return m_type == SmartPointer; }
+ bool useWrapper() const { return m_type == WrappedClass; }
+
+ QString wrapperName() const;
+ /// Returns the wrapper name in case of useWrapper(), the qualified class
+ /// name or the smart pointer specialization.
+ QString effectiveClassName() const;
+
+private:
+ AbstractMetaClassCPtr m_metaClass;
+ AbstractMetaClassCPtr m_pointeeClass;
+ AbstractMetaType m_preciseClassType;
+ QString m_wrappername;
+ Type m_type = Class;
+};
+
+QDebug operator<<(QDebug debug, const GeneratorContext &c);
+
+#endif // GENERATORCONTEXT_H
diff --git a/sources/shiboken6/generator/main.cpp b/sources/shiboken6/generator/main.cpp
new file mode 100644
index 000000000..9871df206
--- /dev/null
+++ b/sources/shiboken6/generator/main.cpp
@@ -0,0 +1,435 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "shibokenconfig.h"
+#include "cppgenerator.h"
+#include "generator.h"
+#include "headergenerator.h"
+#include "qtdocgenerator.h"
+
+#include <apiextractor.h>
+#include <apiextractorresult.h>
+#include <exception.h>
+#include <fileout.h>
+#include <messages.h>
+#include <optionsparser.h>
+#include <reporthandler.h>
+#include <typedatabase.h>
+
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QLibrary>
+#include <QtCore/QVariant>
+
+#include "qtcompat.h"
+
+#include <exception>
+#include <iostream>
+
+using namespace Qt::StringLiterals;
+
+static const char helpHint[] = "Note: use --help or -h for more information.\n";
+static const char appName[] = "shiboken";
+
+static inline Generators docGenerators()
+{
+ Generators result;
+#ifdef DOCSTRINGS_ENABLED
+ result.append(GeneratorPtr(new QtDocGenerator));
+#endif
+ return result;
+}
+
+static inline Generators shibokenGenerators()
+{
+ Generators result;
+ result << GeneratorPtr(new CppGenerator) << GeneratorPtr(new HeaderGenerator);
+ return result;
+}
+
+struct CommonOptions
+{
+ QString generatorSet;
+ QString licenseComment;
+ QString outputDirectory = u"out"_s;
+ QStringList headers;
+ QString typeSystemFileName;
+ bool help = false;
+ bool version = false;
+ bool diff = false;
+ bool dryRun = false;
+ bool logUnmatched = false;
+ bool printBuiltinTypes = false;
+};
+
+class CommonOptionsParser : public OptionsParser
+{
+public:
+ explicit CommonOptionsParser(CommonOptions *o) : m_options(o) {}
+
+ bool handleBoolOption(const QString &key, OptionSource source) override;
+ bool handleOption(const QString &key, const QString &value, OptionSource source) override;
+
+ static OptionDescriptions optionDescriptions();
+
+private:
+ CommonOptions *m_options;
+};
+
+OptionDescriptions CommonOptionsParser::optionDescriptions()
+{
+ return {
+ {u"debug-level=[sparse|medium|full]"_s,
+ u"Set the debug level"_s},
+ {u"documentation-only"_s,
+ u"Do not generates any code, just the documentation"_s},
+ {u"compiler=<type>"_s,
+ u"Emulated compiler type (g++, msvc, clang)"_s},
+ {u"platform=<name>"_s,
+ u"Emulated platform (windows, darwin, unix)"_s},
+ {u"compiler-path=<file>"_s,
+ u"Path to the compiler for determining builtin include paths"_s},
+ {u"generator-set=<\"generator module\">"_s,
+ u"generator-set to be used. e.g. qtdoc"_s},
+ {u"diff"_s, u"Print a diff of wrapper files"_s},
+ {u"dry-run"_s, u"Dry run, do not generate wrapper files"_s},
+ {u"-h"_s, {} },
+ {u"help"_s, u"Display this help and exit"_s},
+ {u"-I<path>"_s, {} },
+ {u"include-paths="_s + OptionsParser::pathSyntax(),
+ u"Include paths used by the C++ parser"_s},
+ {u"license-file=<license-file>"_s,
+ u"File used for copyright headers of generated files"_s},
+ {u"no-suppress-warnings"_s,
+ u"Show all warnings"_s},
+ {u"output-directory=<path>"_s,
+ u"The directory where the generated files will be written"_s},
+ {u"project-file=<file>"_s,
+ u"text file containing a description of the binding project.\n"
+ "Replaces and overrides command line arguments"_s},
+ {u"silent"_s, u"Avoid printing any message"_s},
+ {u"print-builtin-types"_s,
+ u"Print information about builtin types"_s},
+ {u"version"_s,
+ u"Output version information and exit"_s}
+ };
+}
+
+bool CommonOptionsParser::handleBoolOption(const QString &key, OptionSource source)
+{
+ if (source == OptionSource::CommandLineSingleDash) {
+ if (key == u"h") {
+ m_options->help = true;
+ return true;
+ }
+ return false;
+ }
+
+ if (key == u"version") {
+ m_options->version = true;
+ return true;
+ }
+ if (key == u"help") {
+ m_options->help = true;
+ return true;
+ }
+ if (key == u"diff") {
+ FileOut::setDiff(true);
+ return true;
+ }
+ if (key == u"dry-run") {
+ FileOut::setDryRun(true);
+ return true;
+ }
+ if (key == u"silent") {
+ ReportHandler::setSilent(true);
+ return true;
+ }
+ if (key == u"log-unmatched") {
+ m_options->logUnmatched = true;
+ return true;
+ }
+ if (key == u"print-builtin-types") {
+ m_options->printBuiltinTypes = true;
+ return true;
+ }
+
+ return false;
+}
+
+bool CommonOptionsParser::handleOption(const QString &key, const QString &value,
+ OptionSource source)
+{
+ if (source == OptionSource::CommandLineSingleDash)
+ return false;
+
+ if (key == u"generator-set" || key == u"generatorSet" /* legacy */) {
+ m_options->generatorSet = value;
+ return true;
+ }
+ if (key == u"license-file") {
+ QFile licenseFile(value);
+ if (!licenseFile.open(QIODevice::ReadOnly))
+ throw Exception(msgCannotOpenForReading(licenseFile));
+ m_options->licenseComment = QString::fromUtf8(licenseFile.readAll());
+ return true;
+ }
+ if (key == u"debug-level") {
+ if (!ReportHandler::setDebugLevelFromArg(value))
+ throw Exception(u"Invalid debug level: "_s + value);
+ return true;
+ }
+ if (key == u"output-directory") {
+ m_options->outputDirectory = value;
+ return true;
+ }
+ if (key == u"compiler") {
+ if (!clang::setCompiler(value))
+ throw Exception(u"Invalid value \""_s + value + u"\" passed to --compiler"_s);
+ return true;
+ }
+ if (key == u"compiler-path") {
+ clang::setCompilerPath(value);
+ return true;
+ }
+ if (key == u"platform") {
+ if (!clang::setPlatform(value))
+ throw Exception(u"Invalid value \""_s + value + u"\" passed to --platform"_s);
+ return true;
+ }
+
+ if (source == OptionSource::ProjectFile) {
+ if (key == u"header-file") {
+ m_options->headers.append(value);
+ return true;
+ }
+ if (key == u"typesystem-file") {
+ m_options->typeSystemFileName = value;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void printUsage()
+{
+ const auto generatorOptions = Generator::options();
+
+ QTextStream s(stdout);
+ s << "Usage:\n "
+ << "shiboken [options] header-file(s) typesystem-file\n\n"
+ << "General options:\n"
+ << CommonOptionsParser::optionDescriptions()
+ << ApiExtractor::options()
+ << TypeDatabase::options()
+ << "\nSource generator options:\n\n" << generatorOptions
+ << ShibokenGenerator::options();
+
+#ifdef DOCSTRINGS_ENABLED
+ s << "\nDocumentation Generator options:\n\n"
+ << generatorOptions << QtDocGenerator::options();
+#endif
+}
+
+static inline void printVerAndBanner()
+{
+ std::cout << appName << " v" << SHIBOKEN_VERSION << std::endl;
+ std::cout << "Copyright (C) 2016 The Qt Company Ltd." << std::endl;
+}
+
+static inline void errorPrint(const QString &s, const QStringList &arguments)
+{
+ std::cerr << appName << ": " << qPrintable(s) << "\nCommand line:\n";
+ for (const auto &argument : arguments)
+ std::cerr << " \"" << qPrintable(argument) << "\"\n";
+}
+
+int shibokenMain(const QStringList &argV)
+{
+ // PYSIDE-757: Request a deterministic ordering of QHash in the code model
+ // and type system.
+ QHashSeed::setDeterministicGlobalSeed();
+
+ ReportHandler::install();
+ if (ReportHandler::isDebug(ReportHandler::SparseDebug))
+ qCInfo(lcShiboken()).noquote().nospace() << appName << ' ' << argV.join(u' ');
+
+ Options options;
+ options.setOptions(argV);
+
+ CommonOptions commonOptions;
+ {
+ CommonOptionsParser parser(&commonOptions);
+ parser.process(&options);
+ }
+ if (commonOptions.version) {
+ printVerAndBanner();
+ return EXIT_SUCCESS;
+ }
+ if (commonOptions.help) {
+ printUsage();
+ return EXIT_SUCCESS;
+ }
+
+ Generators generators;
+
+ OptionsParserList optionParser;
+ optionParser.append(Generator::createOptionsParser());
+ optionParser.append(TypeDatabase::instance()->createOptionsParser());
+ ApiExtractor extractor;
+ optionParser.append(extractor.createOptionsParser());
+
+ // Pre-defined generator sets.
+ if (commonOptions.generatorSet == u"qtdoc") {
+ generators = docGenerators();
+ if (generators.isEmpty()) {
+ errorPrint(u"Doc strings extractions was not enabled in this shiboken build."_s, argV);
+ return EXIT_FAILURE;
+ }
+#ifdef DOCSTRINGS_ENABLED
+ optionParser.append(QtDocGenerator::createOptionsParser());
+#endif
+ } else if (commonOptions.generatorSet.isEmpty() || commonOptions.generatorSet == u"shiboken") {
+ generators = shibokenGenerators();
+ optionParser.append(ShibokenGenerator::createOptionsParser());
+ } else {
+ errorPrint(u"Unknown generator set, try \"shiboken\" or \"qtdoc\"."_s, argV);
+ return EXIT_FAILURE;
+ }
+
+ if (!QDir(commonOptions.outputDirectory).exists()) {
+ if (!QDir().mkpath(commonOptions.outputDirectory)) {
+ qCWarning(lcShiboken).noquote().nospace()
+ << "Can't create output directory: "
+ << QDir::toNativeSeparators(commonOptions.outputDirectory);
+ return EXIT_FAILURE;
+ }
+ }
+
+ // Create and set-up API Extractor
+ extractor.setLogDirectory(commonOptions.outputDirectory);
+
+ if (commonOptions.typeSystemFileName.isEmpty() && commonOptions.headers.isEmpty()) {
+ if (options.positionalArguments.size() < 2) {
+ errorPrint(u"Insufficient positional arguments, specify header-file and typesystem-file."_s,
+ argV);
+ std::cout << '\n';
+ printUsage();
+ return EXIT_FAILURE;
+ }
+
+ commonOptions.typeSystemFileName = options.positionalArguments.takeLast();
+ commonOptions.headers = options.positionalArguments;
+ }
+
+ QString messagePrefix = QFileInfo(commonOptions.typeSystemFileName).baseName();
+ if (messagePrefix.startsWith(u"typesystem_"))
+ messagePrefix.remove(0, 11);
+ ReportHandler::setPrefix(u'(' + messagePrefix + u')');
+
+ QFileInfoList cppFileNames;
+ for (const QString &cppFileName : std::as_const(commonOptions.headers)) {
+ const QFileInfo cppFileNameFi(cppFileName);
+ if (!cppFileNameFi.isFile() && !cppFileNameFi.isSymLink()) {
+ errorPrint(u'"' + cppFileName + u"\" does not exist."_s, argV);
+ return EXIT_FAILURE;
+ }
+ cppFileNames.append(cppFileNameFi);
+ }
+
+ optionParser.process(&options);
+ optionParser.clear();
+
+ if (!options.boolOptions.isEmpty() || !options.valueOptions.isEmpty()) {
+ errorPrint(msgLeftOverArguments(options.msgUnprocessedOptions(), argV), argV);
+ std::cout << helpHint;
+ return EXIT_FAILURE;
+ }
+
+ if (commonOptions.typeSystemFileName.isEmpty()) {
+ std::cout << "You must specify a Type System file." << std::endl << helpHint;
+ return EXIT_FAILURE;
+ }
+
+ extractor.setCppFileNames(cppFileNames);
+ extractor.setTypeSystem(commonOptions.typeSystemFileName);
+
+ ApiExtractorFlags apiExtractorFlags;
+ if (generators.constFirst()->usePySideExtensions())
+ apiExtractorFlags.setFlag(ApiExtractorFlag::UsePySideExtensions);
+ if (generators.constFirst()->avoidProtectedHack())
+ apiExtractorFlags.setFlag(ApiExtractorFlag::AvoidProtectedHack);
+ const std::optional<ApiExtractorResult> apiOpt = extractor.run(apiExtractorFlags);
+
+ if (!apiOpt.has_value()) {
+ errorPrint(u"Error running ApiExtractor."_s, argV);
+ return EXIT_FAILURE;
+ }
+
+ if (apiOpt->classes().isEmpty())
+ qCWarning(lcShiboken) << "No C++ classes found!";
+
+ if (ReportHandler::isDebug(ReportHandler::FullDebug)
+ || qEnvironmentVariableIsSet("SHIBOKEN_DUMP_CODEMODEL")) {
+ qCInfo(lcShiboken) << "API Extractor:\n" << extractor
+ << "\n\nType datase:\n" << *TypeDatabase::instance();
+ }
+
+ if (commonOptions.printBuiltinTypes)
+ TypeDatabase::instance()->formatBuiltinTypes(qInfo());
+
+ for (const GeneratorPtr &g : std::as_const(generators)) {
+ g->setOutputDirectory(commonOptions.outputDirectory);
+ g->setLicenseComment(commonOptions.licenseComment);
+ ReportHandler::startProgress("Ran "_ba + g->name() + '.');
+ const bool ok = g->setup(apiOpt.value()) && g->generate();
+ ReportHandler::endProgress();
+ if (!ok) {
+ errorPrint(u"Error running generator: "_s
+ + QLatin1StringView(g->name()) + u'.', argV);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (commonOptions.logUnmatched)
+ TypeDatabase::instance()->logUnmatched();
+
+ const QByteArray doneMessage = ReportHandler::doneMessage();
+ std::cout << doneMessage.constData() << std::endl;
+
+ return EXIT_SUCCESS;
+}
+
+#ifndef Q_OS_WIN
+
+static inline QString argvToString(const char *arg)
+{
+ return QString::fromLocal8Bit(arg);
+}
+
+int main(int argc, char *argv[])
+#else
+
+static inline QString argvToString(const wchar_t *arg)
+{
+ return QString::fromWCharArray(arg);
+}
+
+int wmain(int argc, wchar_t *argv[])
+#endif
+{
+ int ex = EXIT_SUCCESS;
+
+ QStringList argV;
+ argV.reserve(argc - 1);
+ std::transform(argv + 1, argv + argc, std::back_inserter(argV), argvToString);
+
+ try {
+ ex = shibokenMain(argV);
+ } catch (const std::exception &e) {
+ std::cerr << appName << " error: " << e.what() << std::endl;
+ ex = EXIT_FAILURE;
+ }
+ return ex;
+}
diff --git a/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp b/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp
new file mode 100644
index 000000000..1634a7e83
--- /dev/null
+++ b/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp
@@ -0,0 +1,1591 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qtdocgenerator.h"
+#include "generatorcontext.h"
+#include "codesnip.h"
+#include "exception.h"
+#include "abstractmetaargument.h"
+#include "apiextractorresult.h"
+#include "qtxmltosphinx.h"
+#include "rstformat.h"
+#include "ctypenames.h"
+#include "pytypenames.h"
+#include <abstractmetaenum.h>
+#include <abstractmetafield.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include "abstractmetalang_helpers.h"
+#include <fileout.h>
+#include <messages.h>
+#include <modifications.h>
+#include <propertyspec.h>
+#include <reporthandler.h>
+#include <textstream.h>
+#include <typedatabase.h>
+#include <functiontypeentry.h>
+#include <enumtypeentry.h>
+#include <complextypeentry.h>
+#include <flagstypeentry.h>
+#include <primitivetypeentry.h>
+#include <qtdocparser.h>
+#include <doxygenparser.h>
+
+#include "qtcompat.h"
+
+#include <QtCore/QTextStream>
+#include <QtCore/QFile>
+#include <QtCore/QDir>
+#include <QtCore/QJsonArray>
+#include <QtCore/QJsonDocument>
+#include <QtCore/QJsonObject>
+#include <QtCore/QSet>
+
+#include <algorithm>
+#include <limits>
+
+using namespace Qt::StringLiterals;
+
+static inline QString classScope(const AbstractMetaClassCPtr &metaClass)
+{
+ return metaClass->fullName();
+}
+
+struct DocPackage
+{
+ QStringList classPages;
+ QStringList decoratorPages;
+ AbstractMetaFunctionCList globalFunctions;
+ AbstractMetaEnumList globalEnums;
+};
+
+struct DocGeneratorOptions
+{
+ QtXmlToSphinxParameters parameters;
+ QString extraSectionDir;
+ QString additionalDocumentationList;
+ QString inheritanceFile;
+ bool doxygen = false;
+ bool inheritanceDiagram = true;
+};
+
+struct GeneratorDocumentation
+{
+ struct Property
+ {
+ QString name;
+ Documentation documentation;
+ AbstractMetaType type;
+ AbstractMetaFunctionCPtr getter;
+ AbstractMetaFunctionCPtr setter;
+ AbstractMetaFunctionCPtr reset;
+ AbstractMetaFunctionCPtr notify;
+ };
+
+ AbstractMetaFunctionCList allFunctions;
+ AbstractMetaFunctionCList tocNormalFunctions; // Index lists
+ AbstractMetaFunctionCList tocVirtuals;
+ AbstractMetaFunctionCList tocSignalFunctions;
+ AbstractMetaFunctionCList tocSlotFunctions;
+ AbstractMetaFunctionCList tocStaticFunctions;
+
+ QList<Property> properties;
+};
+
+static bool operator<(const GeneratorDocumentation::Property &lhs,
+ const GeneratorDocumentation::Property &rhs)
+{
+ return lhs.name < rhs.name;
+}
+
+static QString propertyRefTarget(const QString &name)
+{
+ QString result = name;
+ // For sphinx referencing, disambiguate the target from the getter name
+ // by appending an invisible "Hangul choseong filler" character.
+ result.append(QChar(0x115F));
+ return result;
+}
+
+constexpr auto additionalDocumentationOption = "additional-documentation"_L1;
+
+constexpr auto none = "None"_L1;
+
+static bool shouldSkip(const AbstractMetaFunctionCPtr &func)
+{
+ if (DocParser::skipForQuery(func))
+ return true;
+
+ // Search a const clone (QImage::bits() vs QImage::bits() const)
+ if (func->isConstant())
+ return false;
+
+ const AbstractMetaArgumentList funcArgs = func->arguments();
+ const auto &ownerFunctions = func->ownerClass()->functions();
+ for (const auto &f : ownerFunctions) {
+ if (f != func
+ && f->isConstant()
+ && f->name() == func->name()
+ && f->arguments().size() == funcArgs.size()) {
+ // Compare each argument
+ bool cloneFound = true;
+
+ const AbstractMetaArgumentList fargs = f->arguments();
+ for (qsizetype i = 0, max = funcArgs.size(); i < max; ++i) {
+ if (funcArgs.at(i).type().typeEntry() != fargs.at(i).type().typeEntry()) {
+ cloneFound = false;
+ break;
+ }
+ }
+ if (cloneFound)
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool functionSort(const AbstractMetaFunctionCPtr &func1, const AbstractMetaFunctionCPtr &func2)
+{
+ const bool ctor1 = func1->isConstructor();
+ if (ctor1 != func2->isConstructor())
+ return ctor1;
+ const QString &name1 = func1->name();
+ const QString &name2 = func2->name();
+ if (name1 != name2)
+ return name1 < name2;
+ return func1->arguments().size() < func2->arguments().size();
+}
+
+static inline QVersionNumber versionOf(const TypeEntryCPtr &te)
+{
+ if (te) {
+ const auto version = te->version();
+ if (!version.isNull() && version > QVersionNumber(0, 0))
+ return version;
+ }
+ return {};
+}
+
+struct docRef
+{
+ explicit docRef(const char *kind, QAnyStringView name) :
+ m_kind(kind), m_name(name) {}
+
+ const char *m_kind;
+ QAnyStringView m_name;
+};
+
+static TextStream &operator<<(TextStream &s, const docRef &dr)
+{
+ s << ':' << dr.m_kind << ":`" << dr.m_name << '`';
+ return s;
+}
+
+static QString fileNameToTocEntry(const QString &fileName)
+{
+ constexpr auto rstSuffix = ".rst"_L1;
+
+ QString result = fileName;
+ if (result.endsWith(rstSuffix))
+ result.chop(rstSuffix.size()); // Remove the .rst extension
+ // skip namespace if necessary
+ auto lastDot = result.lastIndexOf(u'.');
+ if (lastDot != -1)
+ result.remove(0, lastDot + 1);
+ return result;
+}
+
+static void readExtraDoc(const QFileInfo &fi,
+ const QString &moduleName,
+ const QString &outputDir,
+ DocPackage *docPackage, QStringList *extraTocEntries)
+{
+ // Strip to "Property.rst" in output directory
+ const QString newFileName = fi.fileName().mid(moduleName.size() + 1);
+ QFile sourceFile(fi.absoluteFilePath());
+ if (!sourceFile.open(QIODevice::ReadOnly|QIODevice::Text)) {
+ qCWarning(lcShibokenDoc, "%s", qPrintable(msgCannotOpenForReading(sourceFile)));
+ return;
+ }
+ const QByteArray contents = sourceFile.readAll();
+ sourceFile.close();
+ QFile targetFile(outputDir + u'/' + newFileName);
+ if (!targetFile.open(QIODevice::WriteOnly|QIODevice::Text)) {
+ qCWarning(lcShibokenDoc, "%s", qPrintable(msgCannotOpenForWriting(targetFile)));
+ return;
+ }
+ targetFile.write(contents);
+ if (contents.contains("decorator::"))
+ docPackage->decoratorPages.append(newFileName);
+ else
+ docPackage->classPages.append(newFileName);
+ extraTocEntries->append(fileNameToTocEntry(newFileName));
+}
+
+// Format a short documentation reference (automatically dropping the prefix
+// by using '~'), usable for property/attributes ("attr").
+struct shortDocRef
+{
+ explicit shortDocRef(const char *kind, QAnyStringView name) :
+ m_kind(kind), m_name(name) {}
+
+ const char *m_kind;
+ QAnyStringView m_name;
+};
+
+static TextStream &operator<<(TextStream &s, const shortDocRef &sdr)
+{
+ s << ':' << sdr.m_kind << ":`~" << sdr.m_name << '`';
+ return s;
+}
+
+struct functionRef : public docRef
+{
+ explicit functionRef(QAnyStringView name) : docRef("meth", name) {}
+};
+
+struct classRef : public shortDocRef
+{
+ explicit classRef(QAnyStringView name) : shortDocRef("class", name) {}
+};
+
+struct propRef : public shortDocRef // Attribute/property (short) reference
+{
+ explicit propRef(const QString &target) :
+ shortDocRef("attr", target) {}
+};
+
+struct headline
+{
+ explicit headline(QAnyStringView title, char underLineChar = '-') :
+ m_title(title), m_underLineChar(underLineChar) {}
+
+ QAnyStringView m_title;
+ char m_underLineChar;
+};
+
+static TextStream &operator<<(TextStream &s, const headline &h)
+{
+ s << h.m_title << '\n' << Pad(h.m_underLineChar, h.m_title.size()) << "\n\n";
+ return s;
+}
+
+struct pyClass
+{
+ explicit pyClass(QAnyStringView name) : m_name(name) {}
+
+ QAnyStringView m_name;
+};
+
+static TextStream &operator<<(TextStream &s, pyClass c)
+{
+ s << ".. py:class:: " << c.m_name << "\n\n";
+ return s;
+}
+
+struct currentModule
+{
+ explicit currentModule(QAnyStringView module) : m_module(module) {}
+
+ QAnyStringView m_module;
+};
+
+static TextStream &operator<<(TextStream &s, const currentModule &m)
+{
+ s << ".. currentmodule:: " << m.m_module << "\n\n\n";
+ return s;
+}
+
+DocGeneratorOptions QtDocGenerator::m_options;
+
+QtDocGenerator::QtDocGenerator()
+{
+ m_options.parameters.snippetComparison =
+ ReportHandler::debugLevel() >= ReportHandler::FullDebug;
+}
+
+QtDocGenerator::~QtDocGenerator() = default;
+
+QString QtDocGenerator::fileNameSuffix()
+{
+ return u".rst"_s;
+}
+
+bool QtDocGenerator::shouldGenerate(const TypeEntryCPtr &te) const
+{
+ return Generator::shouldGenerate(te)
+ && te->type() != TypeEntry::SmartPointerType;
+}
+
+QString QtDocGenerator::fileNameForContext(const GeneratorContext &context) const
+{
+ return fileNameForContextHelper(context, fileNameSuffix(),
+ FileNameFlag::UnqualifiedName
+ | FileNameFlag::KeepCase);
+}
+
+void QtDocGenerator::writeFormattedBriefText(TextStream &s, const Documentation &doc,
+ const QString &scope) const
+{
+ writeFormattedText(s, doc.brief(), doc.format(), scope);
+}
+
+void QtDocGenerator::writeFormattedDetailedText(TextStream &s, const Documentation &doc,
+ const QString &scope) const
+{
+ writeFormattedText(s, doc.detailed(), doc.format(), scope);
+}
+
+void QtDocGenerator::writeFormattedText(TextStream &s, const QString &doc,
+ Documentation::Format format,
+ const QString &scope) const
+{
+ if (format == Documentation::Native) {
+ QtXmlToSphinx x(this, m_options.parameters, doc, scope);
+ s << x;
+ } else {
+ const auto lines = QStringView{doc}.split(u'\n');
+ int typesystemIndentation = std::numeric_limits<int>::max();
+ // check how many spaces must be removed from the beginning of each line
+ for (const auto &line : lines) {
+ const auto it = std::find_if(line.cbegin(), line.cend(),
+ [] (QChar c) { return !c.isSpace(); });
+ if (it != line.cend())
+ typesystemIndentation = qMin(typesystemIndentation, int(it - line.cbegin()));
+ }
+ if (typesystemIndentation == std::numeric_limits<int>::max())
+ typesystemIndentation = 0;
+ for (const auto &line : lines) {
+ s << (typesystemIndentation > 0 && typesystemIndentation < line.size()
+ ? line.right(line.size() - typesystemIndentation) : line)
+ << '\n';
+ }
+ }
+
+ s << '\n';
+}
+
+static void writeInheritanceList(TextStream &s, const AbstractMetaClassCList& classes,
+ const char *label)
+{
+ s << "**" << label << ":** ";
+ for (qsizetype i = 0, size = classes.size(); i < size; ++i) {
+ if (i > 0)
+ s << ", ";
+ s << classRef(classes.at(i)->fullName());
+ }
+ s << "\n\n";
+}
+
+static void writeInheritedByList(TextStream &s, const AbstractMetaClassCPtr &metaClass,
+ const AbstractMetaClassCList& allClasses)
+{
+ AbstractMetaClassCList res;
+ for (const auto &c : allClasses) {
+ if (c != metaClass && inheritsFrom(c, metaClass))
+ res << c;
+ }
+
+ if (!res.isEmpty())
+ writeInheritanceList(s, res, "Inherited by");
+}
+
+static void writeInheritedFromList(TextStream &s, const AbstractMetaClassCPtr &metaClass)
+{
+ AbstractMetaClassCList res;
+
+ recurseClassHierarchy(metaClass, [&res, metaClass](const AbstractMetaClassCPtr &c) {
+ if (c.get() != metaClass.get())
+ res.append(c);
+ return false;
+ });
+
+ if (!res.isEmpty())
+ writeInheritanceList(s, res, "Inherits from");
+}
+
+void QtDocGenerator::generateClass(TextStream &s, const GeneratorContext &classContext)
+{
+ AbstractMetaClassCPtr metaClass = classContext.metaClass();
+ qCDebug(lcShibokenDoc).noquote().nospace() << "Generating Documentation for " << metaClass->fullName();
+
+ m_packages[metaClass->package()].classPages << fileNameForContext(classContext);
+
+ m_docParser->setPackageName(metaClass->package());
+ m_docParser->fillDocumentation(std::const_pointer_cast<AbstractMetaClass>(metaClass));
+
+ s << currentModule(metaClass->package()) << pyClass(metaClass->name());
+ Indentation indent(s);
+
+ auto documentation = metaClass->documentation();
+ const QString scope = classScope(metaClass);
+ if (documentation.hasBrief())
+ writeFormattedBriefText(s, documentation, scope);
+
+ if (!metaClass->baseClasses().isEmpty()) {
+ if (m_options.inheritanceDiagram) {
+ s << ".. inheritance-diagram:: " << metaClass->fullName()<< '\n'
+ << " :parts: 2\n\n";
+ } else {
+ writeInheritedFromList(s, metaClass);
+ }
+ }
+
+ writeInheritedByList(s, metaClass, api().classes());
+
+ const auto version = versionOf(metaClass->typeEntry());
+ if (!version.isNull())
+ s << rstVersionAdded(version);
+ if (metaClass->attributes().testFlag(AbstractMetaClass::Deprecated))
+ s << rstDeprecationNote("class");
+
+ const GeneratorDocumentation doc = generatorDocumentation(metaClass);
+
+ if (!doc.allFunctions.isEmpty() || !doc.properties.isEmpty()) {
+ s << '\n' << headline("Synopsis");
+ writePropertyToc(s, doc);
+ writeFunctionToc(s, u"Methods"_s, doc.tocNormalFunctions);
+ writeFunctionToc(s, u"Virtual methods"_s, doc.tocVirtuals);
+ writeFunctionToc(s, u"Slots"_s, doc.tocSlotFunctions);
+ writeFunctionToc(s, u"Signals"_s, doc.tocSignalFunctions);
+ writeFunctionToc(s, u"Static functions"_s, doc.tocStaticFunctions);
+ }
+
+ s << "\n.. note::\n"
+ " This documentation may contain snippets that were automatically\n"
+ " translated from C++ to Python. We always welcome contributions\n"
+ " to the snippet translation. If you see an issue with the\n"
+ " translation, you can also let us know by creating a ticket on\n"
+ " https:/bugreports.qt.io/projects/PYSIDE\n\n";
+
+ s << '\n' << headline("Detailed Description") << ".. _More:\n";
+
+ writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, metaClass);
+ if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, metaClass))
+ writeFormattedDetailedText(s, documentation, scope);
+ writeInjectDocumentation(s, TypeSystem::DocModificationAppend, metaClass);
+
+ writeEnums(s, metaClass->enums(), scope);
+
+ if (!doc.properties.isEmpty())
+ writeProperties(s, doc, metaClass);
+
+ if (!metaClass->isNamespace())
+ writeFields(s, metaClass);
+
+ writeFunctions(s, doc.allFunctions, metaClass, scope);
+}
+
+void QtDocGenerator::writeFunctionToc(TextStream &s, const QString &title,
+ const AbstractMetaFunctionCList &functions)
+{
+ if (!functions.isEmpty()) {
+ s << headline(title, '^')
+ << ".. container:: function_list\n\n" << indent;
+ // Functions are sorted by the Metabuilder; erase overloads
+ QStringList toc;
+ toc.reserve(functions.size());
+ std::transform(functions.cbegin(), functions.end(),
+ std::back_inserter(toc), getFuncName);
+ toc.erase(std::unique(toc.begin(), toc.end()), toc.end());
+ for (const auto &func : toc)
+ s << "* def " << functionRef(func) << '\n';
+ s << outdent << "\n\n";
+ }
+}
+
+void QtDocGenerator::writePropertyToc(TextStream &s,
+ const GeneratorDocumentation &doc)
+{
+ if (doc.properties.isEmpty())
+ return;
+
+ s << headline("Properties", '^')
+ << ".. container:: function_list\n\n" << indent;
+ for (const auto &prop : doc.properties) {
+ s << "* " << propRef(propertyRefTarget(prop.name));
+ if (prop.documentation.hasBrief())
+ s << " - " << prop.documentation.brief();
+ s << '\n';
+ }
+ s << outdent << "\n\n";
+}
+
+void QtDocGenerator::writeProperties(TextStream &s,
+ const GeneratorDocumentation &doc,
+ const AbstractMetaClassCPtr &cppClass) const
+{
+ s << "\n.. note:: Properties can be used directly when "
+ << "``from __feature__ import true_property`` is used or via accessor "
+ << "functions otherwise.\n\n";
+
+ const QString scope = classScope(cppClass);
+ for (const auto &prop : doc.properties) {
+ const QString type = translateToPythonType(prop.type, cppClass, /* createRef */ false);
+ s << ".. py:property:: " << propertyRefTarget(prop.name)
+ << "\n :type: " << type << "\n\n\n";
+ if (!prop.documentation.isEmpty())
+ writeFormattedText(s, prop.documentation.detailed(), Documentation::Native, scope);
+ s << "**Access functions:**\n";
+ if (prop.getter)
+ s << " * " << functionRef(prop.getter->name()) << '\n';
+ if (prop.setter)
+ s << " * " << functionRef(prop.setter->name()) << '\n';
+ if (prop.reset)
+ s << " * " << functionRef(prop.reset->name()) << '\n';
+ if (prop.notify)
+ s << " * Signal " << functionRef(prop.notify->name()) << '\n';
+ s << '\n';
+ }
+}
+
+void QtDocGenerator::writeEnums(TextStream &s, const AbstractMetaEnumList &enums,
+ const QString &scope) const
+{
+ for (const AbstractMetaEnum &en : enums) {
+ s << pyClass(en.name());
+ Indentation indent(s);
+ writeFormattedDetailedText(s, en.documentation(), scope);
+ const auto version = versionOf(en.typeEntry());
+ if (!version.isNull())
+ s << rstVersionAdded(version);
+ }
+
+}
+
+void QtDocGenerator::writeFields(TextStream &s, const AbstractMetaClassCPtr &cppClass) const
+{
+ constexpr auto section_title = ".. attribute:: "_L1;
+
+ const QString scope = classScope(cppClass);
+ for (const AbstractMetaField &field : cppClass->fields()) {
+ s << section_title << cppClass->fullName() << "." << field.name() << "\n\n";
+ writeFormattedDetailedText(s, field.documentation(), scope);
+ }
+}
+
+QString QtDocGenerator::formatArgs(const AbstractMetaFunctionCPtr &func)
+{
+ QString ret = u"("_s;
+ int optArgs = 0;
+
+ const AbstractMetaArgumentList &arguments = func->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+
+ if (arg.isModifiedRemoved())
+ continue;
+
+ bool thisIsoptional = !arg.defaultValueExpression().isEmpty();
+ if (optArgs || thisIsoptional) {
+ ret += u'[';
+ optArgs++;
+ }
+
+ if (arg.argumentIndex() > 0)
+ ret += u", "_s;
+
+ ret += arg.name();
+
+ if (thisIsoptional) {
+ QString defValue = arg.defaultValueExpression();
+ if (defValue == u"QString()") {
+ defValue = u"\"\""_s;
+ } else if (defValue == u"QStringList()"
+ || defValue.startsWith(u"QVector")
+ || defValue.startsWith(u"QList")) {
+ defValue = u"list()"_s;
+ } else if (defValue == u"QVariant()") {
+ defValue = none;
+ } else {
+ defValue.replace(u"::"_s, u"."_s);
+ if (defValue == u"nullptr")
+ defValue = none;
+ else if (defValue == u"0" && arg.type().isObject())
+ defValue = none;
+ }
+ ret += u'=' + defValue;
+ }
+ }
+
+ ret += QString(optArgs, u']') + u')';
+ return ret;
+}
+
+void QtDocGenerator::writeDocSnips(TextStream &s,
+ const CodeSnipList &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language)
+{
+ Indentation indentation(s);
+ static const QStringList invalidStrings{u"*"_s, u"//"_s, u"/*"_s, u"*/"_s};
+ const static QString startMarkup = u"[sphinx-begin]"_s;
+ const static QString endMarkup = u"[sphinx-end]"_s;
+
+ for (const CodeSnip &snip : codeSnips) {
+ if ((snip.position != position) ||
+ !(snip.language & language))
+ continue;
+
+ QString code = snip.code();
+ while (code.contains(startMarkup) && code.contains(endMarkup)) {
+ const auto startBlock = code.indexOf(startMarkup) + startMarkup.size();
+ const auto endBlock = code.indexOf(endMarkup);
+
+ if ((startBlock == -1) || (endBlock == -1))
+ break;
+
+ QString codeBlock = code.mid(startBlock, endBlock - startBlock);
+ const QStringList rows = codeBlock.split(u'\n');
+ int currentRow = 0;
+ qsizetype offset = 0;
+
+ for (QString row : rows) {
+ for (const QString &invalidString : std::as_const(invalidStrings))
+ row.remove(invalidString);
+
+ if (row.trimmed().size() == 0) {
+ if (currentRow == 0)
+ continue;
+ s << '\n';
+ }
+
+ if (currentRow == 0) {
+ //find offset
+ for (auto c : row) {
+ if (c == u' ')
+ offset++;
+ else if (c == u'\n')
+ offset = 0;
+ else
+ break;
+ }
+ }
+ s << QStringView{row}.mid(offset) << '\n';
+ currentRow++;
+ }
+
+ code = code.mid(endBlock+endMarkup.size());
+ }
+ }
+}
+
+bool QtDocGenerator::writeDocModifications(TextStream &s,
+ const DocModificationList &mods,
+ TypeSystem::DocModificationMode mode,
+ const QString &scope) const
+{
+ bool didSomething = false;
+ for (const DocModification &mod : mods) {
+ if (mod.mode() == mode) {
+ switch (mod.format()) {
+ case TypeSystem::NativeCode:
+ writeFormattedText(s, mod.code(), Documentation::Native, scope);
+ didSomething = true;
+ break;
+ case TypeSystem::TargetLangCode:
+ writeFormattedText(s, mod.code(), Documentation::Target, scope);
+ didSomething = true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return didSomething;
+}
+
+bool QtDocGenerator::writeInjectDocumentation(TextStream &s,
+ TypeSystem::DocModificationMode mode,
+ const AbstractMetaClassCPtr &cppClass) const
+{
+ const bool didSomething =
+ writeDocModifications(s, DocParser::getDocModifications(cppClass),
+ mode, classScope(cppClass));
+ s << '\n';
+
+ // FIXME PYSIDE-7: Deprecate the use of doc string on glue code.
+ // This is pre "add-function" and "inject-documentation" tags.
+ const TypeSystem::CodeSnipPosition pos = mode == TypeSystem::DocModificationPrepend
+ ? TypeSystem::CodeSnipPositionBeginning : TypeSystem::CodeSnipPositionEnd;
+ writeDocSnips(s, cppClass->typeEntry()->codeSnips(), pos, TypeSystem::TargetLangCode);
+ return didSomething;
+}
+
+bool QtDocGenerator::writeInjectDocumentation(TextStream &s,
+ TypeSystem::DocModificationMode mode,
+ const DocModificationList &modifications,
+ const AbstractMetaFunctionCPtr &func,
+ const QString &scope) const
+{
+ const bool didSomething = writeDocModifications(s, modifications, mode, scope);
+ s << '\n';
+
+ // FIXME PYSIDE-7: Deprecate the use of doc string on glue code.
+ // This is pre "add-function" and "inject-documentation" tags.
+ const TypeSystem::CodeSnipPosition pos = mode == TypeSystem::DocModificationPrepend
+ ? TypeSystem::CodeSnipPositionBeginning : TypeSystem::CodeSnipPositionEnd;
+ writeDocSnips(s, func->injectedCodeSnips(), pos, TypeSystem::TargetLangCode);
+ return didSomething;
+}
+
+static QString inline toRef(const QString &t)
+{
+ return ":class:`~"_L1 + t + u'`';
+}
+
+QString QtDocGenerator::translateToPythonType(const AbstractMetaType &type,
+ const AbstractMetaClassCPtr &cppClass,
+ bool createRef) const
+{
+ static const QStringList nativeTypes =
+ {boolT, floatT, intT, pyObjectT, pyStrT};
+
+ QString name = type.name();
+ if (nativeTypes.contains(name))
+ return name;
+
+ if (type.typeUsagePattern() == AbstractMetaType::PrimitivePattern) {
+ const auto &basicName = basicReferencedTypeEntry(type.typeEntry())->name();
+ if (AbstractMetaType::cppSignedIntTypes().contains(basicName)
+ || AbstractMetaType::cppUnsignedIntTypes().contains(basicName)) {
+ return intT;
+ }
+ if (AbstractMetaType::cppFloatTypes().contains(basicName))
+ return floatT;
+ }
+
+ static const QSet<QString> stringTypes = {
+ u"uchar"_s, u"std::string"_s, u"std::wstring"_s,
+ u"std::stringview"_s, u"std::wstringview"_s,
+ qStringT, u"QStringView"_s, u"QAnyStringView"_s, u"QUtf8StringView"_s
+ };
+ if (stringTypes.contains(name))
+ return pyStrT;
+
+ static const QHash<QString, QString> typeMap = {
+ { cPyObjectT, pyObjectT },
+ { u"QStringList"_s, u"list of strings"_s },
+ { qVariantT, pyObjectT }
+ };
+ const auto found = typeMap.constFind(name);
+ if (found != typeMap.cend())
+ return found.value();
+
+ if (type.isFlags()) {
+ const auto fte = std::static_pointer_cast<const FlagsTypeEntry>(type.typeEntry());
+ auto enumTypeEntry = fte->originator();
+ auto enumName = enumTypeEntry->targetLangName();
+ if (createRef)
+ enumName.prepend(enumTypeEntry->targetLangPackage() + u'.');
+ return "Combination of "_L1 + (createRef ? toRef(enumName) : enumName);
+ } else if (type.isEnum()) {
+ auto enumTypeEntry = std::static_pointer_cast<const EnumTypeEntry>(type.typeEntry());
+ auto enumName = enumTypeEntry->targetLangName();
+ if (createRef)
+ enumName.prepend(enumTypeEntry->targetLangPackage() + u'.');
+ return createRef ? toRef(enumName) : enumName;
+ }
+
+ if (type.isConstant() && name == "char"_L1 && type.indirections() == 1)
+ return "str"_L1;
+
+ if (type.isContainer()) {
+ QString strType = translateType(type, cppClass, Options(ExcludeConst) | ExcludeReference);
+ strType.remove(u'*');
+ strType.remove(u'>');
+ strType.remove(u'<');
+ strType.replace(u"::"_s, u"."_s);
+ if (strType.contains(u"QList") || strType.contains(u"QVector")) {
+ strType.replace(u"QList"_s, u"list of "_s);
+ strType.replace(u"QVector"_s, u"list of "_s);
+ } else if (strType.contains(u"QHash") || strType.contains(u"QMap")) {
+ strType.remove(u"QHash"_s);
+ strType.remove(u"QMap"_s);
+ QStringList types = strType.split(u',');
+ strType = QString::fromLatin1("Dictionary with keys of type %1 and values of type %2.")
+ .arg(types[0], types[1]);
+ }
+ return strType;
+ }
+
+ if (auto k = AbstractMetaClass::findClass(api().classes(), type.typeEntry()))
+ return createRef ? toRef(k->fullName()) : k->name();
+
+ return createRef ? toRef(name) : name;
+}
+
+QString QtDocGenerator::getFuncName(const AbstractMetaFunctionCPtr &cppFunc)
+{
+ if (cppFunc->isConstructor())
+ return "__init__"_L1;
+ QString result = cppFunc->name();
+ if (cppFunc->isOperatorOverload()) {
+ const QString pythonOperator = Generator::pythonOperatorFunctionName(result);
+ if (!pythonOperator.isEmpty())
+ return pythonOperator;
+ }
+ result.replace(u"::"_s, u"."_s);
+ return result;
+}
+
+void QtDocGenerator::writeParameterType(TextStream &s,
+ const AbstractMetaClassCPtr &cppClass,
+ const AbstractMetaArgument &arg) const
+{
+ s << ":param " << arg.name() << ": "
+ << translateToPythonType(arg.type(), cppClass) << '\n';
+}
+
+void QtDocGenerator::writeFunctionParametersType(TextStream &s,
+ const AbstractMetaClassCPtr &cppClass,
+ const AbstractMetaFunctionCPtr &func) const
+{
+ s << '\n';
+ const AbstractMetaArgumentList &funcArgs = func->arguments();
+ for (const AbstractMetaArgument &arg : funcArgs) {
+ if (!arg.isModifiedRemoved())
+ writeParameterType(s, cppClass, arg);
+ }
+
+ QString retType;
+ if (!func->isConstructor()) {
+ // check if the return type was modified
+ retType = func->modifiedTypeName();
+ if (retType.isEmpty() && !func->isVoid())
+ retType = translateToPythonType(func->type(), cppClass);
+ }
+
+ if (!retType.isEmpty())
+ s << ":rtype: " << retType << '\n';
+
+ s << '\n';
+}
+
+static bool containsFunctionDirective(const DocModification &dm)
+{
+ return dm.mode() != TypeSystem::DocModificationXPathReplace
+ && dm.code().contains(".. py:"_L1);
+}
+
+void QtDocGenerator::writeFunctions(TextStream &s, const AbstractMetaFunctionCList &funcs,
+ const AbstractMetaClassCPtr &cppClass, const QString &scope)
+{
+ QString lastName;
+ for (const auto &func : funcs) {
+ const bool indexed = func->name() != lastName;
+ lastName = func->name();
+ writeFunction(s, func, cppClass, scope, indexed);
+ }
+}
+
+void QtDocGenerator::writeFunction(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaClassCPtr &cppClass,
+ const QString &scope, bool indexed)
+{
+ const auto modifications = DocParser::getDocModifications(func, cppClass);
+
+ // Enable injecting parameter documentation by adding a complete function directive.
+ if (std::none_of(modifications.cbegin(), modifications.cend(), containsFunctionDirective)) {
+ if (func->ownerClass() == nullptr)
+ s << ".. py:function:: ";
+ else
+ s << (func->isStatic() ? ".. py:staticmethod:: " : ".. py:method:: ");
+ s << getFuncName(func) << formatArgs(func);
+ Indentation indentation(s);
+ if (!indexed)
+ s << "\n:noindex:";
+ if (func->cppAttributes().testFlag(FunctionAttribute::Final))
+ s << "\n:final:";
+ else if (func->isAbstract())
+ s << "\n:abstractmethod:";
+ s << "\n\n";
+ writeFunctionParametersType(s, cppClass, func);
+ const auto version = versionOf(func->typeEntry());
+ if (!version.isNull())
+ s << rstVersionAdded(version);
+ if (func->isDeprecated())
+ s << rstDeprecationNote("function");
+ }
+
+ writeFunctionDocumentation(s, func, modifications, scope);
+
+ if (auto propIndex = func->propertySpecIndex(); propIndex >= 0) {
+ const QString name = cppClass->propertySpecs().at(propIndex).name();
+ const QString target = propertyRefTarget(name);
+ if (func->isPropertyReader())
+ s << "\nGetter of property " << propRef(target) << " .\n\n";
+ else if (func->isPropertyWriter())
+ s << "\nSetter of property " << propRef(target) << " .\n\n";
+ else if (func->isPropertyResetter())
+ s << "\nReset function of property " << propRef(target) << " .\n\n";
+ else if (func->attributes().testFlag(AbstractMetaFunction::Attribute::PropertyNotify))
+ s << "\nNotification signal of property " << propRef(target) << " .\n\n";
+ }
+}
+
+void QtDocGenerator::writeFunctionDocumentation(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ const DocModificationList &modifications,
+ const QString &scope) const
+
+{
+ writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, modifications, func, scope);
+ if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, modifications, func, scope)) {
+ writeFormattedBriefText(s, func->documentation(), scope);
+ writeFormattedDetailedText(s, func->documentation(), scope);
+ }
+ writeInjectDocumentation(s, TypeSystem::DocModificationAppend, modifications, func, scope);
+}
+
+static QStringList fileListToToc(const QStringList &items)
+{
+ QStringList result;
+ result.reserve(items.size());
+ std::transform(items.cbegin(), items.cend(), std::back_inserter(result),
+ fileNameToTocEntry);
+ return result;
+}
+
+static QStringList functionListToToc(const AbstractMetaFunctionCList &functions)
+{
+ QStringList result;
+ result.reserve(functions.size());
+ for (const auto &f : functions)
+ result.append(f->name());
+ // Functions are sorted by the Metabuilder; erase overloads
+ result.erase(std::unique(result.begin(), result.end()), result.end());
+ return result;
+}
+
+static QStringList enumListToToc(const AbstractMetaEnumList &enums)
+{
+ QStringList result;
+ result.reserve(enums.size());
+ for (const auto &e : enums)
+ result.append(e.name());
+ return result;
+}
+
+// Sort entries for a TOC by first character, dropping the
+// leading common Qt prefixes like 'Q'.
+static QChar sortKey(const QString &key)
+{
+ const auto size = key.size();
+ if (size >= 2 && (key.at(0) == u'Q' || key.at(0) == u'q')
+ && (key.at(1).isUpper() || key.at(1).isDigit())) {
+ return key.at(1); // "QClass" -> 'C', "qSin()" -> 'S', 'Q3DSurfaceWidget' -> '3'
+ }
+ if (size >= 3 && key.startsWith("Q_"_L1))
+ return key.at(2).toUpper(); // "Q_ARG" -> 'A'
+ if (size >= 4 && key.startsWith("QT_"_L1))
+ return key.at(3).toUpper(); // "QT_TR" -> 'T'
+ auto idx = 0;
+ for (; idx < size && key.at(idx) == u'_'; ++idx) {
+ } // "__init__" -> 'I'
+ return idx < size ? key.at(idx).toUpper() : u'A';
+}
+
+static void writeFancyToc(TextStream& s, QAnyStringView title,
+ const QStringList& items,
+ QLatin1StringView referenceType)
+{
+ using TocMap = QMap<QChar, QStringList>;
+
+ if (items.isEmpty())
+ return;
+
+ TocMap tocMap;
+ for (const QString &item : items)
+ tocMap[sortKey(item)] << item;
+
+ static const qsizetype numColumns = 4;
+
+ QtXmlToSphinx::Table table;
+ for (auto it = tocMap.cbegin(), end = tocMap.cend(); it != end; ++it) {
+ QtXmlToSphinx::TableRow row;
+ const QString charEntry = u"**"_s + it.key() + u"**"_s;
+ row << QtXmlToSphinx::TableCell(charEntry);
+ for (const QString &item : std::as_const(it.value())) {
+ if (row.size() >= numColumns) {
+ table.appendRow(row);
+ row.clear();
+ row << QtXmlToSphinx::TableCell(QString{});
+ }
+ const QString entry = "* :"_L1 + referenceType + ":`"_L1 + item + u'`';
+ row << QtXmlToSphinx::TableCell(entry);
+ }
+ if (row.size() > 1)
+ table.appendRow(row);
+ }
+
+ table.normalize();
+ s << '\n' << headline(title) << ".. container:: pysidetoc\n\n";
+ table.format(s);
+}
+
+bool QtDocGenerator::finishGeneration()
+{
+ for (const auto &f : api().globalFunctions()) {
+ auto ncf = std::const_pointer_cast<AbstractMetaFunction>(f);
+ m_docParser->fillGlobalFunctionDocumentation(ncf);
+ m_packages[f->targetLangPackage()].globalFunctions.append(f);
+ }
+
+ for (auto e : api().globalEnums()) {
+ m_docParser->fillGlobalEnumDocumentation(e);
+ m_packages[e.typeEntry()->targetLangPackage()].globalEnums.append(e);
+ }
+
+ if (!m_packages.isEmpty())
+ writeModuleDocumentation();
+ if (!m_options.additionalDocumentationList.isEmpty())
+ writeAdditionalDocumentation();
+ if (!m_options.inheritanceFile.isEmpty() && !writeInheritanceFile())
+ return false;
+ return true;
+}
+
+bool QtDocGenerator::writeInheritanceFile()
+{
+ QFile inheritanceFile(m_options.inheritanceFile);
+ if (!inheritanceFile.open(QIODevice::WriteOnly | QIODevice::Text))
+ throw Exception(msgCannotOpenForWriting(m_options.inheritanceFile));
+
+ QJsonObject dict;
+ for (const auto &c : api().classes()) {
+ const auto &bases = c->baseClasses();
+ if (!bases.isEmpty()) {
+ QJsonArray list;
+ for (const auto &base : bases)
+ list.append(QJsonValue(base->fullName()));
+ dict[c->fullName()] = list;
+ }
+ }
+ QJsonDocument document;
+ document.setObject(dict);
+ inheritanceFile.write(document.toJson(QJsonDocument::Compact));
+ return true;
+}
+
+// Remove function entries that have extra documentation pages
+static inline void removeExtraDocs(const QStringList &extraTocEntries,
+ AbstractMetaFunctionCList *functions)
+{
+ auto predicate = [&extraTocEntries](const AbstractMetaFunctionCPtr &f) {
+ return extraTocEntries.contains(f->name());
+ };
+ functions->erase(std::remove_if(functions->begin(),functions->end(), predicate),
+ functions->end());
+}
+
+void QtDocGenerator::writeModuleDocumentation()
+{
+ for (auto it = m_packages.begin(), end = m_packages.end(); it != end; ++it) {
+ auto &docPackage = it.value();
+ std::sort(docPackage.classPages.begin(), docPackage.classPages.end());
+
+ QString key = it.key();
+ key.replace(u'.', u'/');
+ QString outputDir = outputDirectory() + u'/' + key;
+ FileOut output(outputDir + u"/index.rst"_s);
+ TextStream& s = output.stream;
+
+ const QString &title = it.key();
+ s << ".. module:: " << title << "\n\n" << headline(title, '*');
+
+ // Store the it.key() in a QString so that it can be stripped off unwanted
+ // information when neeeded. For example, the RST files in the extras directory
+ // doesn't include the PySide# prefix in their names.
+ QString moduleName = it.key();
+ const int lastIndex = moduleName.lastIndexOf(u'.');
+ if (lastIndex >= 0)
+ moduleName.remove(0, lastIndex + 1);
+
+ // Search for extra-sections
+ QStringList extraTocEntries;
+ if (!m_options.extraSectionDir.isEmpty()) {
+ QDir extraSectionDir(m_options.extraSectionDir);
+ if (!extraSectionDir.exists()) {
+ const QString m = u"Extra sections directory "_s +
+ m_options.extraSectionDir + u" doesn't exist"_s;
+ throw Exception(m);
+ }
+
+ // Filter for "QtCore.Property.rst", skipping module doc "QtCore.rst"
+ const QString filter = moduleName + u".?*.rst"_s;
+ const auto fileList =
+ extraSectionDir.entryInfoList({filter}, QDir::Files, QDir::Name);
+ for (const auto &fi : fileList)
+ readExtraDoc(fi, moduleName, outputDir, &docPackage, &extraTocEntries);
+ }
+
+ removeExtraDocs(extraTocEntries, &docPackage.globalFunctions);
+ const bool hasGlobals = !docPackage.globalFunctions.isEmpty()
+ || !docPackage.globalEnums.isEmpty();
+ const QString globalsPage = moduleName + "_globals.rst"_L1;
+
+ s << ".. container:: hide\n\n" << indent
+ << ".. toctree::\n" << indent
+ << ":maxdepth: 1\n\n";
+ if (hasGlobals)
+ s << globalsPage << '\n';
+ for (const QString &className : std::as_const(docPackage.classPages))
+ s << className << '\n';
+ s << "\n\n" << outdent << outdent << headline("Detailed Description");
+
+ // module doc is always wrong and C++istic, so go straight to the extra directory!
+ QFile moduleDoc(m_options.extraSectionDir + u'/' + moduleName
+ + u".rst"_s);
+ if (moduleDoc.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ s << moduleDoc.readAll();
+ moduleDoc.close();
+ } else {
+ // try the normal way
+ Documentation moduleDoc = m_docParser->retrieveModuleDocumentation(it.key());
+ if (moduleDoc.format() == Documentation::Native) {
+ QString context = it.key();
+ QtXmlToSphinx::stripPythonQualifiers(&context);
+ QtXmlToSphinx x(this, m_options.parameters, moduleDoc.detailed(), context);
+ s << x;
+ } else {
+ s << moduleDoc.detailed();
+ }
+ }
+
+ writeFancyToc(s, "List of Classes", fileListToToc(docPackage.classPages),
+ "class"_L1);
+ writeFancyToc(s, "List of Decorators", fileListToToc(docPackage.decoratorPages),
+ "deco"_L1);
+ writeFancyToc(s, "List of Functions", functionListToToc(docPackage.globalFunctions),
+ "py:func"_L1);
+ writeFancyToc(s, "List of Enumerations", enumListToToc(docPackage.globalEnums),
+ "any"_L1);
+
+ output.done();
+
+ if (hasGlobals)
+ writeGlobals(it.key(), outputDir + u'/' + globalsPage, docPackage);
+ }
+}
+
+void QtDocGenerator::writeGlobals(const QString &package,
+ const QString &fileName,
+ const DocPackage &docPackage)
+{
+ FileOut output(fileName);
+ TextStream &s = output.stream;
+
+ // Write out functions with injected documentation
+ if (!docPackage.globalFunctions.isEmpty()) {
+ s << currentModule(package) << headline("Functions");
+ writeFunctions(s, docPackage.globalFunctions, {}, {});
+ }
+
+ if (!docPackage.globalEnums.isEmpty()) {
+ s << headline("Enumerations");
+ writeEnums(s, docPackage.globalEnums, package);
+ }
+
+ output.done();
+}
+
+static inline QString msgNonExistentAdditionalDocFile(const QString &dir,
+ const QString &fileName)
+{
+ QString result;
+ QTextStream(&result) << "Additional documentation file \""
+ << fileName << "\" does not exist in "
+ << QDir::toNativeSeparators(dir) << '.';
+ return result;
+}
+
+void QtDocGenerator::writeAdditionalDocumentation() const
+{
+ QFile additionalDocumentationFile(m_options.additionalDocumentationList);
+ if (!additionalDocumentationFile.open(QIODevice::ReadOnly | QIODevice::Text))
+ throw Exception(msgCannotOpenForReading(additionalDocumentationFile));
+
+ QDir outDir(outputDirectory());
+ const QString rstSuffix = fileNameSuffix();
+
+ QString errorMessage;
+ int successCount = 0;
+ int count = 0;
+
+ QString targetDir = outDir.absolutePath();
+
+ while (!additionalDocumentationFile.atEnd()) {
+ const QByteArray lineBA = additionalDocumentationFile.readLine().trimmed();
+ if (lineBA.isEmpty() || lineBA.startsWith('#'))
+ continue;
+ const QString line = QFile::decodeName(lineBA);
+ // Parse "[directory]" specification
+ if (line.size() > 2 && line.startsWith(u'[') && line.endsWith(u']')) {
+ const QString dir = line.mid(1, line.size() - 2);
+ if (dir.isEmpty() || dir == u".") {
+ targetDir = outDir.absolutePath();
+ } else {
+ if (!outDir.exists(dir) && !outDir.mkdir(dir)) {
+ const QString m = "Cannot create directory "_L1
+ + dir + " under "_L1
+ + QDir::toNativeSeparators(outputDirectory());
+ throw Exception(m);
+ }
+ targetDir = outDir.absoluteFilePath(dir);
+ }
+ } else {
+ // Normal file entry
+ QFileInfo fi(m_options.parameters.docDataDir + u'/' + line);
+ if (fi.isFile()) {
+ const QString rstFileName = fi.baseName() + rstSuffix;
+ const QString rstFile = targetDir + u'/' + rstFileName;
+ const QString context = targetDir.mid(targetDir.lastIndexOf(u'/') + 1);
+ if (convertToRst(fi.absoluteFilePath(),
+ rstFile, context, &errorMessage)) {
+ ++successCount;
+ qCDebug(lcShibokenDoc).nospace().noquote() << __FUNCTION__
+ << " converted " << fi.fileName()
+ << ' ' << rstFileName;
+ } else {
+ qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
+ }
+ } else {
+ // FIXME: This should be an exception, in principle, but it
+ // requires building all modules.
+ qCWarning(lcShibokenDoc, "%s",
+ qPrintable(msgNonExistentAdditionalDocFile(m_options.parameters.docDataDir, line)));
+ }
+ ++count;
+ }
+ }
+ additionalDocumentationFile.close();
+
+ qCInfo(lcShibokenDoc, "Created %d/%d additional documentation files.",
+ successCount, count);
+}
+
+#ifdef __WIN32__
+# define PATH_SEP ';'
+#else
+# define PATH_SEP ':'
+#endif
+
+bool QtDocGenerator::doSetup()
+{
+ if (m_options.parameters.codeSnippetDirs.isEmpty()) {
+ m_options.parameters.codeSnippetDirs =
+ m_options.parameters.libSourceDir.split(QLatin1Char(PATH_SEP));
+ }
+
+ if (m_docParser.isNull()) {
+ if (m_options.doxygen)
+ m_docParser.reset(new DoxygenParser);
+ else
+ m_docParser.reset(new QtDocParser);
+ }
+
+ if (m_options.parameters.libSourceDir.isEmpty()
+ || m_options.parameters.docDataDir.isEmpty()) {
+ qCWarning(lcShibokenDoc) << "Documentation data dir and/or Qt source dir not informed, "
+ "documentation will not be extracted from Qt sources.";
+ return false;
+ }
+
+ m_docParser->setDocumentationDataDirectory(m_options.parameters.docDataDir);
+ m_docParser->setLibrarySourceDirectory(m_options.parameters.libSourceDir);
+ m_options.parameters.outputDirectory = outputDirectory();
+ return true;
+}
+
+QList<OptionDescription> QtDocGenerator::options()
+{
+ return {
+ {u"doc-parser=<parser>"_s,
+ u"The documentation parser used to interpret the documentation\n"
+ "input files (qdoc|doxygen)"_s},
+ {u"documentation-code-snippets-dir=<dir>"_s,
+ u"Directory used to search code snippets used by the documentation"_s},
+ {u"snippets-path-rewrite=old:new"_s,
+ u"Replacements in code snippet path to find .cpp/.h snippets converted to Python"_s},
+ {u"documentation-data-dir=<dir>"_s,
+ u"Directory with XML files generated by documentation tool"_s},
+ {u"documentation-extra-sections-dir=<dir>"_s,
+ u"Directory used to search for extra documentation sections"_s},
+ {u"library-source-dir=<dir>"_s,
+ u"Directory where library source code is located"_s},
+ {additionalDocumentationOption + u"=<file>"_s,
+ u"List of additional XML files to be converted to .rst files\n"
+ "(for example, tutorials)."_s},
+ {u"inheritance-file=<file>"_s,
+ u"Generate a JSON file containing the class inheritance."_s},
+ {u"disable-inheritance-diagram"_s,
+ u"Disable the generation of the inheritance diagram."_s}
+ };
+}
+
+class QtDocGeneratorOptionsParser : public OptionsParser
+{
+public:
+ explicit QtDocGeneratorOptionsParser(DocGeneratorOptions *o) : m_options(o) {}
+
+ bool handleBoolOption(const QString &key, OptionSource source) override;
+ bool handleOption(const QString &key, const QString &value, OptionSource source) override;
+
+private:
+ DocGeneratorOptions *m_options;
+};
+
+bool QtDocGeneratorOptionsParser::handleBoolOption(const QString &key, OptionSource)
+{
+ if (key == "disable-inheritance-diagram"_L1) {
+ m_options->inheritanceDiagram = false;
+ return true;
+ }
+ return false;
+}
+
+bool QtDocGeneratorOptionsParser::handleOption(const QString &key, const QString &value,
+ OptionSource source)
+{
+ if (source == OptionSource::CommandLineSingleDash)
+ return false;
+ if (key == u"library-source-dir") {
+ m_options->parameters.libSourceDir = value;
+ return true;
+ }
+ if (key == u"documentation-data-dir") {
+ m_options->parameters.docDataDir = value;
+ return true;
+ }
+ if (key == u"documentation-code-snippets-dir") {
+ m_options->parameters.codeSnippetDirs = value.split(QLatin1Char(PATH_SEP));
+ return true;
+ }
+
+ if (key == u"snippets-path-rewrite") {
+ const auto pos = value.indexOf(u':');
+ if (pos == -1)
+ return false;
+ m_options->parameters.codeSnippetRewriteOld= value.left(pos);
+ m_options->parameters.codeSnippetRewriteNew = value.mid(pos + 1);
+ return true;
+ }
+
+ if (key == u"documentation-extra-sections-dir") {
+ m_options->extraSectionDir = value;
+ return true;
+ }
+ if (key == u"doc-parser") {
+ qCDebug(lcShibokenDoc).noquote().nospace() << "doc-parser: " << value;
+ if (value == u"doxygen")
+ m_options->doxygen = true;
+ return true;
+ }
+ if (key == additionalDocumentationOption) {
+ m_options->additionalDocumentationList = value;
+ return true;
+ }
+
+ if (key == u"inheritance-file") {
+ m_options->inheritanceFile = value;
+ return true;
+ }
+
+ return false;
+}
+
+std::shared_ptr<OptionsParser> QtDocGenerator::createOptionsParser()
+{
+ return std::make_shared<QtDocGeneratorOptionsParser>(&m_options);
+}
+
+bool QtDocGenerator::convertToRst(const QString &sourceFileName,
+ const QString &targetFileName,
+ const QString &context,
+ QString *errorMessage) const
+{
+ QFile sourceFile(sourceFileName);
+ if (!sourceFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ if (errorMessage)
+ *errorMessage = msgCannotOpenForReading(sourceFile);
+ return false;
+ }
+ const QString doc = QString::fromUtf8(sourceFile.readAll());
+ sourceFile.close();
+
+ FileOut targetFile(targetFileName);
+ QtXmlToSphinx x(this, m_options.parameters, doc, context);
+ targetFile.stream << x;
+ targetFile.done();
+ return true;
+}
+
+GeneratorDocumentation
+ QtDocGenerator::generatorDocumentation(const AbstractMetaClassCPtr &cppClass)
+{
+ GeneratorDocumentation result;
+ const auto allFunctions = cppClass->functions();
+ result.allFunctions.reserve(allFunctions.size());
+ std::remove_copy_if(allFunctions.cbegin(), allFunctions.cend(),
+ std::back_inserter(result.allFunctions), shouldSkip);
+
+ std::stable_sort(result.allFunctions.begin(), result.allFunctions.end(), functionSort);
+
+ for (const auto &func : std::as_const(result.allFunctions)) {
+ if (func->isStatic())
+ result.tocStaticFunctions.append(func);
+ else if (func->isVirtual())
+ result.tocVirtuals.append(func);
+ else if (func->isSignal())
+ result.tocSignalFunctions.append(func);
+ else if (func->isSlot())
+ result.tocSlotFunctions.append(func);
+ else
+ result.tocNormalFunctions.append(func);
+ }
+
+ // Find the property getters/setters
+ for (const auto &spec: cppClass->propertySpecs()) {
+ GeneratorDocumentation::Property property;
+ property.name = spec.name();
+ property.type = spec.type();
+ property.documentation = spec.documentation();
+ if (!spec.read().isEmpty())
+ property.getter = AbstractMetaFunction::find(result.allFunctions, spec.read());
+ if (!spec.write().isEmpty())
+ property.setter = AbstractMetaFunction::find(result.allFunctions, spec.write());
+ if (!spec.reset().isEmpty())
+ property.reset = AbstractMetaFunction::find(result.allFunctions, spec.reset());
+ if (!spec.notify().isEmpty())
+ property.notify = AbstractMetaFunction::find(result.tocSignalFunctions, spec.notify());
+ result.properties.append(property);
+ }
+ std::sort(result.properties.begin(), result.properties.end());
+
+ return result;
+}
+
+// QtXmlToSphinxDocGeneratorInterface
+QString QtDocGenerator::expandFunction(const QString &function) const
+{
+ const auto firstDot = function.indexOf(u'.');
+ AbstractMetaClassCPtr metaClass;
+ if (firstDot != -1) {
+ const auto className = QStringView{function}.left(firstDot);
+ for (const auto &cls : api().classes()) {
+ if (cls->name() == className) {
+ metaClass = cls;
+ break;
+ }
+ }
+ }
+
+ return metaClass
+ ? metaClass->typeEntry()->qualifiedTargetLangName()
+ + function.right(function.size() - firstDot)
+ : function;
+}
+
+QString QtDocGenerator::expandClass(const QString &context,
+ const QString &name) const
+{
+ if (auto typeEntry = TypeDatabase::instance()->findType(name))
+ return typeEntry->qualifiedTargetLangName();
+ // fall back to the old heuristic if the type wasn't found.
+ QString result = name;
+ const auto rawlinklist = QStringView{name}.split(u'.');
+ QStringList splittedContext = context.split(u'.');
+ if (rawlinklist.size() == 1 || rawlinklist.constFirst() == splittedContext.constLast()) {
+ splittedContext.removeLast();
+ result.prepend(u'~' + splittedContext.join(u'.') + u'.');
+ }
+ return result;
+}
+
+QString QtDocGenerator::resolveContextForMethod(const QString &context,
+ const QString &methodName) const
+{
+ const auto currentClass = QStringView{context}.split(u'.').constLast();
+
+ AbstractMetaClassCPtr metaClass;
+ for (const auto &cls : api().classes()) {
+ if (cls->name() == currentClass) {
+ metaClass = cls;
+ break;
+ }
+ }
+
+ if (metaClass) {
+ AbstractMetaFunctionCList funcList;
+ const auto &methods = metaClass->queryFunctionsByName(methodName);
+ for (const auto &func : methods) {
+ if (methodName == func->name())
+ funcList.append(func);
+ }
+
+ AbstractMetaClassCPtr implementingClass;
+ for (const auto &func : std::as_const(funcList)) {
+ implementingClass = func->implementingClass();
+ if (implementingClass->name() == currentClass)
+ break;
+ }
+
+ if (implementingClass)
+ return implementingClass->typeEntry()->qualifiedTargetLangName();
+ }
+
+ return u'~' + context;
+}
+
+const QLoggingCategory &QtDocGenerator::loggingCategory() const
+{
+ return lcShibokenDoc();
+}
+
+static bool isRelativeHtmlFile(const QString &linkRef)
+{
+ return !linkRef.startsWith(u"http")
+ && (linkRef.endsWith(u".html") || linkRef.contains(u".html#"));
+}
+
+// Resolve relative, local .html documents links to doc.qt.io as they
+// otherwise will not work and neither be found in the HTML tree.
+QtXmlToSphinxLink QtDocGenerator::resolveLink(const QtXmlToSphinxLink &link) const
+{
+ if (link.type != QtXmlToSphinxLink::Reference || !isRelativeHtmlFile(link.linkRef))
+ return link;
+ static const QString prefix = "https://doc.qt.io/qt-"_L1
+ + QString::number(QT_VERSION_MAJOR) + u'/';
+ QtXmlToSphinxLink resolved = link;
+ resolved.type = QtXmlToSphinxLink::External;
+ resolved.linkRef = prefix + link.linkRef;
+ if (resolved.linkText.isEmpty()) {
+ resolved.linkText = link.linkRef;
+ const qsizetype anchor = resolved.linkText.lastIndexOf(u'#');
+ if (anchor != -1)
+ resolved.linkText.truncate(anchor);
+ }
+ return resolved;
+}
+
+QtXmlToSphinxDocGeneratorInterface::Image
+ QtDocGenerator::resolveImage(const QString &href, const QString &context) const
+{
+ QString relativeSourceDir = href;
+ const QString source = m_options.parameters.docDataDir + u'/' + relativeSourceDir;
+ if (!QFileInfo::exists(source))
+ throw Exception(msgCannotFindImage(href, context,source));
+
+ // Determine target directory from context, "Pyside2.QtGui.QPainter" ->"Pyside2/QtGui".
+ // FIXME: Not perfect yet, should have knowledge about namespaces (DataVis3D) or
+ // nested classes "Pyside2.QtGui.QTouchEvent.QTouchPoint".
+ QString relativeTargetDir = context;
+ const auto lastDot = relativeTargetDir.lastIndexOf(u'.');
+ if (lastDot != -1)
+ relativeTargetDir.truncate(lastDot);
+ relativeTargetDir.replace(u'.', u'/');
+ if (!relativeTargetDir.isEmpty())
+ relativeTargetDir += u'/';
+ relativeTargetDir += href;
+
+ return {relativeSourceDir, relativeTargetDir};
+}
diff --git a/sources/shiboken6/generator/qtdoc/qtdocgenerator.h b/sources/shiboken6/generator/qtdoc/qtdocgenerator.h
new file mode 100644
index 000000000..56e15e2a1
--- /dev/null
+++ b/sources/shiboken6/generator/qtdoc/qtdocgenerator.h
@@ -0,0 +1,130 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#ifndef DOCGENERATOR_H
+#define DOCGENERATOR_H
+
+#include <QtCore/QStringList>
+#include <QtCore/QMap>
+#include <QtCore/QScopedPointer>
+
+#include "generator.h"
+#include "documentation.h"
+#include <optionsparser.h>
+#include "typesystem_enums.h"
+#include "modifications_typedefs.h"
+#include "qtxmltosphinxinterface.h"
+
+class DocParser;
+struct DocGeneratorOptions;
+struct GeneratorDocumentation;
+struct DocPackage;
+
+/**
+* The DocGenerator generates documentation from library being binded.
+*/
+class QtDocGenerator : public Generator, public QtXmlToSphinxDocGeneratorInterface
+{
+public:
+ Q_DISABLE_COPY_MOVE(QtDocGenerator)
+
+ QtDocGenerator();
+ ~QtDocGenerator();
+
+ bool doSetup() override;
+
+ const char* name() const override
+ {
+ return "QtDocGenerator";
+ }
+
+ static QList<OptionDescription> options();
+ static std::shared_ptr<OptionsParser> createOptionsParser();
+
+ // QtXmlToSphinxDocGeneratorInterface
+ QString expandFunction(const QString &function) const override;
+ QString expandClass(const QString &context,
+ const QString &name) const override;
+ QString resolveContextForMethod(const QString &context,
+ const QString &methodName) const override;
+ const QLoggingCategory &loggingCategory() const override;
+ QtXmlToSphinxLink resolveLink(const QtXmlToSphinxLink &) const override;
+ Image resolveImage(const QString &href, const QString &context) const override;
+
+ static QString getFuncName(const AbstractMetaFunctionCPtr &cppFunc);
+ static QString formatArgs(const AbstractMetaFunctionCPtr &func);
+
+protected:
+ bool shouldGenerate(const TypeEntryCPtr &) const override;
+ static QString fileNameSuffix();
+ QString fileNameForContext(const GeneratorContext &context) const override;
+ void generateClass(TextStream &ts, const GeneratorContext &classContext) override;
+ bool finishGeneration() override;
+
+private:
+ void writeEnums(TextStream &s, const AbstractMetaEnumList &enums,
+ const QString &scope) const;
+
+ void writeFields(TextStream &s, const AbstractMetaClassCPtr &cppClass) const;
+ void writeFunctions(TextStream &s, const AbstractMetaFunctionCList &funcs,
+ const AbstractMetaClassCPtr &cppClass, const QString &scope);
+ void writeFunction(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaClassCPtr &cppClass = {},
+ const QString &scope = {}, bool indexed = true);
+ void writeFunctionDocumentation(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ const DocModificationList &modifications,
+ const QString &scope) const;
+ void writeFunctionParametersType(TextStream &s, const AbstractMetaClassCPtr &cppClass,
+ const AbstractMetaFunctionCPtr &func) const;
+ static void writeFunctionToc(TextStream &s, const QString &title,
+ const AbstractMetaFunctionCList &functions);
+ static void writePropertyToc(TextStream &s,
+ const GeneratorDocumentation &doc);
+ void writeProperties(TextStream &s,
+ const GeneratorDocumentation &doc,
+ const AbstractMetaClassCPtr &cppClass) const;
+ void writeParameterType(TextStream &s, const AbstractMetaClassCPtr &cppClass,
+ const AbstractMetaArgument &arg) const;
+ void writeFormattedText(TextStream &s, const QString &doc,
+ Documentation::Format format,
+ const QString &scope = {}) const;
+ void writeFormattedBriefText(TextStream &s, const Documentation &doc,
+ const QString &scope = {}) const;
+ void writeFormattedDetailedText(TextStream &s, const Documentation &doc,
+ const QString &scope = {}) const;
+
+ bool writeInjectDocumentation(TextStream &s, TypeSystem::DocModificationMode mode,
+ const AbstractMetaClassCPtr &cppClass) const;
+ bool writeInjectDocumentation(TextStream &s, TypeSystem::DocModificationMode mode,
+ const DocModificationList &modifications,
+ const AbstractMetaFunctionCPtr &func,
+ const QString &scope = {}) const;
+ bool writeDocModifications(TextStream &s, const DocModificationList &mods,
+ TypeSystem::DocModificationMode mode,
+ const QString &scope = {}) const;
+ static void writeDocSnips(TextStream &s, const CodeSnipList &codeSnips,
+ TypeSystem::CodeSnipPosition position, TypeSystem::Language language);
+
+ void writeModuleDocumentation();
+ void writeGlobals(const QString &package, const QString &fileName,
+ const DocPackage &docPackage);
+ void writeAdditionalDocumentation() const;
+ bool writeInheritanceFile();
+
+ QString translateToPythonType(const AbstractMetaType &type,
+ const AbstractMetaClassCPtr &cppClass,
+ bool createRef = true) const;
+
+ bool convertToRst(const QString &sourceFileName,
+ const QString &targetFileName,
+ const QString &context = QString(),
+ QString *errorMessage = nullptr) const;
+
+ static GeneratorDocumentation generatorDocumentation(const AbstractMetaClassCPtr &cppClass);
+
+ QStringList m_functionList;
+ QMap<QString, DocPackage> m_packages;
+ QScopedPointer<DocParser> m_docParser;
+ static DocGeneratorOptions m_options;
+};
+
+#endif // DOCGENERATOR_H
diff --git a/sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp b/sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp
new file mode 100644
index 000000000..b8fec836c
--- /dev/null
+++ b/sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp
@@ -0,0 +1,1643 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qtxmltosphinx.h"
+#include "exception.h"
+#include "qtxmltosphinxinterface.h"
+#include <codesniphelpers.h>
+#include "rstformat.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QHash>
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QRegularExpression>
+#include <QtCore/QXmlStreamReader>
+
+using namespace Qt::StringLiterals;
+
+QString msgTagWarning(const QXmlStreamReader &reader, const QString &context,
+ const QString &tag, const QString &message)
+{
+ QString result;
+ QTextStream str(&result);
+ str << "While handling <";
+ const auto currentTag = reader.name();
+ if (currentTag.isEmpty())
+ str << tag;
+ else
+ str << currentTag;
+ str << "> in " << context << ", line "<< reader.lineNumber()
+ << ": " << message;
+ return result;
+}
+
+QString msgFallbackWarning(const QString &location, const QString &identifier,
+ const QString &fallback)
+{
+ QString message = u"Falling back to \""_s
+ + QDir::toNativeSeparators(fallback) + u"\" for \""_s
+ + location + u'"';
+ if (!identifier.isEmpty())
+ message += u" ["_s + identifier + u']';
+ return message;
+}
+
+QString msgSnippetsResolveError(const QString &path, const QStringList &locations)
+{
+ QString result;
+ QTextStream(&result) << "Could not resolve \"" << path << R"(" in ")"
+ << locations.join(uR"(", ")"_s);
+ return result;
+}
+
+static bool isHttpLink(const QString &ref)
+{
+ return ref.startsWith(u"http://") || ref.startsWith(u"https://");
+}
+
+static QString trimRight(QString s)
+{
+ while (!s.isEmpty() && s.crbegin()->isSpace())
+ s.chop(1);
+ return s;
+}
+
+static QString trimLeadingNewlines(QString s)
+{
+ while (!s.isEmpty() && s.at(0) == u'\n')
+ s.remove(0, 1);
+ return s;
+}
+
+QDebug operator<<(QDebug d, const QtXmlToSphinxLink &l)
+{
+ static const QHash<QtXmlToSphinxLink::Type, const char *> typeName = {
+ {QtXmlToSphinxLink::Method, "Method"},
+ {QtXmlToSphinxLink::Function, "Function"},
+ {QtXmlToSphinxLink::Class, "Class"},
+ {QtXmlToSphinxLink::Attribute, "Attribute"},
+ {QtXmlToSphinxLink::Module, "Module"},
+ {QtXmlToSphinxLink::Reference, "Reference"},
+ {QtXmlToSphinxLink::External, "External"},
+ };
+
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "QtXmlToSphinxLinkContext(" << typeName.value(l.type, "") << ", ref=\""
+ << l.linkRef << '"';
+ if (!l.linkText.isEmpty())
+ d << ", text=\"" << l.linkText << '"';
+ d << ')';
+ return d;
+}
+
+QDebug operator<<(QDebug debug, const QtXmlToSphinx::TableCell &c)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "Cell(\"" << c.data << '"';
+ if (c.colSpan != 0)
+ debug << ", colSpan=" << c.colSpan;
+ if (c.rowSpan != 0)
+ debug << ", rowSpan=" << c.rowSpan;
+ debug << ')';
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const QtXmlToSphinx::Table &t)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ t.formatDebug(debug);
+ return debug;
+}
+
+static const char *linkKeyWord(QtXmlToSphinxLink::Type type)
+{
+ switch (type) {
+ case QtXmlToSphinxLink::Method:
+ return ":meth:";
+ case QtXmlToSphinxLink::Function:
+ return ":func:";
+ case QtXmlToSphinxLink::Class:
+ return ":class:";
+ case QtXmlToSphinxLink::Attribute:
+ return ":attr:";
+ case QtXmlToSphinxLink::Module:
+ return ":mod:";
+ case QtXmlToSphinxLink::Reference:
+ return ":ref:";
+ case QtXmlToSphinxLink::External:
+ break;
+ case QtXmlToSphinxLink::FunctionMask:
+ break;
+ }
+ return "";
+}
+
+TextStream &operator<<(TextStream &str, const QtXmlToSphinxLink &linkContext)
+{
+ // Temporarily turn off bold/italic since links do not work within
+ if (linkContext.flags & QtXmlToSphinxLink::InsideBold)
+ str << "**";
+ else if (linkContext.flags & QtXmlToSphinxLink::InsideItalic)
+ str << '*';
+ str << ' ' << linkKeyWord(linkContext.type) << '`';
+ const bool isExternal = linkContext.type == QtXmlToSphinxLink::External;
+ if (!linkContext.linkText.isEmpty()) {
+ writeEscapedRstText(str, linkContext.linkText);
+ if (isExternal && !linkContext.linkText.endsWith(u' '))
+ str << ' ';
+ str << '<';
+ }
+ // Convert page titles to RST labels
+ str << (linkContext.type == QtXmlToSphinxLink::Reference
+ ? toRstLabel(linkContext.linkRef) : linkContext.linkRef);
+ if (!linkContext.linkText.isEmpty())
+ str << '>';
+ str << '`';
+ if (isExternal)
+ str << '_';
+ str << ' ';
+ if (linkContext.flags & QtXmlToSphinxLink::InsideBold)
+ str << "**";
+ else if (linkContext.flags & QtXmlToSphinxLink::InsideItalic)
+ str << '*';
+ return str;
+}
+
+enum class WebXmlTag {
+ Unknown,
+ heading, brief, para, italic, bold, see_also, snippet, dots, codeline,
+ table, header, row, item, argument, teletype, link, inlineimage, image,
+ list, term, raw, underline, superscript, code, badcode, legalese,
+ rst, section, quotefile,
+ // ignored tags
+ generatedlist, tableofcontents, quotefromfile, skipto, target, page, group,
+ // useless tags
+ description, definition, printuntil, relation,
+ // Doxygen tags
+ title, ref, computeroutput, detaileddescription, name, listitem,
+ parametername, parameteritem, ulink, itemizedlist, parameternamelist,
+ parameterlist,
+ // Doxygen ignored tags
+ highlight, linebreak, programlisting, xreftitle, sp, entry, simplesect,
+ verbatim, xrefsect, xrefdescription,
+};
+
+using WebXmlTagHash = QHash<QStringView, WebXmlTag>;
+
+static const WebXmlTagHash &webXmlTagHash()
+{
+ static const WebXmlTagHash result = {
+ {u"heading", WebXmlTag::heading},
+ {u"brief", WebXmlTag::brief},
+ {u"para", WebXmlTag::para},
+ {u"italic", WebXmlTag::italic},
+ {u"bold", WebXmlTag::bold},
+ {u"see-also", WebXmlTag::see_also},
+ {u"snippet", WebXmlTag::snippet},
+ {u"dots", WebXmlTag::dots},
+ {u"codeline", WebXmlTag::codeline},
+ {u"table", WebXmlTag::table},
+ {u"header", WebXmlTag::header},
+ {u"row", WebXmlTag::row},
+ {u"item", WebXmlTag::item},
+ {u"argument", WebXmlTag::argument},
+ {u"teletype", WebXmlTag::teletype},
+ {u"link", WebXmlTag::link},
+ {u"inlineimage", WebXmlTag::inlineimage},
+ {u"image", WebXmlTag::image},
+ {u"list", WebXmlTag::list},
+ {u"term", WebXmlTag::term},
+ {u"raw", WebXmlTag::raw},
+ {u"underline", WebXmlTag::underline},
+ {u"superscript", WebXmlTag::superscript},
+ {u"code", WebXmlTag::code},
+ {u"badcode", WebXmlTag::badcode},
+ {u"legalese", WebXmlTag::legalese},
+ {u"rst", WebXmlTag::rst},
+ {u"section", WebXmlTag::section},
+ {u"quotefile", WebXmlTag::quotefile},
+ {u"generatedlist", WebXmlTag::generatedlist},
+ {u"tableofcontents", WebXmlTag::tableofcontents},
+ {u"quotefromfile", WebXmlTag::quotefromfile},
+ {u"skipto", WebXmlTag::skipto},
+ {u"target", WebXmlTag::target},
+ {u"page", WebXmlTag::page},
+ {u"group", WebXmlTag::group},
+ {u"description", WebXmlTag::description},
+ {u"definition", WebXmlTag::definition},
+ {u"printuntil", WebXmlTag::printuntil},
+ {u"relation", WebXmlTag::relation},
+ {u"title", WebXmlTag::title},
+ {u"ref", WebXmlTag::ref},
+ {u"computeroutput", WebXmlTag::computeroutput},
+ {u"detaileddescription", WebXmlTag::detaileddescription},
+ {u"name", WebXmlTag::name},
+ {u"listitem", WebXmlTag::listitem},
+ {u"parametername", WebXmlTag::parametername},
+ {u"parameteritem", WebXmlTag::parameteritem},
+ {u"ulink", WebXmlTag::ulink},
+ {u"itemizedlist", WebXmlTag::itemizedlist},
+ {u"parameternamelist", WebXmlTag::parameternamelist},
+ {u"parameterlist", WebXmlTag::parameterlist},
+ {u"highlight", WebXmlTag::highlight},
+ {u"linebreak", WebXmlTag::linebreak},
+ {u"programlisting", WebXmlTag::programlisting},
+ {u"xreftitle", WebXmlTag::xreftitle},
+ {u"sp", WebXmlTag::sp},
+ {u"entry", WebXmlTag::entry},
+ {u"simplesect", WebXmlTag::simplesect},
+ {u"verbatim", WebXmlTag::verbatim},
+ {u"xrefsect", WebXmlTag::xrefsect},
+ {u"xrefdescription", WebXmlTag::xrefdescription},
+ };
+ return result;
+}
+
+QtXmlToSphinx::QtXmlToSphinx(const QtXmlToSphinxDocGeneratorInterface *docGenerator,
+ const QtXmlToSphinxParameters &parameters,
+ const QString& doc, const QString& context)
+ : m_output(static_cast<QString *>(nullptr)),
+ m_context(context),
+ m_generator(docGenerator), m_parameters(parameters)
+{
+ m_result = transform(doc);
+}
+
+QtXmlToSphinx::~QtXmlToSphinx() = default;
+
+void QtXmlToSphinx::callHandler(WebXmlTag t, QXmlStreamReader &r)
+{
+ switch (t) {
+ case WebXmlTag::heading:
+ handleHeadingTag(r);
+ break;
+ case WebXmlTag::brief:
+ case WebXmlTag::para:
+ handleParaTag(r);
+ break;
+ case WebXmlTag::italic:
+ handleItalicTag(r);
+ break;
+ case WebXmlTag::bold:
+ handleBoldTag(r);
+ break;
+ case WebXmlTag::see_also:
+ handleSeeAlsoTag(r);
+ break;
+ case WebXmlTag::snippet:
+ handleSnippetTag(r);
+ break;
+ case WebXmlTag::dots:
+ case WebXmlTag::codeline:
+ handleDotsTag(r);
+ break;
+ case WebXmlTag::table:
+ handleTableTag(r);
+ break;
+ case WebXmlTag::header:
+ handleHeaderTag(r);
+ break;
+ case WebXmlTag::row:
+ handleRowTag(r);
+ break;
+ case WebXmlTag::item:
+ handleItemTag(r);
+ break;
+ case WebXmlTag::argument:
+ handleArgumentTag(r);
+ break;
+ case WebXmlTag::teletype:
+ handleArgumentTag(r);
+ break;
+ case WebXmlTag::link:
+ handleLinkTag(r);
+ break;
+ case WebXmlTag::inlineimage:
+ handleInlineImageTag(r);
+ break;
+ case WebXmlTag::image:
+ handleImageTag(r);
+ break;
+ case WebXmlTag::list:
+ handleListTag(r);
+ break;
+ case WebXmlTag::term:
+ handleTermTag(r);
+ break;
+ case WebXmlTag::raw:
+ handleRawTag(r);
+ break;
+ case WebXmlTag::underline:
+ handleItalicTag(r);
+ break;
+ case WebXmlTag::superscript:
+ handleSuperScriptTag(r);
+ break;
+ case WebXmlTag::code:
+ case WebXmlTag::badcode:
+ case WebXmlTag::legalese:
+ handleCodeTag(r);
+ break;
+ case WebXmlTag::rst:
+ handleRstPassTroughTag(r);
+ break;
+ case WebXmlTag::section:
+ handleAnchorTag(r);
+ break;
+ case WebXmlTag::quotefile:
+ handleQuoteFileTag(r);
+ break;
+ case WebXmlTag::generatedlist:
+ case WebXmlTag::tableofcontents:
+ case WebXmlTag::quotefromfile:
+ case WebXmlTag::skipto:
+ handleIgnoredTag(r);
+ break;
+ case WebXmlTag::target:
+ handleTargetTag(r);
+ break;
+ case WebXmlTag::page:
+ case WebXmlTag::group:
+ handlePageTag(r);
+ break;
+ case WebXmlTag::description:
+ case WebXmlTag::definition:
+ case WebXmlTag::printuntil:
+ case WebXmlTag::relation:
+ handleUselessTag(r);
+ break;
+ case WebXmlTag::title:
+ handleHeadingTag(r);
+ break;
+ case WebXmlTag::ref:
+ case WebXmlTag::computeroutput:
+ case WebXmlTag::detaileddescription:
+ case WebXmlTag::name:
+ handleParaTag(r);
+ break;
+ case WebXmlTag::listitem:
+ case WebXmlTag::parametername:
+ case WebXmlTag::parameteritem:
+ handleItemTag(r);
+ break;
+ case WebXmlTag::ulink:
+ handleLinkTag(r);
+ break;
+ case WebXmlTag::itemizedlist:
+ case WebXmlTag::parameternamelist:
+ case WebXmlTag::parameterlist:
+ handleListTag(r);
+ break;
+ case WebXmlTag::highlight:
+ case WebXmlTag::linebreak:
+ case WebXmlTag::programlisting:
+ case WebXmlTag::xreftitle:
+ case WebXmlTag::sp:
+ case WebXmlTag::entry:
+ case WebXmlTag::simplesect:
+ case WebXmlTag::verbatim:
+ case WebXmlTag::xrefsect:
+ case WebXmlTag::xrefdescription:
+ handleIgnoredTag(r);
+ break;
+ case WebXmlTag::Unknown:
+ break;
+ }
+}
+
+void QtXmlToSphinx::formatCurrentTable()
+{
+ Q_ASSERT(!m_tables.isEmpty());
+ auto &table = m_tables.back();
+ if (table.isEmpty())
+ return;
+ table.normalize();
+ m_output << '\n';
+ table.format(m_output);
+}
+
+void QtXmlToSphinx::pushOutputBuffer()
+{
+ m_buffers.append(std::make_shared<QString>());
+ m_output.setString(m_buffers.top().get());
+}
+
+QString QtXmlToSphinx::popOutputBuffer()
+{
+ Q_ASSERT(!m_buffers.isEmpty());
+ QString result(*m_buffers.top());
+ m_buffers.pop();
+ m_output.setString(m_buffers.isEmpty() ? nullptr : m_buffers.top().get());
+ return result;
+}
+
+constexpr auto autoTranslatedPlaceholder = "AUTO_GENERATED\n"_L1;
+constexpr auto autoTranslatedNote =
+R"(.. warning::
+ This section contains snippets that were automatically
+ translated from C++ to Python and may contain errors.
+
+)"_L1;
+
+void QtXmlToSphinx::setAutoTranslatedNote(QString *str) const
+{
+ if (m_containsAutoTranslations)
+ str->replace(autoTranslatedPlaceholder, autoTranslatedNote);
+ else
+ str->remove(autoTranslatedPlaceholder);
+}
+
+QString QtXmlToSphinx::transform(const QString& doc)
+{
+ Q_ASSERT(m_buffers.isEmpty());
+ if (doc.trimmed().isEmpty())
+ return doc;
+
+ pushOutputBuffer();
+
+ QXmlStreamReader reader(doc);
+
+ m_output << autoTranslatedPlaceholder;
+ Indentation indentation(m_output);
+
+ while (!reader.atEnd()) {
+ QXmlStreamReader::TokenType token = reader.readNext();
+ if (reader.hasError()) {
+ QString message;
+ QTextStream(&message) << "XML Error "
+ << reader.errorString() << " at " << reader.lineNumber()
+ << ':' << reader.columnNumber() << '\n' << doc;
+ m_output << message;
+ throw Exception(message);
+ break;
+ }
+
+ if (token == QXmlStreamReader::StartElement) {
+ WebXmlTag tag = webXmlTagHash().value(reader.name(), WebXmlTag::Unknown);
+ if (!m_tagStack.isEmpty() && tag == WebXmlTag::raw)
+ tag = WebXmlTag::Unknown;
+ m_tagStack.push(tag);
+ }
+
+ if (!m_tagStack.isEmpty())
+ callHandler(m_tagStack.top(), reader);
+
+ if (token == QXmlStreamReader::EndElement) {
+ m_tagStack.pop();
+ m_lastTagName = reader.name().toString();
+ }
+ }
+
+ if (!m_inlineImages.isEmpty()) {
+ // Write out inline image definitions stored in handleInlineImageTag().
+ m_output << '\n' << disableIndent;
+ for (const InlineImage &img : std::as_const(m_inlineImages))
+ m_output << ".. |" << img.tag << "| image:: " << img.href << '\n';
+ m_output << '\n' << enableIndent;
+ m_inlineImages.clear();
+ }
+
+ m_output.flush();
+ QString retval = popOutputBuffer();
+ Q_ASSERT(m_buffers.isEmpty());
+ setAutoTranslatedNote(&retval);
+ return retval;
+}
+
+static QString resolveFile(const QStringList &locations, const QString &path)
+{
+ for (QString location : locations) {
+ location.append(u'/');
+ location.append(path);
+ if (QFileInfo::exists(location))
+ return location;
+ }
+ return QString();
+}
+
+enum class SnippetType
+{
+ Other, // .qdoc, .qml,...
+ CppSource, CppHeader // Potentially converted to Python
+};
+
+SnippetType snippetType(const QString &path)
+{
+ if (path.endsWith(u".cpp"))
+ return SnippetType::CppSource;
+ if (path.endsWith(u".h"))
+ return SnippetType::CppHeader;
+ return SnippetType::Other;
+}
+
+// Return the name of a .cpp/.h snippet converted to Python by snippets-translate
+static QString pySnippetName(const QString &path, SnippetType type)
+{
+ switch (type) {
+ case SnippetType::CppSource:
+ return path.left(path.size() - 3) + u"py"_s;
+ break;
+ case SnippetType::CppHeader:
+ return path + u".py"_s;
+ break;
+ default:
+ break;
+ }
+ return {};
+}
+
+QtXmlToSphinx::Snippet QtXmlToSphinx::readSnippetFromLocations(const QString &path,
+ const QString &identifier,
+ const QString &fallbackPath,
+ QString *errorMessage)
+{
+ // For anything else but C++ header/sources (no conversion to Python),
+ // use existing fallback paths first.
+ const auto type = snippetType(path);
+ if (type == SnippetType::Other && !fallbackPath.isEmpty()) {
+ const QString code = readFromLocation(fallbackPath, identifier, errorMessage);
+ return {code, code.isNull() ? Snippet::Error : Snippet::Fallback};
+ }
+
+ // For C++ header/sources, try snippets converted to Python first.
+ QString resolvedPath;
+ const auto &locations = m_parameters.codeSnippetDirs;
+
+ if (type != SnippetType::Other) {
+ if (!fallbackPath.isEmpty() && !m_parameters.codeSnippetRewriteOld.isEmpty()) {
+ // Try looking up Python converted snippets by rewriting snippets paths
+ QString rewrittenPath = pySnippetName(fallbackPath, type);
+ if (!rewrittenPath.isEmpty()) {
+ rewrittenPath.replace(m_parameters.codeSnippetRewriteOld,
+ m_parameters.codeSnippetRewriteNew);
+ const QString code = readFromLocation(rewrittenPath, identifier, errorMessage);
+ m_containsAutoTranslations = true;
+ return {code, code.isNull() ? Snippet::Error : Snippet::Converted};
+ }
+ }
+
+ resolvedPath = resolveFile(locations, pySnippetName(path, type));
+ if (!resolvedPath.isEmpty()) {
+ const QString code = readFromLocation(resolvedPath, identifier, errorMessage);
+ return {code, code.isNull() ? Snippet::Error : Snippet::Converted};
+ }
+ }
+
+ resolvedPath = resolveFile(locations, path);
+ if (!resolvedPath.isEmpty()) {
+ const QString code = readFromLocation(resolvedPath, identifier, errorMessage);
+ return {code, code.isNull() ? Snippet::Error : Snippet::Resolved};
+ }
+
+ if (!fallbackPath.isEmpty()) {
+ *errorMessage = msgFallbackWarning(path, identifier, fallbackPath);
+ const QString code = readFromLocation(fallbackPath, identifier, errorMessage);
+ return {code, code.isNull() ? Snippet::Error : Snippet::Fallback};
+ }
+
+ *errorMessage = msgSnippetsResolveError(path, locations);
+ return {{}, Snippet::Error};
+}
+
+// Helpers for extracting qdoc snippets "#/// [id]"
+static QString fileNameOfDevice(const QIODevice *inputFile)
+{
+ const auto *file = qobject_cast<const QFile *>(inputFile);
+ return file ? QDir::toNativeSeparators(file->fileName()) : u"<stdin>"_s;
+}
+
+static QString msgSnippetNotFound(const QIODevice &inputFile,
+ const QString &identifier)
+{
+ return u"Code snippet file found ("_s + fileNameOfDevice(&inputFile)
+ + u"), but snippet ["_s + identifier + u"] not found."_s;
+}
+
+static QString msgEmptySnippet(const QIODevice &inputFile, int lineNo,
+ const QString &identifier)
+{
+ return u"Empty code snippet ["_s + identifier + u"] at "_s
+ + fileNameOfDevice(&inputFile) + u':' + QString::number(lineNo);
+}
+
+// Pattern to match qdoc snippet IDs with "#/// [id]" comments and helper to find ID
+static const QRegularExpression &snippetIdPattern()
+{
+ static const QRegularExpression result(uR"RX((//|#) *! *\[([^]]+)\])RX"_s);
+ Q_ASSERT(result.isValid());
+ return result;
+}
+
+static bool matchesSnippetId(QRegularExpressionMatchIterator it,
+ const QString &identifier)
+{
+ while (it.hasNext()) {
+ if (it.next().captured(2) == identifier)
+ return true;
+ }
+ return false;
+}
+
+QString QtXmlToSphinx::readSnippet(QIODevice &inputFile, const QString &identifier,
+ QString *errorMessage)
+{
+ const QByteArray identifierBA = identifier.toUtf8();
+ // Lambda that matches the snippet id
+ const auto snippetIdPred = [&identifierBA, &identifier](const QByteArray &lineBA)
+ {
+ const bool isComment = lineBA.contains('/') || lineBA.contains('#');
+ if (!isComment || !lineBA.contains(identifierBA))
+ return false;
+ const QString line = QString::fromUtf8(lineBA);
+ return matchesSnippetId(snippetIdPattern().globalMatch(line), identifier);
+ };
+
+ // Find beginning, skip over
+ int lineNo = 1;
+ for (; !inputFile.atEnd() && !snippetIdPred(inputFile.readLine());
+ ++lineNo) {
+ }
+
+ if (inputFile.atEnd()) {
+ *errorMessage = msgSnippetNotFound(inputFile, identifier);
+ return {};
+ }
+
+ QString code;
+ for (; !inputFile.atEnd(); ++lineNo) {
+ const QString line = QString::fromUtf8(inputFile.readLine());
+ auto it = snippetIdPattern().globalMatch(line);
+ if (it.hasNext()) { // Skip snippet id lines
+ if (matchesSnippetId(it, identifier))
+ break;
+ } else {
+ code += line;
+ }
+ }
+
+ if (code.isEmpty())
+ *errorMessage = msgEmptySnippet(inputFile, lineNo, identifier);
+
+ return code;
+}
+
+QString QtXmlToSphinx::readFromLocation(const QString &location, const QString &identifier,
+ QString *errorMessage)
+{
+ QFile inputFile;
+ inputFile.setFileName(location);
+ if (!inputFile.open(QIODevice::ReadOnly)) {
+ QTextStream(errorMessage) << "Could not read code snippet file: "
+ << QDir::toNativeSeparators(inputFile.fileName())
+ << ": " << inputFile.errorString();
+ return {}; // null
+ }
+
+ QString code = u""_s; // non-null
+ if (identifier.isEmpty()) {
+ while (!inputFile.atEnd())
+ code += QString::fromUtf8(inputFile.readLine());
+ return CodeSnipHelpers::fixSpaces(code);
+ }
+
+ code = readSnippet(inputFile, identifier, errorMessage);
+ return code.isEmpty() ? QString{} : CodeSnipHelpers::fixSpaces(code); // maintain isNull()
+}
+
+void QtXmlToSphinx::handleHeadingTag(QXmlStreamReader& reader)
+{
+ static int headingSize = 0;
+ static char type;
+ static char types[] = { '-', '^' };
+ QXmlStreamReader::TokenType token = reader.tokenType();
+ if (token == QXmlStreamReader::StartElement) {
+ uint typeIdx = reader.attributes().value(u"level"_s).toUInt();
+ if (typeIdx >= sizeof(types))
+ type = types[sizeof(types)-1];
+ else
+ type = types[typeIdx];
+ } else if (token == QXmlStreamReader::EndElement) {
+ m_output << disableIndent << Pad(type, headingSize) << "\n\n"
+ << enableIndent;
+ } else if (token == QXmlStreamReader::Characters) {
+ m_output << "\n\n" << disableIndent;
+ headingSize = writeEscapedRstText(m_output, reader.text().trimmed());
+ m_output << '\n' << enableIndent;
+ }
+}
+
+void QtXmlToSphinx::handleParaTag(QXmlStreamReader& reader)
+{
+ switch (reader.tokenType()) {
+ case QXmlStreamReader::StartElement:
+ handleParaTagStart();
+ break;
+ case QXmlStreamReader::EndElement:
+ handleParaTagEnd();
+ break;
+ case QXmlStreamReader::Characters:
+ handleParaTagText(reader);
+ break;
+ default:
+ break;
+ }
+}
+
+void QtXmlToSphinx::handleParaTagStart()
+{
+ pushOutputBuffer();
+}
+
+void QtXmlToSphinx::handleParaTagText(QXmlStreamReader& reader)
+{
+ const auto text = reader.text();
+ const QChar end = m_output.lastChar();
+ if (!text.isEmpty() && m_output.indentation() == 0 && !end.isNull()) {
+ QChar start = text[0];
+ if ((end == u'*' || end == u'`') && start != u' ' && !start.isPunct())
+ m_output << '\\';
+ }
+ m_output << escape(text);
+}
+
+void QtXmlToSphinx::handleParaTagEnd()
+{
+ QString result = popOutputBuffer().simplified();
+ if (result.startsWith(u"**Warning:**"))
+ result.replace(0, 12, ".. warning:: "_L1);
+ else if (result.startsWith(u"**Note:**"))
+ result.replace(0, 9, ".. note:: "_L1);
+ m_output << result << "\n\n";
+}
+
+void QtXmlToSphinx::handleItalicTag(QXmlStreamReader& reader)
+{
+ switch (reader.tokenType()) {
+ case QXmlStreamReader::StartElement:
+ if (m_formattingDepth++ == 0) {
+ m_insideItalic = true;
+ m_output << rstItalic;
+ }
+ break;
+ case QXmlStreamReader::EndElement:
+ if (--m_formattingDepth == 0) {
+ m_insideItalic = false;
+ m_output << rstItalicOff;
+ }
+ break;
+ case QXmlStreamReader::Characters:
+ m_output << escape(reader.text().trimmed());
+ break;
+ default:
+ break;
+ }
+}
+
+void QtXmlToSphinx::handleBoldTag(QXmlStreamReader& reader)
+{
+ switch (reader.tokenType()) {
+ case QXmlStreamReader::StartElement:
+ if (m_formattingDepth++ == 0) {
+ m_insideBold = true;
+ m_output << rstBold;
+ }
+ break;
+ case QXmlStreamReader::EndElement:
+ if (--m_formattingDepth == 0) {
+ m_insideBold = false;
+ m_output << rstBoldOff;
+ }
+ break;
+ case QXmlStreamReader::Characters:
+ m_output << escape(reader.text().trimmed());
+ break;
+ default:
+ break;
+ }
+}
+
+void QtXmlToSphinx::handleArgumentTag(QXmlStreamReader& reader)
+{
+ switch (reader.tokenType()) {
+ case QXmlStreamReader::StartElement:
+ if (m_formattingDepth++ == 0)
+ m_output << rstCode;
+ break;
+ case QXmlStreamReader::EndElement:
+ if (--m_formattingDepth == 0)
+ m_output << rstCodeOff;
+ break;
+ case QXmlStreamReader::Characters:
+ m_output << reader.text().trimmed();
+ break;
+ default:
+ break;
+ }
+}
+
+constexpr auto functionLinkType = "function"_L1;
+constexpr auto classLinkType = "class"_L1;
+
+static inline QString fixLinkType(QStringView type)
+{
+ // TODO: create a flag PROPERTY-AS-FUNCTION to ask if the properties
+ // are recognized as such or not in the binding
+ if (type == u"property")
+ return functionLinkType;
+ if (type == u"typedef")
+ return classLinkType;
+ return type.toString();
+}
+
+static inline QString linkSourceAttribute(const QString &type)
+{
+ if (type == functionLinkType || type == classLinkType)
+ return u"raw"_s;
+ return type == u"enum" || type == u"page"
+ ? type : u"href"_s;
+}
+
+// "See also" links may appear as nested links:
+// <see-also>QAbstractXmlReceiver<link raw="isValid()" href="qxmlquery.html#isValid" type="function">isValid()</link>
+// which is handled in handleLinkTag
+// or direct text:
+// <see-also>rootIsDecorated()</see-also>
+// which is handled here.
+
+void QtXmlToSphinx::handleSeeAlsoTag(QXmlStreamReader& reader)
+{
+ switch (reader.tokenType()) {
+ case QXmlStreamReader::StartElement:
+ m_output << ".. seealso:: ";
+ break;
+ case QXmlStreamReader::Characters: {
+ // Direct embedded link: <see-also>rootIsDecorated()</see-also>
+ const auto textR = reader.text().trimmed();
+ if (!textR.isEmpty()) {
+ const QString text = textR.toString();
+ if (m_seeAlsoContext.isNull()) {
+ const QString type = text.endsWith(u"()")
+ ? functionLinkType : classLinkType;
+ m_seeAlsoContext.reset(handleLinkStart(type, text));
+ }
+ handleLinkText(m_seeAlsoContext.data(), text);
+ }
+ }
+ break;
+ case QXmlStreamReader::EndElement:
+ if (!m_seeAlsoContext.isNull()) { // direct, no nested </link> seen
+ handleLinkEnd(m_seeAlsoContext.data());
+ m_seeAlsoContext.reset();
+ }
+ m_output << "\n\n";
+ break;
+ default:
+ break;
+ }
+}
+
+constexpr auto fallbackPathAttribute = "path"_L1;
+
+template <class Indent> // const char*/class Indentor
+void formatSnippet(TextStream &str, Indent indent, const QString &snippet)
+{
+ const auto lines = QStringView{snippet}.split(u'\n');
+ for (const auto &line : lines) {
+ if (!line.trimmed().isEmpty())
+ str << indent << line;
+ str << '\n';
+ }
+}
+
+static QString msgSnippetComparison(const QString &location, const QString &identifier,
+ const QString &pythonCode, const QString &fallbackCode)
+{
+ StringStream str;
+ str.setTabWidth(2);
+ str << "Python snippet " << location;
+ if (!identifier.isEmpty())
+ str << " [" << identifier << ']';
+ str << ":\n" << indent << pythonCode << ensureEndl << outdent
+ << "Corresponding fallback snippet:\n"
+ << indent << fallbackCode << ensureEndl << outdent << "-- end --\n";
+ return str;
+}
+
+void QtXmlToSphinx::handleSnippetTag(QXmlStreamReader& reader)
+{
+ QXmlStreamReader::TokenType token = reader.tokenType();
+ if (token == QXmlStreamReader::StartElement) {
+ const bool consecutiveSnippet = m_lastTagName == u"snippet"
+ || m_lastTagName == u"dots" || m_lastTagName == u"codeline";
+ if (consecutiveSnippet) {
+ m_output.flush();
+ m_output.string()->chop(1); // Strip newline from previous snippet
+ }
+ QString location = reader.attributes().value(u"location"_s).toString();
+ QString identifier = reader.attributes().value(u"identifier"_s).toString();
+ QString fallbackPath;
+ if (reader.attributes().hasAttribute(fallbackPathAttribute))
+ fallbackPath = reader.attributes().value(fallbackPathAttribute).toString();
+ QString errorMessage;
+
+ const Snippet snippet = readSnippetFromLocations(location, identifier,
+ fallbackPath, &errorMessage);
+ if (!errorMessage.isEmpty())
+ warn(msgTagWarning(reader, m_context, m_lastTagName, errorMessage));
+
+ if (m_parameters.snippetComparison && snippet.result == Snippet::Converted
+ && !fallbackPath.isEmpty()) {
+ const QString fallbackCode = readFromLocation(fallbackPath, identifier, &errorMessage);
+ debug(msgSnippetComparison(location, identifier, snippet.code, fallbackCode));
+ }
+
+ if (!consecutiveSnippet)
+ m_output << "::\n\n";
+
+ Indentation indentation(m_output);
+ if (snippet.result == Snippet::Error)
+ m_output << "<Code snippet \"" << location << ':' << identifier << "\" not found>\n";
+ else
+ m_output << snippet.code << ensureEndl;
+ m_output << '\n';
+ }
+}
+
+void QtXmlToSphinx::handleDotsTag(QXmlStreamReader& reader)
+{
+ QXmlStreamReader::TokenType token = reader.tokenType();
+ if (token == QXmlStreamReader::StartElement) {
+ const bool consecutiveSnippet = m_lastTagName == u"snippet"
+ || m_lastTagName == u"dots" || m_lastTagName == u"codeline";
+ if (consecutiveSnippet) {
+ m_output.flush();
+ m_output.string()->chop(2);
+ } else {
+ m_output << "::\n\n";
+ }
+ pushOutputBuffer();
+ int indent = reader.attributes().value(u"indent"_s).toInt()
+ + m_output.indentation() * m_output.tabWidth();
+ for (int i = 0; i < indent; ++i)
+ m_output << ' ';
+ } else if (token == QXmlStreamReader::Characters) {
+ m_output << reader.text().toString().trimmed();
+ } else if (token == QXmlStreamReader::EndElement) {
+ m_output << disableIndent << popOutputBuffer() << "\n\n\n" << enableIndent;
+ }
+}
+
+void QtXmlToSphinx::handleTableTag(QXmlStreamReader& reader)
+{
+ QXmlStreamReader::TokenType token = reader.tokenType();
+ if (token == QXmlStreamReader::StartElement) {
+ if (parentTag() == WebXmlTag::para)
+ handleParaTagEnd(); // End <para> to prevent the table from being rst-escaped
+ m_tables.push({});
+ } else if (token == QXmlStreamReader::EndElement) {
+ // write the table on m_output
+ formatCurrentTable();
+ m_tables.pop();
+ if (parentTag() == WebXmlTag::para)
+ handleParaTagStart();
+ }
+}
+
+void QtXmlToSphinx::handleTermTag(QXmlStreamReader& reader)
+{
+ QXmlStreamReader::TokenType token = reader.tokenType();
+ if (token == QXmlStreamReader::StartElement) {
+ pushOutputBuffer();
+ } else if (token == QXmlStreamReader::Characters) {
+ m_output << reader.text().toString().replace(u"::"_s, u"."_s);
+ } else if (token == QXmlStreamReader::EndElement) {
+ TableCell cell;
+ cell.data = popOutputBuffer().trimmed();
+ m_tables.back().appendRow(TableRow(1, cell));
+ }
+}
+
+
+void QtXmlToSphinx::handleItemTag(QXmlStreamReader& reader)
+{
+ QXmlStreamReader::TokenType token = reader.tokenType();
+ if (token == QXmlStreamReader::StartElement) {
+ auto &table = m_tables.back();
+ if (table.isEmpty())
+ table.appendRow({});
+ TableRow& row = table.last();
+ TableCell cell;
+ cell.colSpan = reader.attributes().value(u"colspan"_s).toShort();
+ cell.rowSpan = reader.attributes().value(u"rowspan"_s).toShort();
+ row << cell;
+ pushOutputBuffer();
+ } else if (token == QXmlStreamReader::EndElement) {
+ QString data = trimLeadingNewlines(trimRight(popOutputBuffer()));
+ auto &table = m_tables.back();
+ if (!table.isEmpty()) {
+ TableRow& row = table.last();
+ if (!row.isEmpty())
+ row.last().data = data;
+ }
+ }
+}
+
+void QtXmlToSphinx::handleHeaderTag(QXmlStreamReader &reader)
+{
+ // <header> in WebXML is either a table header or a description of a
+ // C++ header with "name"/"href" attributes.
+ if (reader.tokenType() == QXmlStreamReader::StartElement
+ && !reader.attributes().hasAttribute(u"name"_s)) {
+ auto &table = m_tables.back();
+ table.setHeaderEnabled(true);
+ table.appendRow({});
+ }
+}
+
+void QtXmlToSphinx::handleRowTag(QXmlStreamReader& reader)
+{
+ if (reader.tokenType() == QXmlStreamReader::StartElement)
+ m_tables.back().appendRow({});
+}
+
+enum ListType { BulletList, OrderedList, EnumeratedList };
+
+static inline ListType webXmlListType(QStringView t)
+{
+ if (t == u"enum")
+ return EnumeratedList;
+ if (t == u"ordered")
+ return OrderedList;
+ return BulletList;
+}
+
+void QtXmlToSphinx::handleListTag(QXmlStreamReader& reader)
+{
+ static ListType listType = BulletList;
+ QXmlStreamReader::TokenType token = reader.tokenType();
+ if (token == QXmlStreamReader::StartElement) {
+ m_tables.push({});
+ auto &table = m_tables.back();
+ listType = webXmlListType(reader.attributes().value(u"type"_s));
+ if (listType == EnumeratedList) {
+ table.appendRow(TableRow{TableCell(u"Constant"_s),
+ TableCell(u"Description"_s)});
+ table.setHeaderEnabled(true);
+ }
+ m_output.indent();
+ } else if (token == QXmlStreamReader::EndElement) {
+ m_output.outdent();
+ const auto &table = m_tables.back();
+ if (!table.isEmpty()) {
+ switch (listType) {
+ case BulletList:
+ case OrderedList: {
+ m_output << '\n';
+ const char *separator = listType == BulletList ? "* " : "#. ";
+ const char *indentLine = listType == BulletList ? " " : " ";
+ for (const TableCell &cell : table.constFirst()) {
+ const auto itemLines = QStringView{cell.data}.split(u'\n');
+ m_output << separator << itemLines.constFirst() << '\n';
+ for (qsizetype i = 1, max = itemLines.size(); i < max; ++i)
+ m_output << indentLine << itemLines[i] << '\n';
+ }
+ m_output << '\n';
+ }
+ break;
+ case EnumeratedList:
+ formatCurrentTable();
+ break;
+ }
+ }
+ m_tables.pop();
+ }
+}
+
+void QtXmlToSphinx::handleLinkTag(QXmlStreamReader& reader)
+{
+ switch (reader.tokenType()) {
+ case QXmlStreamReader::StartElement: {
+ // <link> embedded in <see-also> means the characters of <see-also> are no link.
+ m_seeAlsoContext.reset();
+ const QString type = fixLinkType(reader.attributes().value(u"type"_s));
+ const QString ref = reader.attributes().value(linkSourceAttribute(type)).toString();
+ m_linkContext.reset(handleLinkStart(type, ref));
+ }
+ break;
+ case QXmlStreamReader::Characters:
+ Q_ASSERT(!m_linkContext.isNull());
+ handleLinkText(m_linkContext.data(), reader.text().toString());
+ break;
+ case QXmlStreamReader::EndElement:
+ Q_ASSERT(!m_linkContext.isNull());
+ handleLinkEnd(m_linkContext.data());
+ m_linkContext.reset();
+ break;
+ default:
+ break;
+ }
+}
+
+QtXmlToSphinxLink *QtXmlToSphinx::handleLinkStart(const QString &type, QString ref) const
+{
+ ref.replace(u"::"_s, u"."_s);
+ ref.remove(u"()"_s);
+ auto *result = new QtXmlToSphinxLink(ref);
+
+ if (m_insideBold)
+ result->flags |= QtXmlToSphinxLink::InsideBold;
+ else if (m_insideItalic)
+ result->flags |= QtXmlToSphinxLink::InsideItalic;
+
+ if (type == u"external" || isHttpLink(ref)) {
+ result->type = QtXmlToSphinxLink::External;
+ } else if (type == functionLinkType && !m_context.isEmpty()) {
+ result->type = QtXmlToSphinxLink::Method;
+ const auto rawlinklist = QStringView{result->linkRef}.split(u'.');
+ if (rawlinklist.size() == 1 || rawlinklist.constFirst() == m_context) {
+ const auto lastRawLink = rawlinklist.constLast().toString();
+ QString context = m_generator->resolveContextForMethod(m_context, lastRawLink);
+ if (!result->linkRef.startsWith(context))
+ result->linkRef.prepend(context + u'.');
+ } else {
+ result->linkRef = m_generator->expandFunction(result->linkRef);
+ }
+ } else if (type == functionLinkType && m_context.isEmpty()) {
+ result->type = QtXmlToSphinxLink::Function;
+ } else if (type == classLinkType) {
+ result->type = QtXmlToSphinxLink::Class;
+ result->linkRef = m_generator->expandClass(m_context, result->linkRef);
+ } else if (type == u"enum") {
+ result->type = QtXmlToSphinxLink::Attribute;
+ } else if (type == u"page") {
+ // Module, external web page or reference
+ if (result->linkRef == m_parameters.moduleName)
+ result->type = QtXmlToSphinxLink::Module;
+ else
+ result->type = QtXmlToSphinxLink::Reference;
+ } else {
+ result->type = QtXmlToSphinxLink::Reference;
+ }
+ return result;
+}
+
+// <link raw="Model/View Classes" href="model-view-programming.html#model-view-classes"
+// type="page" page="Model/View Programming">Model/View Classes</link>
+// <link type="page" page="https://doc.qt.io/qt-5/class.html">QML types</link>
+// <link raw="Qt Quick" href="qtquick-index.html" type="page" page="Qt Quick">Qt Quick</link>
+// <link raw="QObject" href="qobject.html" type="class">QObject</link>
+// <link raw="Qt::Window" href="qt.html#WindowType-enum" type="enum" enum="Qt::WindowType">Qt::Window</link>
+// <link raw="QNetworkSession::reject()" href="qnetworksession.html#reject" type="function">QNetworkSession::reject()</link>
+
+static QString fixLinkText(const QtXmlToSphinxLink *linkContext,
+ QString linktext)
+{
+ if (linkContext->type == QtXmlToSphinxLink::External
+ || linkContext->type == QtXmlToSphinxLink::Reference) {
+ return linktext;
+ }
+ // For the language reference documentation, strip the module name.
+ // Clear the link text if that matches the function/class/enumeration name.
+ const int lastSep = linktext.lastIndexOf(u"::");
+ if (lastSep != -1)
+ linktext.remove(0, lastSep + 2);
+ else
+ QtXmlToSphinx::stripPythonQualifiers(&linktext);
+ if (linkContext->linkRef == linktext)
+ return {};
+ if ((linkContext->type & QtXmlToSphinxLink::FunctionMask) != 0
+ && (linkContext->linkRef + u"()"_s) == linktext) {
+ return {};
+ }
+ return linktext;
+}
+
+void QtXmlToSphinx::handleLinkText(QtXmlToSphinxLink *linkContext, const QString &linktext)
+{
+ linkContext->linkText = fixLinkText(linkContext, linktext);
+}
+
+void QtXmlToSphinx::handleLinkEnd(QtXmlToSphinxLink *linkContext)
+{
+ m_output << m_generator->resolveLink(*linkContext);
+}
+
+WebXmlTag QtXmlToSphinx::parentTag() const
+{
+ const auto index = m_tagStack.size() - 2;
+ return index >= 0 ? m_tagStack.at(index) : WebXmlTag::Unknown;
+}
+
+// Copy images that are placed in a subdirectory "images" under the webxml files
+// by qdoc to a matching subdirectory under the "rst/PySide6/<module>" directory
+static bool copyImage(const QString &docDataDir, const QString &relativeSourceFile,
+ const QString &outputDir, const QString &relativeTargetFile,
+ const QLoggingCategory &lc, QString *errorMessage)
+{
+ QString targetFileName = outputDir + u'/' + relativeTargetFile;
+ if (QFileInfo::exists(targetFileName))
+ return true;
+
+ QString relativeTargetDir = relativeTargetFile;
+ relativeTargetDir.truncate(qMax(relativeTargetDir.lastIndexOf(u'/'), qsizetype(0)));
+ if (!relativeTargetDir.isEmpty() && !QFileInfo::exists(outputDir + u'/' + relativeTargetDir)) {
+ const QDir outDir(outputDir);
+ if (!outDir.mkpath(relativeTargetDir)) {
+ QTextStream(errorMessage) << "Cannot create " << QDir::toNativeSeparators(relativeTargetDir)
+ << " under " << QDir::toNativeSeparators(outputDir);
+ return false;
+ }
+ }
+
+ QFile source(docDataDir + u'/' + relativeSourceFile);
+ if (!source.copy(targetFileName)) {
+ QTextStream(errorMessage) << "Cannot copy " << QDir::toNativeSeparators(source.fileName())
+ << " to " << QDir::toNativeSeparators(targetFileName) << ": "
+ << source.errorString();
+ return false;
+ }
+
+ qCDebug(lc).noquote().nospace() << __FUNCTION__ << " \"" << relativeSourceFile
+ << "\"->\"" << relativeTargetFile << '"';
+ return true;
+}
+
+bool QtXmlToSphinx::copyImage(const QString &href) const
+{
+ QString errorMessage;
+ const auto imagePaths = m_generator->resolveImage(href, m_context);
+ const bool result = ::copyImage(m_parameters.docDataDir,
+ imagePaths.source,
+ m_parameters.outputDirectory,
+ imagePaths.target,
+ m_generator->loggingCategory(),
+ &errorMessage);
+ if (!result)
+ throw Exception(errorMessage);
+ return result;
+}
+
+void QtXmlToSphinx::handleImageTag(QXmlStreamReader& reader)
+{
+ if (reader.tokenType() != QXmlStreamReader::StartElement)
+ return;
+ const QString href = reader.attributes().value(u"href"_s).toString();
+ if (copyImage(href))
+ m_output << ".. image:: " << href << "\n\n";
+}
+
+void QtXmlToSphinx::handleInlineImageTag(QXmlStreamReader& reader)
+{
+ if (reader.tokenType() != QXmlStreamReader::StartElement)
+ return;
+ const QString href = reader.attributes().value(u"href"_s).toString();
+ if (!copyImage(href))
+ return;
+ // Handle inline images by substitution references. Insert a unique tag
+ // enclosed by '|' and define it further down. Determine tag from the base
+ //file name with number.
+ QString tag = href;
+ auto pos = tag.lastIndexOf(u'/');
+ if (pos != -1)
+ tag.remove(0, pos + 1);
+ pos = tag.indexOf(u'.');
+ if (pos != -1)
+ tag.truncate(pos);
+ tag += QString::number(m_inlineImages.size() + 1);
+ m_inlineImages.append(InlineImage{tag, href});
+ m_output << '|' << tag << '|' << ' ';
+}
+
+void QtXmlToSphinx::handleRawTag(QXmlStreamReader& reader)
+{
+ QXmlStreamReader::TokenType token = reader.tokenType();
+ if (token == QXmlStreamReader::StartElement) {
+ QString format = reader.attributes().value(u"format"_s).toString();
+ m_output << ".. raw:: " << format.toLower() << "\n\n";
+ } else if (token == QXmlStreamReader::Characters) {
+ Indentation indent(m_output);
+ m_output << reader.text();
+ } else if (token == QXmlStreamReader::EndElement) {
+ m_output << "\n\n";
+ }
+}
+
+void QtXmlToSphinx::handleCodeTag(QXmlStreamReader& reader)
+{
+ QXmlStreamReader::TokenType token = reader.tokenType();
+ if (token == QXmlStreamReader::StartElement) {
+ m_output << "::\n\n" << indent;
+ } else if (token == QXmlStreamReader::Characters) {
+ Indentation indent(m_output);
+ m_output << reader.text();
+ } else if (token == QXmlStreamReader::EndElement) {
+ m_output << outdent << "\n\n";
+ }
+}
+
+void QtXmlToSphinx::handleUnknownTag(QXmlStreamReader& reader)
+{
+ QXmlStreamReader::TokenType token = reader.tokenType();
+ if (token == QXmlStreamReader::StartElement) {
+ qCDebug(m_generator->loggingCategory()).noquote().nospace()
+ << "Unknown QtDoc tag: \"" << reader.name().toString() << "\".";
+ }
+}
+
+void QtXmlToSphinx::handleSuperScriptTag(QXmlStreamReader& reader)
+{
+ QXmlStreamReader::TokenType token = reader.tokenType();
+ if (token == QXmlStreamReader::StartElement) {
+ m_output << " :sup:`";
+ pushOutputBuffer();
+ } else if (token == QXmlStreamReader::Characters) {
+ m_output << reader.text().toString();
+ } else if (token == QXmlStreamReader::EndElement) {
+ m_output << popOutputBuffer();
+ m_output << '`';
+ }
+}
+
+void QtXmlToSphinx::handlePageTag(QXmlStreamReader &reader)
+{
+ if (reader.tokenType() != QXmlStreamReader::StartElement)
+ return;
+
+ m_output << disableIndent;
+
+ const auto title = reader.attributes().value("title");
+ if (!title.isEmpty())
+ m_output << rstLabel(title.toString());
+
+ const auto fullTitle = reader.attributes().value("fulltitle");
+ const int size = fullTitle.isEmpty()
+ ? writeEscapedRstText(m_output, title)
+ : writeEscapedRstText(m_output, fullTitle);
+
+ m_output << '\n' << Pad('*', size) << "\n\n"
+ << enableIndent;
+}
+
+void QtXmlToSphinx::handleTargetTag(QXmlStreamReader &reader)
+{
+ if (reader.tokenType() != QXmlStreamReader::StartElement)
+ return;
+ const auto name = reader.attributes().value("name");
+ if (!name.isEmpty())
+ m_output << rstLabel(name.toString());
+}
+
+void QtXmlToSphinx::handleIgnoredTag(QXmlStreamReader&)
+{
+}
+
+void QtXmlToSphinx::handleUselessTag(QXmlStreamReader&)
+{
+ // Tag "description" just marks the init of "Detailed description" title.
+ // Tag "definition" just marks enums. We have a different way to process them.
+}
+
+void QtXmlToSphinx::handleAnchorTag(QXmlStreamReader& reader)
+{
+ QXmlStreamReader::TokenType token = reader.tokenType();
+ if (token == QXmlStreamReader::StartElement) {
+ QString anchor;
+ if (reader.attributes().hasAttribute(u"id"_s))
+ anchor = reader.attributes().value(u"id"_s).toString();
+ else if (reader.attributes().hasAttribute(u"name"_s))
+ anchor = reader.attributes().value(u"name"_s).toString();
+ if (!anchor.isEmpty() && m_opened_anchor != anchor) {
+ m_opened_anchor = anchor;
+ if (!m_context.isEmpty())
+ anchor.prepend(m_context + u'_');
+ m_output << rstLabel(anchor);
+ }
+ } else if (token == QXmlStreamReader::EndElement) {
+ m_opened_anchor.clear();
+ }
+}
+
+void QtXmlToSphinx::handleRstPassTroughTag(QXmlStreamReader& reader)
+{
+ if (reader.tokenType() == QXmlStreamReader::Characters)
+ m_output << reader.text();
+}
+
+void QtXmlToSphinx::handleQuoteFileTag(QXmlStreamReader& reader)
+{
+ QXmlStreamReader::TokenType token = reader.tokenType();
+ if (token == QXmlStreamReader::Characters) {
+ QString location = reader.text().toString();
+ location.prepend(m_parameters.libSourceDir + u'/');
+ QString errorMessage;
+ QString code = readFromLocation(location, QString(), &errorMessage);
+ if (!errorMessage.isEmpty())
+ warn(msgTagWarning(reader, m_context, m_lastTagName, errorMessage));
+ m_output << "::\n\n";
+ Indentation indentation(m_output);
+ if (code.isEmpty())
+ m_output << "<Code snippet \"" << location << "\" not found>\n";
+ else
+ m_output << code << ensureEndl;
+ m_output << '\n';
+ }
+}
+
+bool QtXmlToSphinx::Table::hasEmptyLeadingRow() const
+{
+ return !m_rows.isEmpty() && m_rows.constFirst().isEmpty();
+}
+
+bool QtXmlToSphinx::Table::hasEmptyTrailingRow() const
+{
+ return !m_rows.isEmpty() && m_rows.constLast().isEmpty();
+}
+
+void QtXmlToSphinx::Table::normalize()
+{
+ if (m_normalized)
+ return;
+
+ // Empty leading/trailing rows have been observed with nested tables
+ if (hasEmptyLeadingRow() || hasEmptyLeadingRow()) {
+ qWarning() << "QtXmlToSphinx: Table with leading/trailing empty columns found: " << *this;
+ while (hasEmptyTrailingRow())
+ m_rows.pop_back();
+ while (hasEmptyLeadingRow())
+ m_rows.pop_front();
+ }
+
+ if (isEmpty())
+ return;
+
+ //QDoc3 generates tables with wrong number of columns. We have to
+ //check and if necessary, merge the last columns.
+ qsizetype maxCols = -1;
+ for (const auto &row : std::as_const(m_rows)) {
+ if (row.size() > maxCols)
+ maxCols = row.size();
+ }
+ if (maxCols <= 0)
+ return;
+ // add col spans
+ for (qsizetype row = 0; row < m_rows.size(); ++row) {
+ for (qsizetype col = 0; col < m_rows.at(row).size(); ++col) {
+ QtXmlToSphinx::TableCell& cell = m_rows[row][col];
+ bool mergeCols = (col >= maxCols);
+ if (cell.colSpan > 0) {
+ QtXmlToSphinx::TableCell newCell;
+ newCell.colSpan = -1;
+ for (int i = 0, max = cell.colSpan-1; i < max; ++i)
+ m_rows[row].insert(col + 1, newCell);
+ cell.colSpan = 0;
+ col++;
+ } else if (mergeCols) {
+ m_rows[row][maxCols - 1].data += u' ' + cell.data;
+ }
+ }
+ }
+
+ // row spans
+ const qsizetype numCols = m_rows.constFirst().size();
+ for (qsizetype col = 0; col < numCols; ++col) {
+ for (qsizetype row = 0; row < m_rows.size(); ++row) {
+ if (col < m_rows[row].size()) {
+ QtXmlToSphinx::TableCell& cell = m_rows[row][col];
+ if (cell.rowSpan > 0) {
+ QtXmlToSphinx::TableCell newCell;
+ newCell.rowSpan = -1;
+ qsizetype targetRow = row + 1;
+ const qsizetype targetEndRow =
+ std::min(targetRow + cell.rowSpan - 1, m_rows.size());
+ cell.rowSpan = 0;
+ for ( ; targetRow < targetEndRow; ++targetRow)
+ m_rows[targetRow].insert(col, newCell);
+ row++;
+ }
+ }
+ }
+ }
+ m_normalized = true;
+}
+
+void QtXmlToSphinx::Table::format(TextStream& s) const
+{
+ if (isEmpty())
+ return;
+
+ Q_ASSERT(isNormalized());
+
+ // calc width and height of each column and row
+ const qsizetype headerColumnCount = m_rows.constFirst().size();
+ QList<qsizetype> colWidths(headerColumnCount, 0);
+ QList<qsizetype> rowHeights(m_rows.size(), 0);
+ for (qsizetype i = 0, maxI = m_rows.size(); i < maxI; ++i) {
+ const QtXmlToSphinx::TableRow& row = m_rows.at(i);
+ for (qsizetype j = 0, maxJ = std::min(row.size(), colWidths.size()); j < maxJ; ++j) {
+ // cache this would be a good idea
+ const auto rowLines = QStringView{row[j].data}.split(u'\n');
+ for (const auto &str : rowLines)
+ colWidths[j] = std::max(colWidths[j], str.size());
+ rowHeights[i] = std::max(rowHeights[i], rowLines.size());
+ }
+ }
+
+ if (!*std::max_element(colWidths.begin(), colWidths.end()))
+ return; // empty table (table with empty cells)
+
+ // create a horizontal line to be used later.
+ QString horizontalLine = u"+"_s;
+ for (auto colWidth : colWidths)
+ horizontalLine += QString(colWidth, u'-') + u'+';
+
+ // write table rows
+ for (qsizetype i = 0, maxI = m_rows.size(); i < maxI; ++i) { // for each row
+ const QtXmlToSphinx::TableRow& row = m_rows.at(i);
+
+ // print line
+ s << '+';
+ for (qsizetype col = 0; col < headerColumnCount; ++col) {
+ char c = '-';
+ if (col >= row.size() || row[col].rowSpan == -1)
+ c = ' ';
+ else if (i == 1 && hasHeader())
+ c = '=';
+ s << Pad(c, colWidths.at(col)) << '+';
+ }
+ s << '\n';
+
+
+ // Print the table cells
+ for (qsizetype rowLine = 0; rowLine < rowHeights.at(i); ++rowLine) { // for each line in a row
+ qsizetype j = 0;
+ for (qsizetype maxJ = std::min(row.size(), headerColumnCount); j < maxJ; ++j) { // for each column
+ const QtXmlToSphinx::TableCell& cell = row[j];
+ // FIXME: Cache this!!!
+ const auto rowLines = QStringView{cell.data}.split(u'\n');
+
+ if (!j || !cell.colSpan)
+ s << '|';
+ else
+ s << ' ';
+ const auto width = int(colWidths.at(j));
+ if (rowLine < rowLines.size())
+ s << AlignedField(rowLines.at(rowLine), width);
+ else
+ s << Pad(' ', width);
+ }
+ for ( ; j < headerColumnCount; ++j) // pad
+ s << '|' << Pad(' ', colWidths.at(j));
+ s << "|\n";
+ }
+ }
+ s << horizontalLine << "\n\n";
+}
+
+void QtXmlToSphinx::Table::formatDebug(QDebug &debug) const
+{
+ const auto rowCount = m_rows.size();
+ debug << "Table(" <<rowCount << " rows";
+ if (m_hasHeader)
+ debug << ", [header]";
+ if (m_normalized)
+ debug << ", [normalized]";
+ for (qsizetype r = 0; r < rowCount; ++r) {
+ const auto &row = m_rows.at(r);
+ const auto &colCount = row.size();
+ debug << ", row " << r << " [" << colCount << "]={";
+ for (qsizetype c = 0; c < colCount; ++c) {
+ if (c > 0)
+ debug << ", ";
+ debug << row.at(c);
+ }
+ debug << '}';
+ }
+ debug << ')';
+}
+
+void QtXmlToSphinx::stripPythonQualifiers(QString *s)
+{
+ const int lastSep = s->lastIndexOf(u'.');
+ if (lastSep != -1)
+ s->remove(0, lastSep + 1);
+}
+
+void QtXmlToSphinx::warn(const QString &message) const
+{
+ qCWarning(m_generator->loggingCategory(), "%s", qPrintable(message));
+}
+
+void QtXmlToSphinx::debug(const QString &message) const
+{
+ qCDebug(m_generator->loggingCategory(), "%s", qPrintable(message));
+}
diff --git a/sources/shiboken6/generator/qtdoc/qtxmltosphinx.h b/sources/shiboken6/generator/qtdoc/qtxmltosphinx.h
new file mode 100644
index 000000000..398c5bc97
--- /dev/null
+++ b/sources/shiboken6/generator/qtdoc/qtxmltosphinx.h
@@ -0,0 +1,216 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QTXMLTOSPHINX_H
+#define QTXMLTOSPHINX_H
+
+#include <textstream.h>
+
+#include <QtCore/QList>
+#include <QtCore/QScopedPointer>
+#include <QtCore/QStack>
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+class QDebug;
+class QXmlStreamReader;
+QT_END_NAMESPACE
+
+class QtXmlToSphinxDocGeneratorInterface;
+struct QtXmlToSphinxParameters;
+struct QtXmlToSphinxLink;
+
+enum class WebXmlTag;
+
+class QtXmlToSphinx
+{
+public:
+ Q_DISABLE_COPY_MOVE(QtXmlToSphinx)
+
+ struct InlineImage
+ {
+ QString tag;
+ QString href;
+ };
+
+ struct TableCell
+ {
+ short rowSpan = 0;
+ short colSpan = 0;
+ QString data;
+
+ TableCell(const QString& text = QString()) : data(text) {}
+ TableCell(const char* text) : data(QString::fromLatin1(text)) {}
+ };
+
+ using TableRow = QList<TableCell>;
+
+ class Table
+ {
+ public:
+ Table() = default;
+
+ bool isEmpty() const { return m_rows.isEmpty(); }
+
+ void setHeaderEnabled(bool enable)
+ {
+ m_hasHeader = enable;
+ }
+
+ bool hasHeader() const
+ {
+ return m_hasHeader;
+ }
+
+ void normalize();
+
+ bool isNormalized() const
+ {
+ return m_normalized;
+ }
+
+ void appendRow(const TableRow &row) { m_rows.append(row); }
+
+ const TableRow &constFirst() const { return m_rows.constFirst(); }
+ TableRow &first() { return m_rows.first(); }
+ TableRow &last() { return m_rows.last(); }
+
+ void format(TextStream& s) const;
+ void formatDebug(QDebug &debug) const;
+
+ private:
+ bool hasEmptyLeadingRow() const;
+ bool hasEmptyTrailingRow() const;
+
+ QList<TableRow> m_rows;
+ bool m_hasHeader = false;
+ bool m_normalized = false;
+ };
+
+ explicit QtXmlToSphinx(const QtXmlToSphinxDocGeneratorInterface *docGenerator,
+ const QtXmlToSphinxParameters &parameters,
+ const QString& doc,
+ const QString& context = QString());
+ ~QtXmlToSphinx();
+
+ QString result() const
+ {
+ return m_result;
+ }
+
+ static void stripPythonQualifiers(QString *s);
+
+ // For testing
+ static QString readSnippet(QIODevice &inputFile, const QString &identifier,
+ QString *errorMessage);
+
+private:
+ using StringSharedPtr = std::shared_ptr<QString>;
+
+ QString transform(const QString& doc);
+
+ void handleHeadingTag(QXmlStreamReader& reader);
+ void handleParaTag(QXmlStreamReader& reader);
+ void handleParaTagStart();
+ void handleParaTagText(QXmlStreamReader &reader);
+ void handleParaTagEnd();
+ void handleItalicTag(QXmlStreamReader& reader);
+ void handleBoldTag(QXmlStreamReader& reader);
+ void handleArgumentTag(QXmlStreamReader& reader);
+ void handleSeeAlsoTag(QXmlStreamReader& reader);
+ void handleSnippetTag(QXmlStreamReader& reader);
+ void handleDotsTag(QXmlStreamReader& reader);
+ void handleLinkTag(QXmlStreamReader& reader);
+ void handleImageTag(QXmlStreamReader& reader);
+ void handleInlineImageTag(QXmlStreamReader& reader);
+ void handleListTag(QXmlStreamReader& reader);
+ void handleTermTag(QXmlStreamReader& reader);
+ void handleSuperScriptTag(QXmlStreamReader& reader);
+ void handleQuoteFileTag(QXmlStreamReader& reader);
+
+ // table tagsvoid QtXmlToSphinx::handleValueTag(QXmlStreamReader& reader)
+
+ void handleTableTag(QXmlStreamReader& reader);
+ void handleHeaderTag(QXmlStreamReader& reader);
+ void handleRowTag(QXmlStreamReader& reader);
+ void handleItemTag(QXmlStreamReader& reader);
+ void handleRawTag(QXmlStreamReader& reader);
+ void handleCodeTag(QXmlStreamReader& reader);
+ void handlePageTag(QXmlStreamReader&);
+ void handleTargetTag(QXmlStreamReader&);
+
+ void handleIgnoredTag(QXmlStreamReader& reader);
+ void handleUnknownTag(QXmlStreamReader& reader);
+ void handleUselessTag(QXmlStreamReader& reader);
+ void handleAnchorTag(QXmlStreamReader& reader);
+ void handleRstPassTroughTag(QXmlStreamReader& reader);
+
+ QtXmlToSphinxLink *handleLinkStart(const QString &type, QString ref) const;
+ static void handleLinkText(QtXmlToSphinxLink *linkContext, const QString &linktext) ;
+ void handleLinkEnd(QtXmlToSphinxLink *linkContext);
+ WebXmlTag parentTag() const;
+
+ void warn(const QString &message) const;
+ void debug(const QString &message) const;
+
+ QStack<WebXmlTag> m_tagStack;
+ TextStream m_output;
+ QString m_result;
+
+ QStack<StringSharedPtr> m_buffers; // Maintain address stability since it used in TextStream
+
+ QStack<Table> m_tables; // Stack of tables, used for <table><list> with nested <item>
+ QScopedPointer<QtXmlToSphinxLink> m_linkContext; // for <link>
+ QScopedPointer<QtXmlToSphinxLink> m_seeAlsoContext; // for <see-also>foo()</see-also>
+ QString m_context;
+ const QtXmlToSphinxDocGeneratorInterface *m_generator;
+ const QtXmlToSphinxParameters &m_parameters;
+ int m_formattingDepth = 0;
+ bool m_insideBold = false;
+ bool m_insideItalic = false;
+ QString m_lastTagName;
+ QString m_opened_anchor;
+ QList<InlineImage> m_inlineImages;
+
+ bool m_containsAutoTranslations = false;
+
+ struct Snippet
+ {
+ enum Result {
+ Converted, // C++ converted to Python
+ Resolved, // Otherwise resolved in snippet paths
+ Fallback, // Fallback from XML
+ Error
+ };
+
+ QString code;
+ Result result;
+ };
+
+ void setAutoTranslatedNote(QString *str) const;
+
+ Snippet readSnippetFromLocations(const QString &path,
+ const QString &identifier,
+ const QString &fallbackPath,
+ QString *errorMessage);
+ static QString readFromLocation(const QString &location, const QString &identifier,
+ QString *errorMessage);
+ void pushOutputBuffer();
+ QString popOutputBuffer();
+ void writeTable(Table& table);
+ bool copyImage(const QString &href) const;
+ void callHandler(WebXmlTag t, QXmlStreamReader &);
+ void formatCurrentTable();
+};
+
+inline TextStream& operator<<(TextStream& s, const QtXmlToSphinx& xmlToSphinx)
+{
+ return s << xmlToSphinx.result();
+}
+
+QDebug operator<<(QDebug d, const QtXmlToSphinxLink &l);
+QDebug operator<<(QDebug debug, const QtXmlToSphinx::Table &t);
+QDebug operator<<(QDebug debug, const QtXmlToSphinx::TableCell &c);
+
+#endif // QTXMLTOSPHINX_H
diff --git a/sources/shiboken6/generator/qtdoc/qtxmltosphinxinterface.h b/sources/shiboken6/generator/qtdoc/qtxmltosphinxinterface.h
new file mode 100644
index 000000000..d4a098a12
--- /dev/null
+++ b/sources/shiboken6/generator/qtdoc/qtxmltosphinxinterface.h
@@ -0,0 +1,68 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QTXMLTOSPHINXINTERFACE_H
+#define QTXMLTOSPHINXINTERFACE_H
+
+#include <QtCore/QStringList>
+
+QT_FORWARD_DECLARE_CLASS(QLoggingCategory)
+
+struct QtXmlToSphinxParameters
+{
+ QString moduleName;
+ QString docDataDir;
+ QString outputDirectory;
+ QString libSourceDir;
+ QStringList codeSnippetDirs;
+ QString codeSnippetRewriteOld;
+ QString codeSnippetRewriteNew;
+ bool snippetComparison = false;
+};
+
+struct QtXmlToSphinxLink
+{
+ enum Type
+ {
+ Method = 0x1, Function = 0x2,
+ FunctionMask = Method | Function,
+ Class = 0x4, Attribute = 0x8, Module = 0x10,
+ Reference = 0x20, External= 0x40
+ };
+
+ enum Flags { InsideBold = 0x1, InsideItalic = 0x2 };
+
+ explicit QtXmlToSphinxLink(const QString &ref) : linkRef(ref) {}
+
+ QString linkRef;
+ QString linkText;
+ Type type = Reference;
+ int flags = 0;
+};
+
+class QtXmlToSphinxDocGeneratorInterface
+{
+public:
+ virtual QString expandFunction(const QString &function) const = 0;
+ virtual QString expandClass(const QString &context,
+ const QString &name) const = 0;
+ virtual QString resolveContextForMethod(const QString &context,
+ const QString &methodName) const = 0;
+
+ virtual const QLoggingCategory &loggingCategory() const = 0;
+
+ virtual QtXmlToSphinxLink resolveLink(const QtXmlToSphinxLink &) const = 0;
+
+ // Resolve images paths relative to doc data directory/output directory.
+ struct Image
+ {
+ QString source;
+ QString target;
+ };
+
+ virtual Image resolveImage(const QString &href, const QString &context) const = 0;
+
+ virtual ~QtXmlToSphinxDocGeneratorInterface() = default;
+};
+
+#endif // QTXMLTOSPHINXINTERFACE_H
diff --git a/sources/shiboken6/generator/qtdoc/rstformat.h b/sources/shiboken6/generator/qtdoc/rstformat.h
new file mode 100644
index 000000000..8af7671fb
--- /dev/null
+++ b/sources/shiboken6/generator/qtdoc/rstformat.h
@@ -0,0 +1,99 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef RSTFORMAT_H
+#define RSTFORMAT_H
+
+#include <textstream.h>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QString>
+#include <QtCore/QTextStream>
+#include <QtCore/QVersionNumber>
+
+struct rstVersionAdded
+{
+ explicit rstVersionAdded(const QVersionNumber &v) : m_version(v) {}
+
+ const QVersionNumber m_version;
+};
+
+inline TextStream &operator<<(TextStream &s, const rstVersionAdded &v)
+{
+ s << ".. versionadded:: "<< v.m_version.toString() << "\n\n";
+ return s;
+}
+
+inline QByteArray rstDeprecationNote(const char *what)
+{
+ return QByteArrayLiteral(".. note:: This ")
+ + what + QByteArrayLiteral(" is deprecated.\n\n");
+}
+
+template <class String>
+inline int writeEscapedRstText(TextStream &str, const String &s)
+{
+ int escaped = 0;
+ for (const QChar &c : s) {
+ switch (c.unicode()) {
+ case '*':
+ case '`':
+ case '_':
+ case '\\':
+ str << '\\';
+ ++escaped;
+ break;
+ }
+ str << c;
+ }
+ return s.size() + escaped;
+}
+
+class escape
+{
+public:
+ explicit escape(QStringView s) : m_string(s) {}
+
+ void write(TextStream &str) const { writeEscapedRstText(str, m_string); }
+
+private:
+ const QStringView m_string;
+};
+
+inline TextStream &operator<<(TextStream &str, const escape &e)
+{
+ e.write(str);
+ return str;
+}
+
+// RST anchor string: Anything else but letters, numbers, '_' or '.' replaced by '-'
+inline bool isValidRstLabelChar(QChar c)
+{
+ return c.isLetterOrNumber() || c == u'_' || c == u'.';
+}
+
+inline QString toRstLabel(QString s)
+{
+ for (int i = 0, size = s.size(); i < size; ++i) {
+ if (!isValidRstLabelChar(s.at(i)))
+ s[i] = u'-';
+ }
+ return s;
+}
+
+class rstLabel
+{
+public:
+ explicit rstLabel(const QString &l) : m_label(l) {}
+
+ friend TextStream &operator<<(TextStream &str, const rstLabel &a)
+ {
+ str << ".. _" << toRstLabel(a.m_label) << ":\n\n";
+ return str;
+ }
+
+private:
+ const QString &m_label;
+};
+
+#endif // RSTFORMAT_H
diff --git a/sources/shiboken6/generator/shiboken/configurablescope.h b/sources/shiboken6/generator/shiboken/configurablescope.h
new file mode 100644
index 000000000..9040c7ad9
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/configurablescope.h
@@ -0,0 +1,33 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CONFIGURABLESCOPE_H
+#define CONFIGURABLESCOPE_H
+
+#include <textstream.h>
+#include <configurabletypeentry.h>
+
+/// Enclose a scope within preprocessor conditions for configurable entries
+class ConfigurableScope
+{
+public:
+ explicit ConfigurableScope(TextStream &s, const ConfigurableTypeEntryCPtr &t) :
+ m_stream(s),
+ m_hasConfigCondition(t->hasConfigCondition())
+ {
+ if (m_hasConfigCondition)
+ m_stream << t->configCondition() << '\n';
+ }
+
+ ~ConfigurableScope()
+ {
+ if (m_hasConfigCondition)
+ m_stream << "#endif\n";
+ }
+
+private:
+ TextStream &m_stream;
+ const bool m_hasConfigCondition;
+};
+
+#endif // CONFIGURABLESCOPE_H
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp
new file mode 100644
index 000000000..97a38a08d
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp
@@ -0,0 +1,6819 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "cppgenerator.h"
+#include "configurablescope.h"
+#include "generatorargument.h"
+#include "generatorstrings.h"
+#include "defaultvalue.h"
+#include "generatorcontext.h"
+#include "codesnip.h"
+#include "customconversion.h"
+#include "headergenerator.h"
+#include "apiextractorresult.h"
+#include "ctypenames.h"
+#include <exception.h>
+#include "pytypenames.h"
+#include "fileout.h"
+#include "overloaddata.h"
+#include "pymethoddefentry.h"
+#include <abstractmetaenum.h>
+#include <abstractmetafield.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <abstractmetalang_helpers.h>
+#include <messages.h>
+#include <modifications.h>
+#include <propertyspec.h>
+#include <reporthandler.h>
+#include <sourcelocation.h>
+#include <textstream.h>
+#include <typedatabase.h>
+#include <containertypeentry.h>
+#include <enumtypeentry.h>
+#include <flagstypeentry.h>
+#include <functiontypeentry.h>
+#include <namespacetypeentry.h>
+#include <primitivetypeentry.h>
+#include <smartpointertypeentry.h>
+#include <typesystemtypeentry.h>
+#include <valuetypeentry.h>
+#include <parser/enumvalue.h>
+
+#include "qtcompat.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QMetaObject>
+#include <QtCore/QMetaType>
+#include <QtCore/QRegularExpression>
+#include <QtCore/QTextStream>
+
+#include <algorithm>
+#include <cstring>
+#include <memory>
+#include <set>
+
+using namespace Qt::StringLiterals;
+
+static const char shibokenErrorsOccurred[] = "Shiboken::Errors::occurred() != nullptr";
+
+static constexpr auto virtualMethodStaticReturnVar = "result"_L1;
+static constexpr auto initFuncPrefix = "init_"_L1;
+
+static constexpr auto sbkObjectTypeF = "SbkObject_TypeF()"_L1;
+static const char initInheritanceFunction[] = "initInheritance";
+
+static QString mangleName(QString name)
+{
+ if (name == u"None" || name == u"False" || name == u"True" || name == u"from")
+ name += u'_';
+ return name;
+}
+
+struct sbkUnusedVariableCast
+{
+ explicit sbkUnusedVariableCast(QAnyStringView name) : m_name(name) {}
+
+ const QAnyStringView m_name;
+};
+
+TextStream &operator<<(TextStream &str, const sbkUnusedVariableCast &c)
+{
+ str << "SBK_UNUSED(" << c.m_name << ")\n";
+ return str;
+}
+
+struct pyTypeGetSlot
+{
+ explicit pyTypeGetSlot(QAnyStringView funcType, QAnyStringView typeObject,
+ QAnyStringView aSlot) :
+ m_funcType(funcType), m_typeObject(typeObject), m_slot(aSlot) {}
+
+ const QAnyStringView m_funcType;
+ const QAnyStringView m_typeObject;
+ const QAnyStringView m_slot;
+};
+
+TextStream &operator<<(TextStream &str, const pyTypeGetSlot &p)
+{
+ str << "reinterpret_cast<" << p.m_funcType << ">(PepType_GetSlot("
+ << p.m_typeObject << ", " << p.m_slot << "));\n";
+ return str;
+}
+
+TextStream &operator<<(TextStream &s, CppGenerator::ErrorReturn r)
+{
+ s << "return";
+ switch (r) {
+ case CppGenerator::ErrorReturn::Default:
+ s << " {}";
+ break;
+ case CppGenerator::ErrorReturn::Zero:
+ s << " 0";
+ break;
+ case CppGenerator::ErrorReturn::MinusOne:
+ s << " -1";
+ break;
+ case CppGenerator::ErrorReturn::Void:
+ break;
+ }
+ s << ";\n";
+ return s;
+}
+
+static constexpr auto converterVar = "converter"_L1;
+
+struct registerConverterName
+{
+ explicit registerConverterName(QAnyStringView typeName,
+ QAnyStringView varName = converterVar) :
+ m_typeName(typeName), m_varName(varName) {}
+
+ QAnyStringView m_typeName;
+ QAnyStringView m_varName;
+};
+
+TextStream &operator<<(TextStream &s, const registerConverterName &r)
+{
+ s << "Shiboken::Conversions::registerConverterName(" << r.m_varName
+ << ", \"" << r.m_typeName << "\");\n";
+ return s;
+}
+
+// Protocol function name / function parameters / return type
+struct ProtocolEntry
+{
+ QString name;
+ QString arguments;
+ QString returnType;
+};
+
+using ProtocolEntries = QList<ProtocolEntry>;
+
+static bool contains(const ProtocolEntries &l, const QString &needle)
+{
+ for (const auto &m : l) {
+ if (m.name == needle)
+ return true;
+ }
+ return false;
+}
+
+// Maps special function names to function parameters and return types
+// used by CPython API in the mapping protocol.
+const ProtocolEntries &mappingProtocols()
+{
+ static const ProtocolEntries result = {
+ {u"__mlen__"_s,
+ u"PyObject *self"_s,
+ u"Py_ssize_t"_s},
+ {u"__mgetitem__"_s,
+ u"PyObject *self, PyObject *_key"_s,
+ u"PyObject*"_s},
+ {u"__msetitem__"_s,
+ u"PyObject *self, PyObject *_key, PyObject *_value"_s,
+ intT}};
+ return result;
+}
+
+// Maps special function names to function parameters and return types
+// used by CPython API in the sequence protocol.
+const ProtocolEntries &sequenceProtocols()
+{
+ static const ProtocolEntries result = {
+ {u"__len__"_s,
+ u"PyObject *self"_s,
+ u"Py_ssize_t"_s},
+ {u"__getitem__"_s,
+ u"PyObject *self, Py_ssize_t _i"_s,
+ u"PyObject*"_s},
+ {u"__setitem__"_s,
+ u"PyObject *self, Py_ssize_t _i, PyObject *_value"_s,
+ intT},
+ {u"__getslice__"_s,
+ u"PyObject *self, Py_ssize_t _i1, Py_ssize_t _i2"_s,
+ u"PyObject*"_s},
+ {u"__setslice__"_s,
+ u"PyObject *self, Py_ssize_t _i1, Py_ssize_t _i2, PyObject *_value"_s,
+ intT},
+ {u"__contains__"_s,
+ u"PyObject *self, PyObject *_value"_s,
+ intT},
+ {u"__concat__"_s,
+ u"PyObject *self, PyObject *_other"_s,
+ u"PyObject*"_s}
+ };
+ return result;
+}
+
+// Return name of function to create PyObject wrapping a container
+static QString opaqueContainerCreationFunc(const AbstractMetaType &type)
+{
+ const auto containerTypeEntry =
+ std::static_pointer_cast<const ContainerTypeEntry>(type.typeEntry());
+ const auto instantiationTypeEntry =
+ type.instantiations().constFirst().typeEntry();
+ QString result = u"create"_s;
+ if (type.isConstant())
+ result += u"Const"_s;
+ result += containerTypeEntry->opaqueContainerName(type.instantiationCppSignatures());
+ return result;
+}
+
+// Write declaration of the function to create PyObject wrapping a container
+static void writeOpaqueContainerCreationFuncDecl(TextStream &s, const QString &name,
+ AbstractMetaType type)
+{
+ type.setReferenceType(NoReference);
+ // Maintain const
+ s << "PyObject *" << name << '(' << type.cppSignature() << "*);\n";
+}
+
+CppGenerator::CppGenerator() = default;
+
+QString CppGenerator::fileNameForContext(const GeneratorContext &context) const
+{
+ return fileNameForContextHelper(context, u"_wrapper.cpp"_s);
+}
+
+void CppGenerator::clearTpFuncs()
+{
+ // Functions that should not be registered under a name in PyMethodDef,
+ // but under a special constant under slots.
+ m_tpFuncs = {
+ {u"__str__"_s, {}}, {u"__str__"_s, {}},
+ {REPR_FUNCTION, {}}, {u"__iter__"_s, {}},
+ {u"__next__"_s, {}}
+ };
+ m_nbFuncs = { {u"__abs__"_s, {}}, {u"__pow__"_s, {} }};
+}
+
+// Prevent ELF symbol qt_version_tag from being generated into the source
+static const char includeQDebug[] =
+"#ifndef QT_NO_VERSION_TAGGING\n"
+"# define QT_NO_VERSION_TAGGING\n"
+"#endif\n"
+"#include <QtCore/QDebug>\n";
+
+QString CppGenerator::chopType(QString s)
+{
+ if (s.endsWith(u"_Type"))
+ s.chop(5);
+ else if (s.endsWith(u"_TypeF()"))
+ s.chop(8);
+ return s;
+}
+
+static bool isStdSetterName(QString setterName, QString propertyName)
+{
+ return setterName.size() == propertyName.size() + 3
+ && setterName.startsWith(u"set")
+ && setterName.endsWith(QStringView{propertyName}.right(propertyName.size() - 1))
+ && setterName.at(3) == propertyName.at(0).toUpper();
+}
+
+static QString buildPropertyString(const QPropertySpec &spec)
+{
+ QString text = u'"' + spec.name() + u':';
+
+ if (spec.read() != spec.name())
+ text += spec.read();
+
+ if (!spec.write().isEmpty()) {
+ text += u':';
+ if (!isStdSetterName(spec.write(), spec.name()))
+ text += spec.write();
+ }
+
+ text += u'"';
+ return text;
+}
+
+static QString _plainName(const QString &s)
+{
+ auto cutPos = s.lastIndexOf(u"::"_s);
+ return cutPos < 0 ? s : s.right(s.length() - (cutPos + 2));
+}
+
+/**********************************************************************
+ *
+ * Decision whether to use an IntEnum/IntFlag
+ * ------------------------------------------
+ *
+ * Unfortunately, all attempts to drive this decision automagically
+ * did not work out. We therefore compile a list in with known
+ * IntEnum and IntFlag.
+ */
+
+/*
+ * This function is now unused and replaced by TypeSystem::PythonEnumType
+ */
+#if 0
+static QSet<QString> useIntSet()
+{
+ static const QSet<QString> result{
+ /* IntEnum */ u"PySide6.QtCore.QDataStream.Version"_s,
+ /* IntEnum */ u"PySide6.QtCore.QEvent.Type"_s,
+ /* IntEnum */ u"PySide6.QtCore.QLocale.FloatingPointPrecisionOption"_s,
+ /* IntFlag */ u"PySide6.QtCore.QLocale.LanguageCodeType"_s,
+ /* IntFlag */ u"PySide6.QtCore.QUrl.ComponentFormattingOption"_s,
+ // note: "QUrl::UrlFormattingOption" is set as IntFlag without flags
+ /* IntFlag */ u"PySide6.QtCore.QUrl.UrlFormattingOption"_s,
+ /* IntFlag */ u"PySide6.QtCore.Qt.AlignmentFlag"_s,
+ /* IntFlag */ u"PySide6.QtCore.Qt.FocusPolicy"_s,
+ /* IntEnum */ u"PySide6.QtCore.Qt.GestureType"_s,
+ /* IntEnum */ u"PySide6.QtCore.Qt.ItemDataRole"_s,
+ /* IntEnum */ u"PySide6.QtCore.Qt.Key"_s,
+ /* Flag */ u"PySide6.QtCore.Qt.Modifier"_s,
+ // note: "Qt::TextFlag" is set as IntFlag without flags
+ /* IntFlag */ u"PySide6.QtCore.Qt.TextFlag"_s,
+ /* IntFlag */ u"PySide6.QtCore.Qt.WindowType"_s,
+ // This is found in QtWidgets but should be in QtGui.
+ /* IntEnum */ u"PySide6.QtGui.QFileSystemModel.Roles"_s,
+ /* IntEnum */ u"PySide6.QtGui.QFont.Stretch"_s,
+ /* IntEnum */ u"PySide6.QtGui.QFont.Weight"_s,
+ /* IntEnum */ u"PySide6.QtGui.QTextDocument.ResourceType"_s,
+ /* IntEnum */ u"PySide6.QtGui.QTextFormat.FormatType"_s,
+ /* IntEnum */ u"PySide6.QtGui.QTextFormat.ObjectTypes"_s,
+ /* IntEnum */ u"PySide6.QtGui.QTextFormat.Property"_s,
+ /* IntEnum */ u"PySide6.QtWidgets.QDialog.DialogCode"_s,
+ /* IntEnum */ u"PySide6.QtWidgets.QFrame.Shadow"_s,
+ /* IntEnum */ u"PySide6.QtWidgets.QFrame.Shape"_s,
+ /* IntEnum */ u"PySide6.QtWidgets.QListWidgetItem.ItemType"_s,
+ /* IntFlag */ u"PySide6.QtWidgets.QMessageBox.StandardButton"_s,
+ // note: "QSizePolicy::PolicyFlag" is set as IntFlag without flags
+ /* IntFlag */ u"PySide6.QtWidgets.QSizePolicy.PolicyFlag"_s,
+ /* IntEnum */ u"PySide6.QtWidgets.QStyle.ComplexControl"_s,
+ /* IntEnum */ u"PySide6.QtWidgets.QStyle.ContentsType"_s,
+ /* IntEnum */ u"PySide6.QtWidgets.QStyle.ControlElement"_s,
+ /* IntEnum */ u"PySide6.QtWidgets.QStyle.PixelMetric"_s,
+ /* IntEnum */ u"PySide6.QtWidgets.QStyle.PrimitiveElement"_s,
+ /* IntEnum */ u"PySide6.QtWidgets.QStyle.StandardPixmap"_s,
+ /* IntEnum */ u"PySide6.QtWidgets.QStyle.StyleHint"_s,
+ /* IntEnum */ u"PySide6.QtWidgets.QStyle.SubElement"_s,
+ /* IntEnum */ u"PySide6.QtWidgets.QTableWidgetItem.ItemType"_s,
+ /* IntEnum */ u"PySide6.QtWidgets.QTreeWidgetItem.ItemType"_s,
+ /* IntEnum */ u"PySide6.QtCharts.QBoxSet.ValuePositions"_s,
+ /* IntEnum */ u"PySide6.QtMultimedia.QMediaPlayer.Loops"_s,
+ /* IntEnum */ u"PySide6.QtQuick.QSGGeometry.DrawingMode"_s,
+ /* IntEnum */ u"PySide6.QtWebEngineCore.QWebEngineScript.ScriptWorldId"_s,
+ // Added because it should really be used as number
+ /* IntEnum */ u"PySide6.QtCore.QMetaType.Type"_s,
+ /* IntEnum */ u"PySide6.QtSerialPort.QSerialPort.BaudRate"_s,
+ };
+ return result;
+}
+#endif
+
+static bool _shouldInheritInt(const AbstractMetaEnum &cppEnum)
+{
+ return !cppEnum.fullName().startsWith(u"PySide6."_s);
+}
+
+static QString BuildEnumFlagInfo(const AbstractMetaEnum &cppEnum)
+{
+ auto enumType = cppEnum.typeEntry();
+ QString result = _plainName(enumType->name());
+ auto flags = enumType->flags();
+ auto decision = enumType->pythonEnumType();
+ bool _int = _shouldInheritInt(cppEnum);
+ bool _flag = bool(flags);
+
+ if (decision != TypeSystem::PythonEnumType::Unspecified) {
+ _int = decision == TypeSystem::PythonEnumType::IntEnum ||
+ decision == TypeSystem::PythonEnumType::IntFlag;
+ _flag = decision == TypeSystem::PythonEnumType::Flag ||
+ decision == TypeSystem::PythonEnumType::IntFlag;
+ }
+ result += _flag ? (_int ? u":IntFlag"_s : u":Flag"_s)
+ : (_int ? u":IntEnum"_s : u":Enum"_s);
+ if (flags)
+ result += u':' + _plainName(flags->flagsName());
+ return u'"' + result + u'"';
+}
+
+static void writePyGetSetDefEntry(TextStream &s, const QString &name,
+ const QString &getFunc, const QString &setFunc)
+{
+ s << "{const_cast<char *>(\"" << mangleName(name) << "\"), " << getFunc << ", "
+ << (setFunc.isEmpty() ? NULL_PTR : setFunc) << ", nullptr, nullptr},\n";
+}
+
+static bool generateRichComparison(const GeneratorContext &c)
+{
+ const auto metaClass = c.metaClass();
+ if (c.forSmartPointer()) {
+ auto te = std::static_pointer_cast<const SmartPointerTypeEntry>(metaClass->typeEntry());
+ return te->smartPointerType() == TypeSystem::SmartPointerType::Shared;
+ }
+
+ return !metaClass->isNamespace() && metaClass->hasComparisonOperatorOverload();
+}
+
+void CppGenerator::generateIncludes(TextStream &s, const GeneratorContext &classContext,
+ const IncludeGroupList &includes,
+ const AbstractMetaClassCList &innerClasses) const
+{
+ const auto metaClass = classContext.metaClass();
+
+ // write license comment
+ s << licenseComment() << '\n';
+
+ const bool normalClass = !classContext.forSmartPointer();
+ // Normally only required for classes for which we want to generate protected API,
+ // but it needs to be generated into all files to ensure ODR for Unity builds.
+ if (!avoidProtectedHack())
+ s << HeaderGenerator::protectedHackDefine;
+
+ QByteArrayList cppIncludes{"typeinfo", "iterator", // for containers
+ "cctype", "cstring"};
+ // headers
+ s << "// default includes\n";
+ s << "#include <shiboken.h>\n";
+ if (wrapperDiagnostics()) {
+ s << "#include <helper.h>\n";
+ cppIncludes << "iostream";
+ }
+
+ if (normalClass && usePySideExtensions()) {
+ s << includeQDebug;
+ if (metaClass->hasToStringCapability())
+ s << "#include <QtCore/QBuffer>\n";
+ if (isQObject(metaClass)) {
+ s << "#include <pysideqobject.h>\n"
+ << "#include <pysidesignal.h>\n"
+ << "#include <pysideproperty.h>\n"
+ << "#include <signalmanager.h>\n"
+ << "#include <pysidemetafunction.h>\n";
+ }
+ s << "#include <pysideqenum.h>\n"
+ << "#include <pysideqmetatype.h>\n"
+ << "#include <pysideutils.h>\n"
+ << "#include <feature_select.h>\n"
+ << "QT_WARNING_DISABLE_DEPRECATED\n\n";
+ }
+
+ // The multiple inheritance initialization function
+ // needs the 'set' class from C++ STL.
+ if (normalClass && getMultipleInheritingClass(metaClass) != nullptr)
+ cppIncludes << "algorithm" << "set";
+ if (normalClass && metaClass->generateExceptionHandling())
+ cppIncludes << "exception";
+
+ s << "\n// module include\n" << "#include \"" << getModuleHeaderFileName() << "\"\n";
+ if (hasPrivateClasses())
+ s << "#include \"" << getPrivateModuleHeaderFileName() << "\"\n";
+
+ s << "\n// main header\n" << "#include \""
+ << HeaderGenerator::headerFileNameForContext(classContext) << "\"\n";
+
+ if (!innerClasses.isEmpty()) {
+ s << "\n// inner classes\n";
+ for (const auto &innerClass : innerClasses) {
+ GeneratorContext innerClassContext = contextForClass(innerClass);
+ s << "#include \""
+ << HeaderGenerator::headerFileNameForContext(innerClassContext) << "\"\n";
+ }
+ }
+
+ if (avoidProtectedHack())
+ s << baseWrapperIncludes(classContext);
+
+ for (const auto &g : includes)
+ s << g;
+
+ // C++ includes
+ std::sort(cppIncludes.begin(), cppIncludes.end());
+ s << '\n';
+ for (const auto &i : std::as_const(cppIncludes))
+ s << "#include <" << i << ">\n";
+}
+
+// Write methods definition
+void CppGenerator::writePyMethodDefs(TextStream &s, const QString &className,
+ const QString &methodsDefinitions)
+{
+ s << "static PyMethodDef " << className << "_methods[] = {\n" << indent
+ << methodsDefinitions << METHOD_DEF_SENTINEL << outdent << "};\n\n";
+}
+
+void CppGenerator::writeModuleCodeSnips(TextStream &s, const CodeSnipList &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language) const
+{
+ if (!codeSnips.isEmpty()) {
+ try {
+ writeCodeSnips(s, codeSnips, position, language);
+ } catch (const std::exception &e) {
+ throw Exception(msgSnippetError("module source of "_L1 + moduleName(), e.what()));
+ }
+ }
+}
+
+bool CppGenerator::hasHashFunction(const AbstractMetaClassCPtr &c)
+{
+ return !c->typeEntry()->hashFunction().isEmpty()
+ || c->hasHashFunction();
+}
+
+static bool needsTypeDiscoveryFunction(const AbstractMetaClassCPtr &c)
+{
+ return c->baseClass() != nullptr
+ && (c->isPolymorphic() || !c->typeEntry()->polymorphicIdValue().isEmpty());
+}
+
+static void writeAddedTypeSignatures(TextStream &s, const ComplexTypeEntryCPtr &te)
+{
+ for (const auto &e : te->addedPyMethodDefEntrys()) {
+ if (auto count = e.signatures.size()) {
+ for (qsizetype i = 0; i < count; ++i) {
+ if (count > 1)
+ s << i << ':';
+ s << e.signatures.at(i) << '\n';
+ }
+ }
+ }
+}
+
+/// Function used to write the class generated binding code on the buffer
+/// \param s the output buffer
+/// \param classContext the pointer to metaclass information
+void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classContext)
+{
+ if (classContext.forSmartPointer()) {
+ generateSmartPointerClass(s, classContext);
+ return;
+ }
+
+ s.setLanguage(TextStream::Language::Cpp);
+ AbstractMetaClassCPtr metaClass = classContext.metaClass();
+ const auto typeEntry = metaClass->typeEntry();
+
+ auto innerClasses = metaClass->innerClasses();
+ for (auto it = innerClasses.begin(); it != innerClasses.end(); ) {
+ auto innerTypeEntry = (*it)->typeEntry();
+ if (shouldGenerate(innerTypeEntry) && !innerTypeEntry->isSmartPointer())
+ ++it;
+ else
+ it = innerClasses.erase(it);
+ }
+
+ AbstractMetaEnumList classEnums = metaClass->enums();
+ metaClass->getEnumsFromInvisibleNamespacesToBeGenerated(&classEnums);
+
+ IncludeGroupList includeGroups;
+ if (!classContext.useWrapper() || !avoidProtectedHack())
+ includeGroups.append(classIncludes(metaClass));
+ generateIncludes(s, classContext, includeGroups, innerClasses);
+
+ if (typeEntry->typeFlags().testFlag(ComplexTypeEntry::Deprecated))
+ s << "#Deprecated\n";
+
+ // Use class base namespace
+ {
+ AbstractMetaClassCPtr context = metaClass->enclosingClass();
+ while (context) {
+ if (context->isNamespace() && !context->enclosingClass()
+ && std::static_pointer_cast<const NamespaceTypeEntry>(context->typeEntry())->generateUsing()) {
+ s << "\nusing namespace " << context->qualifiedCppName() << ";\n";
+ break;
+ }
+ context = context->enclosingClass();
+ }
+ }
+
+ s << '\n';
+
+ // class inject-code native/beginning
+ if (!typeEntry->codeSnips().isEmpty()) {
+ writeClassCodeSnips(s, typeEntry->codeSnips(),
+ TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode,
+ classContext);
+ s << '\n';
+ }
+
+ // python conversion rules
+ if (typeEntry->isValue()) {
+ auto vte = std::static_pointer_cast<const ValueTypeEntry>(typeEntry);
+ if (vte->hasTargetConversionRule()) {
+ s << "// Python Conversion\n";
+ s << vte->targetConversionRule() << '\n';
+ }
+ }
+
+ if (classContext.useWrapper()) {
+ s << "// Native ---------------------------------------------------------\n\n";
+
+ if (avoidProtectedHack() && usePySideExtensions()) {
+ s << "void " << classContext.wrapperName() << "::pysideInitQtMetaTypes()\n{\n"
+ << indent;
+ writeInitQtMetaTypeFunctionBody(s, classContext);
+ s << outdent << "}\n\n";
+ }
+
+ int maxOverrides = 0;
+ writeCacheResetNative(s, classContext);
+ for (const auto &func : metaClass->functions()) {
+ const auto generation = functionGeneration(func);
+ if (generation.testFlag(FunctionGenerationFlag::WrapperConstructor))
+ writeConstructorNative(s, classContext, func);
+ else if (generation.testFlag(FunctionGenerationFlag::VirtualMethod))
+ writeVirtualMethodNative(s, func, maxOverrides++);
+ }
+
+ if (shouldGenerateMetaObjectFunctions(metaClass))
+ writeMetaObjectMethod(s, classContext);
+ if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor())
+ writeDestructorNative(s, classContext);
+ }
+
+ for (const auto &f : metaClass->userAddedPythonOverrides())
+ writeUserAddedPythonOverride(s, f);
+
+ StringStream smd(TextStream::Language::Cpp);
+ StringStream md(TextStream::Language::Cpp);
+ StringStream signatureStream(TextStream::Language::Cpp);
+
+ s << openTargetExternC;
+
+ const auto &functionGroups = getFunctionGroups(metaClass);
+ for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) {
+ if (contains(sequenceProtocols(), it.key()) || contains(mappingProtocols(), it.key()))
+ continue;
+ const AbstractMetaFunctionCList &overloads = it.value();
+ if (overloads.isEmpty())
+ continue;
+
+ const auto rfunc = overloads.constFirst();
+ OverloadData overloadData(overloads, api());
+
+ if (rfunc->isConstructor()) {
+ writeConstructorWrapper(s, overloadData, classContext);
+ writeSignatureInfo(signatureStream, overloadData);
+ }
+ // call operators
+ else if (rfunc->name() == u"operator()") {
+ writeMethodWrapper(s, overloadData, classContext);
+ writeSignatureInfo(signatureStream, overloadData);
+ }
+ else if (!rfunc->isOperatorOverload()) {
+ writeMethodWrapper(s, overloadData, classContext);
+ writeSignatureInfo(signatureStream, overloadData);
+ // For a mixture of static and member function overloads,
+ // a separate PyMethodDef entry is written which is referenced
+ // in the PyMethodDef list and later in getattro() for handling
+ // the non-static case.
+ const auto defEntries = methodDefinitionEntries(overloadData);
+ if (OverloadData::hasStaticAndInstanceFunctions(overloads)) {
+ QString methDefName = cpythonMethodDefinitionName(rfunc);
+ smd << "static PyMethodDef " << methDefName << " = " << indent
+ << defEntries.constFirst() << outdent << ";\n\n";
+ }
+ const auto &fname = rfunc->name();
+ if (!m_tpFuncs.contains(fname) && !m_nbFuncs.contains(fname))
+ md << defEntries;
+ }
+ }
+ for (const auto &pyMethodDef : typeEntry->addedPyMethodDefEntrys())
+ md << pyMethodDef << ",\n";
+
+ if (typeEntry->isValue())
+ writeCopyFunction(s, md, signatureStream, classContext);
+
+ const QString methodsDefinitions = md.toString();
+ const QString singleMethodDefinitions = smd.toString();
+
+ const QString className = chopType(cpythonTypeName(metaClass));
+
+ // Write single method definitions
+ s << singleMethodDefinitions;
+
+ if (usePySideExtensions()) {
+ // PYSIDE-1019: Write a compressed list of all properties `name:getter[:setter]`.
+ // Default values are suppressed.
+ QStringList sorter;
+ for (const auto &spec : metaClass->propertySpecs()) {
+ if (!spec.generateGetSetDef())
+ sorter.append(buildPropertyString(spec));
+ }
+ sorter.sort();
+
+ s << '\n';
+ s << "static const char *" << className << "_PropertyStrings[] = {\n" << indent;
+ for (const auto &entry : std::as_const(sorter))
+ s << entry << ",\n";
+ s << NULL_PTR << " // Sentinel\n"
+ << outdent << "};\n\n";
+
+ }
+ // PYSIDE-1735: Write an EnumFlagInfo structure
+ QStringList sorter;
+ for (const auto &entry : std::as_const(classEnums))
+ sorter.append(BuildEnumFlagInfo(entry));
+ sorter.sort();
+ if (!sorter.empty()) {
+ s << "static const char *" << className << "_EnumFlagInfo[] = {\n" << indent;
+ for (const auto &entry : std::as_const(sorter))
+ s << entry << ",\n";
+ s << NULL_PTR << " // Sentinel\n"
+ << outdent << "};\n\n";
+ }
+
+ // Write methods definition
+ writePyMethodDefs(s, className, methodsDefinitions);
+
+ // Write tp_s/getattro function
+ const AttroCheck attroCheck = checkAttroFunctionNeeds(metaClass);
+ if ((attroCheck & AttroCheckFlag::GetattroMask) != 0)
+ writeGetattroFunction(s, attroCheck, classContext);
+ if ((attroCheck & AttroCheckFlag::SetattroMask) != 0)
+ writeSetattroFunction(s, attroCheck, classContext);
+
+ if (const auto f = boolCast(metaClass) ; f.has_value())
+ writeNbBoolFunction(classContext, f.value(), s);
+
+ if (supportsNumberProtocol(metaClass)) {
+ const auto numberProtocolOps = numberProtocolOperators(metaClass);
+ for (const auto &overloads : numberProtocolOps) {
+ OverloadData overloadData(overloads, api());
+ writeMethodWrapper(s, overloadData, classContext);
+ writeSignatureInfo(signatureStream, overloadData);
+ }
+ }
+
+ if (supportsSequenceProtocol(metaClass)) {
+ writeSequenceMethods(s, metaClass, classContext);
+ }
+
+ if (supportsMappingProtocol(metaClass)) {
+ writeMappingMethods(s, metaClass, classContext);
+ }
+
+ if (generateRichComparison(classContext)) {
+ s << "// Rich comparison\n";
+ writeRichCompareFunction(s, classContext);
+ }
+
+ if (shouldGenerateGetSetList(metaClass)) {
+ const AbstractMetaFieldList &fields = metaClass->fields();
+ for (const AbstractMetaField &metaField : fields) {
+ if (metaField.canGenerateGetter())
+ writeGetterFunction(s, metaField, classContext);
+ if (metaField.canGenerateSetter())
+ writeSetterFunction(s, metaField, classContext);
+ s << '\n';
+ }
+
+ for (const QPropertySpec &property : metaClass->propertySpecs()) {
+ if (property.generateGetSetDef() || !usePySideExtensions()) {
+ writeGetterFunction(s, property, classContext);
+ if (property.hasWrite())
+ writeSetterFunction(s, property, classContext);
+ }
+ }
+
+ s << "// Getters and Setters for " << metaClass->name() << '\n';
+ s << "static PyGetSetDef " << cpythonGettersSettersDefinitionName(metaClass)
+ << "[] = {\n" << indent;
+ for (const AbstractMetaField &metaField : fields) {
+ const bool canGenerateGetter = metaField.canGenerateGetter();
+ const bool canGenerateSetter = metaField.canGenerateSetter();
+ if (canGenerateGetter || canGenerateSetter) {
+ const QString getter = canGenerateGetter
+ ? cpythonGetterFunctionName(metaField) : QString();
+ const QString setter = canGenerateSetter
+ ? cpythonSetterFunctionName(metaField) : QString();
+ const auto names = metaField.definitionNames();
+ for (const auto &name : names)
+ writePyGetSetDefEntry(s, name, getter, setter);
+ }
+ }
+
+ for (const QPropertySpec &property : metaClass->propertySpecs()) {
+ if (property.generateGetSetDef() || !usePySideExtensions()) {
+ const QString setter = property.hasWrite()
+ ? cpythonSetterFunctionName(property, metaClass) : QString();
+ writePyGetSetDefEntry(s, property.name(),
+ cpythonGetterFunctionName(property, metaClass), setter);
+ }
+ }
+ s << "{nullptr, nullptr, nullptr, nullptr, nullptr} // Sentinel\n"
+ << outdent << "};\n\n";
+ }
+
+ s << closeExternC;
+
+ if (hasHashFunction(metaClass))
+ writeHashFunction(s, classContext);
+
+ // Write tp_traverse and tp_clear functions.
+ writeTpTraverseFunction(s, metaClass);
+ writeTpClearFunction(s, metaClass);
+
+ writeClassDefinition(s, metaClass, classContext);
+ s << '\n';
+
+ if (needsTypeDiscoveryFunction(metaClass)) {
+ writeTypeDiscoveryFunction(s, metaClass);
+ s << '\n';
+ }
+
+ writeConverterFunctions(s, metaClass, classContext);
+ writeAddedTypeSignatures(signatureStream, typeEntry);
+ writeClassRegister(s, metaClass, classContext, signatureStream);
+
+ if (metaClass->hasStaticFields())
+ writeStaticFieldInitialization(s, metaClass);
+
+ // class inject-code native/end
+ if (!typeEntry->codeSnips().isEmpty()) {
+ writeClassCodeSnips(s, typeEntry->codeSnips(),
+ TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode,
+ classContext);
+ s << '\n';
+ }
+}
+
+void CppGenerator::writeMethodWrapper(TextStream &s, TextStream &definitionStream,
+ TextStream &signatureStream,
+ const AbstractMetaFunctionCList &overloads,
+ const GeneratorContext &classContext) const
+{
+ OverloadData overloadData(overloads, api());
+ writeMethodWrapper(s, overloadData, classContext);
+ writeSignatureInfo(signatureStream, overloadData);
+ definitionStream << methodDefinitionEntries(overloadData);
+}
+
+void CppGenerator::writeCacheResetNative(TextStream &s, const GeneratorContext &classContext)
+{
+ s << "void " << classContext.wrapperName()
+ << "::resetPyMethodCache()\n{\n" << indent
+ << "std::fill_n(m_PyMethodCache, sizeof(m_PyMethodCache) / sizeof(m_PyMethodCache[0]), false);\n"
+ << outdent << "}\n\n";
+}
+
+void CppGenerator::writeConstructorNative(TextStream &s, const GeneratorContext &classContext,
+ const AbstractMetaFunctionCPtr &func) const
+{
+ const QString qualifiedName = classContext.wrapperName() + u"::"_s;
+ s << functionSignature(func, qualifiedName, QString(),
+ OriginalTypeDescription | SkipDefaultValues);
+ s << " : ";
+ writeFunctionCall(s, func);
+ s << "\n{\n" << indent;
+ if (wrapperDiagnostics())
+ s << R"(std::cerr << __FUNCTION__ << ' ' << this << '\n';)" << '\n';
+ const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast();
+ s << "resetPyMethodCache();\n";
+ writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionBeginning,
+ TypeSystem::NativeCode, func, false, lastArg);
+ s << "// ... middle\n";
+ writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionEnd,
+ TypeSystem::NativeCode, func, false, lastArg);
+ s << outdent << "}\n\n";
+}
+
+void CppGenerator::writeDestructorNative(TextStream &s,
+ const GeneratorContext &classContext)
+{
+ s << classContext.wrapperName() << "::~"
+ << classContext.wrapperName() << "()\n{\n" << indent;
+ if (wrapperDiagnostics())
+ s << R"(std::cerr << __FUNCTION__ << ' ' << this << '\n';)" << '\n';
+ // kill pyobject
+ s << R"(SbkObject *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(this);
+Shiboken::Object::destroy(wrapper, this);
+)" << outdent << "}\n";
+}
+
+// Return type for error messages when getting invalid types from virtual
+// methods implemented in Python in C++ wrappers
+QString CppGenerator::getVirtualFunctionReturnTypeName(const AbstractMetaFunctionCPtr &func) const
+{
+ if (func->type().isVoid())
+ return u"\"\""_s;
+
+ if (func->isTypeModified())
+ return u'"' + func->modifiedTypeName() + u'"';
+
+ // SbkType would return null when the type is a container.
+ auto typeEntry = func->type().typeEntry();
+ if (typeEntry->isContainer()) {
+ const auto cte = std::static_pointer_cast<const ContainerTypeEntry>(typeEntry);
+ switch (cte->containerKind()) {
+ case ContainerTypeEntry::ListContainer:
+ case ContainerTypeEntry::SpanContainer:
+ break;
+ case ContainerTypeEntry::SetContainer:
+ return uR"("set")"_s;
+ break;
+ case ContainerTypeEntry::MapContainer:
+ case ContainerTypeEntry::MultiMapContainer:
+ return uR"("dict")"_s;
+ break;
+ case ContainerTypeEntry::PairContainer:
+ return uR"("tuple")"_s;
+ break;
+ }
+ return uR"("list")"_s;
+ }
+ if (typeEntry->isSmartPointer())
+ return u'"' + typeEntry->qualifiedCppName() + u'"';
+
+ if (avoidProtectedHack()) {
+ auto metaEnum = api().findAbstractMetaEnum(func->type().typeEntry());
+ if (metaEnum.has_value() && metaEnum->isProtected())
+ return u'"' + protectedEnumSurrogateName(metaEnum.value()) + u'"';
+ }
+
+ if (func->type().isPrimitive())
+ return u'"' + func->type().name() + u'"';
+
+ return u"Shiboken::SbkType< "_s
+ + typeEntry->qualifiedCppName() + u" >()->tp_name"_s;
+}
+
+// When writing an overridden method of a wrapper class, write the part
+// calling the C++ function in case no overload in Python exists.
+void CppGenerator::writeVirtualMethodCppCall(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ const QString &funcName,
+ const CodeSnipList &snips,
+ const AbstractMetaArgument *lastArg,
+ const TypeEntryCPtr &retType,
+ const QString &returnStatement, bool hasGil) const
+{
+ if (!snips.isEmpty()) {
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning,
+ TypeSystem::ShellCode, func, false, lastArg);
+ }
+
+ if (func->isAbstract()) {
+ if (!hasGil)
+ s << "Shiboken::GilState gil;\n";
+ s << "Shiboken::Errors::setPureVirtualMethodError(\""
+ << func->ownerClass()->name() << '.' << funcName << "\");\n"
+ << returnStatement << '\n';
+ return;
+ }
+
+ if (hasGil)
+ s << "gil.release();\n";
+
+ if (retType)
+ s << "return ";
+ s << "this->::" << func->implementingClass()->qualifiedCppName() << "::";
+ writeFunctionCall(s, func, Generator::VirtualCall);
+ s << ";\n";
+ if (retType)
+ return;
+ if (!snips.isEmpty()) {
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd,
+ TypeSystem::ShellCode, func, false, lastArg);
+ }
+ s << "return;\n";
+}
+
+// Determine the return statement (void or a result value).
+
+CppGenerator::VirtualMethodReturn
+ CppGenerator::virtualMethodReturn(const ApiExtractorResult &api,
+ const AbstractMetaFunctionCPtr &func,
+ const FunctionModificationList &functionModifications)
+{
+ VirtualMethodReturn result;
+ if (func->isVoid()) {
+ result.statement = "return;"_L1;
+ return result;
+ }
+
+ result.statement = "return "_L1;
+ const AbstractMetaType &returnType = func->type();
+ for (const FunctionModification &mod : functionModifications) {
+ for (const ArgumentModification &argMod : mod.argument_mods()) {
+ if (argMod.index() == 0 && !argMod.replacedDefaultExpression().isEmpty()) {
+ static const QRegularExpression regex("%(\\d+)"_L1);
+ Q_ASSERT(regex.isValid());
+ QString expr = argMod.replacedDefaultExpression();
+ for (int offset = 0; ; ) {
+ const QRegularExpressionMatch match = regex.match(expr, offset);
+ if (!match.hasMatch())
+ break;
+ const int argId = match.capturedView(1).toInt() - 1;
+ if (argId < 0 || argId > func->arguments().size()) {
+ qCWarning(lcShiboken, "The expression used in return value contains an invalid index.");
+ break;
+ }
+ expr.replace(match.captured(0), func->arguments().at(argId).name());
+ offset = match.capturedStart(1);
+ }
+ DefaultValue defaultReturnExpr(DefaultValue::Custom, expr);
+ result.statement += defaultReturnExpr.returnValue() + u';';
+ return result;
+ }
+ }
+ }
+ QString errorMessage;
+ const auto defaultReturnExpr = minimalConstructor(api, returnType, &errorMessage);
+ if (!defaultReturnExpr.has_value()) {
+ QString errorMsg = QLatin1StringView(__FUNCTION__) + u": "_s
+ + func->classQualifiedSignature();
+ errorMsg = msgCouldNotFindMinimalConstructor(errorMsg,
+ func->type().cppSignature(),
+ errorMessage);
+ throw Exception(errorMsg);
+ }
+
+ result.needsReference = returnType.referenceType() == LValueReference;
+ result.statement += (result.needsReference
+ ? virtualMethodStaticReturnVar : defaultReturnExpr->returnValue()) + u';';
+ return result;
+}
+
+// Create an argument for Py_BuildValue() when writing virtual methods.
+// Return a pair of (argument, format-char).
+std::pair<QString, QChar> CppGenerator::virtualMethodNativeArg(const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaArgument &arg)
+{
+ if (func->hasConversionRule(TypeSystem::TargetLangCode, arg.argumentIndex() + 1))
+ return {arg.name() + CONV_RULE_OUT_VAR_SUFFIX, u'N'};
+
+ const auto &type = arg.type();
+ auto argTypeEntry = type.typeEntry();
+ // Check for primitive types convertible by Py_BuildValue()
+ if (argTypeEntry->isPrimitive() && !type.isCString()) {
+ const auto pte = basicReferencedTypeEntry(argTypeEntry);
+ auto it = formatUnits().constFind(pte->name());
+ if (it != formatUnits().constEnd())
+ return {arg.name(), it.value()};
+ }
+
+ // Rest: convert
+ StringStream ac(TextStream::Language::Cpp);
+ writeToPythonConversion(ac, type, func->ownerClass(), arg.name());
+ return {ac.toString(), u'N'};
+}
+
+static const char PYTHON_ARGS_ARRAY[] = "pyArgArray";
+
+void CppGenerator::writeVirtualMethodNativeVectorCallArgs(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaArgumentList &arguments,
+ const QList<int> &invalidateArgs)
+{
+ Q_ASSERT(!arguments.isEmpty());
+ s << "PyObject *" << PYTHON_ARGS_ARRAY <<'[' << arguments.size() << "] = {\n" << indent;
+ const qsizetype last = arguments.size() - 1;
+ for (qsizetype i = 0; i <= last; ++i) {
+ const AbstractMetaArgument &arg = arguments.at(i);
+ if (func->hasConversionRule(TypeSystem::TargetLangCode, arg.argumentIndex() + 1)) {
+ s << arg.name() + CONV_RULE_OUT_VAR_SUFFIX;
+ } else {
+ writeToPythonConversion(s, arg.type(), func->ownerClass(), arg.name());
+ }
+ if (i < last)
+ s << ',';
+ s << '\n';
+ }
+ s << outdent << "};\n";
+
+ if (!invalidateArgs.isEmpty())
+ s << '\n';
+ for (int index : invalidateArgs) {
+ s << "const bool invalidateArg" << index << " = Py_REFCNT(" << PYTHON_ARGS_ARRAY <<
+ '[' << index - 1 << "]) == 1;\n";
+ }
+}
+
+void CppGenerator::writeVirtualMethodNativeArgs(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaArgumentList &arguments,
+ const QList<int> &invalidateArgs)
+{
+ s << "Shiboken::AutoDecRef " << PYTHON_ARGS << '(';
+ if (arguments.isEmpty()) {
+ s << "PyTuple_New(0));\n";
+ return;
+ }
+
+ QString format;
+ QStringList argConversions;
+ for (const AbstractMetaArgument &arg : arguments) {
+ auto argPair = virtualMethodNativeArg(func, arg);
+ argConversions.append(argPair.first);
+ format += argPair.second;
+ }
+
+ s << "Py_BuildValue(\"(" << format << ")\",\n"
+ << indent << argConversions.join(u",\n"_s) << outdent << "\n));\n";
+
+ for (int index : std::as_const(invalidateArgs)) {
+ s << "bool invalidateArg" << index << " = Py_REFCNT(PyTuple_GET_ITEM(" << PYTHON_ARGS
+ << ", " << index - 1 << ")) == 1;\n";
+ }
+}
+
+static bool isArgumentNotRemoved(const AbstractMetaArgument &a)
+{
+ return !a.isModifiedRemoved();
+}
+
+// PyObject_Vectorcall(): since 3.9
+static const char vectorCallCondition[] =
+ "#if !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)\n";
+
+// PyObject_CallNoArgs(): since 3.9, stable API since 3.10
+static const char noArgsCallCondition[] =
+ "#if !defined(PYPY_VERSION) && ((defined(Py_LIMITED_API) && Py_LIMITED_API >= 0x030A0000) || !defined(Py_LIMITED_API))\n";
+static const char inverseNoArgsCallCondition[] =
+ "#if defined(PYPY_VERSION) || (defined(Py_LIMITED_API) && Py_LIMITED_API < 0x030A0000)\n";
+
+static inline void writeVirtualMethodStaticReturnVar(TextStream &s, const AbstractMetaFunctionCPtr &func)
+{
+ s << "static " << func->type().typeEntry()->qualifiedCppName() << ' '
+ << virtualMethodStaticReturnVar << ";\n";
+}
+
+static void writeFuncNameVar(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ const QString &funcName)
+{
+ // PYSIDE-1019: Add info about properties
+ int propFlag = 0;
+ if (func->isPropertyReader())
+ propFlag |= 1;
+ if (func->isPropertyWriter())
+ propFlag |= 2;
+ if (propFlag && func->isStatic())
+ propFlag |= 4;
+ QString propStr;
+ if (propFlag != 90)
+ propStr = QString::number(propFlag) + u':';
+
+ if (propFlag != 0)
+ s << "// This method belongs to a property.\n";
+ s << "static const char *funcName = \"";
+ if (propFlag != 0)
+ s << propFlag << ':';
+ s << funcName << "\";\n";
+}
+
+void CppGenerator::writeVirtualMethodNative(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ int cacheIndex) const
+{
+ TypeEntryCPtr retType = func->type().typeEntry();
+ const QString funcName = func->isOperatorOverload()
+ ? pythonOperatorFunctionName(func) : func->definitionNames().constFirst();
+
+ QString prefix = wrapperName(func->ownerClass()) + u"::"_s;
+ s << functionSignature(func, prefix, QString(), Generator::SkipDefaultValues |
+ Generator::OriginalTypeDescription)
+ << "\n{\n" << indent;
+
+ const auto returnStatement = virtualMethodReturn(api(), func,
+ func->modifications());
+
+ if (returnStatement.needsReference)
+ writeVirtualMethodStaticReturnVar(s, func);
+
+ const bool isAbstract = func->isAbstract();
+ if (isAbstract && func->isModifiedRemoved()) {
+ qCWarning(lcShiboken, "%s", qPrintable(msgPureVirtualFunctionRemoved(func.get())));
+ s << returnStatement.statement << '\n' << outdent << "}\n\n";
+ return;
+ }
+
+ const CodeSnipList snips = func->hasInjectedCode()
+ ? func->injectedCodeSnips() : CodeSnipList();
+ const AbstractMetaArgument *lastArg = func->arguments().isEmpty()
+ ? nullptr : &func->arguments().constLast();
+
+ // Write declaration/native injected code.
+ if (!snips.isEmpty()) {
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionDeclaration,
+ TypeSystem::ShellCode, func, false, lastArg);
+ }
+
+ if (wrapperDiagnostics()) {
+ s << "std::cerr << ";
+#ifndef Q_CC_MSVC // g++ outputs __FUNCTION__ unqualified
+ s << '"' << prefix << R"(" << )";
+#endif
+ s << R"(__FUNCTION__ << ' ' << this << " m_PyMethodCache[" << )"
+ << cacheIndex << R"( << "]=" << m_PyMethodCache[)" << cacheIndex
+ << R"(] << '\n';)" << '\n';
+ }
+ // PYSIDE-803: Build a boolean cache for unused overrides
+ const bool multi_line = func->isVoid() || !snips.isEmpty() || isAbstract;
+ s << "if (m_PyMethodCache[" << cacheIndex << "])" << (multi_line ? " {\n" : "\n")
+ << indent;
+ writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType,
+ returnStatement.statement, false);
+ s << outdent;
+ if (multi_line)
+ s << "}\n";
+
+ s << "Shiboken::GilState gil;\n";
+
+ // Get out of virtual method call if someone already threw an error.
+ s << "if (" << shibokenErrorsOccurred << ")\n" << indent
+ << returnStatement.statement << '\n' << outdent;
+
+ s << "static PyObject *nameCache[2] = {};\n";
+ writeFuncNameVar(s, func, funcName);
+ s << "Shiboken::AutoDecRef " << PYTHON_OVERRIDE_VAR
+ << "(Shiboken::BindingManager::instance().getOverride(this, nameCache, funcName));\n"
+ << "if (" << PYTHON_OVERRIDE_VAR << ".isNull()) {\n" << indent;
+ if (useOverrideCaching(func->ownerClass()))
+ s << "m_PyMethodCache[" << cacheIndex << "] = true;\n";
+ writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType,
+ returnStatement.statement, true);
+ s << outdent << "}\n\n"; //WS
+
+ if (!snips.isEmpty()) {
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionPyOverride,
+ TypeSystem::ShellCode, func, false, lastArg);
+ }
+
+ writeVirtualMethodPythonOverride(s, func, snips, returnStatement);
+}
+
+void CppGenerator::writeVirtualMethodPythonOverride(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ const CodeSnipList &snips,
+ const VirtualMethodReturn &returnStatement) const
+{
+ writeConversionRule(s, func, TypeSystem::TargetLangCode, false);
+
+ bool invalidateReturn = false;
+ QList<int> invalidateArgs;
+ for (const FunctionModification &funcMod : func->modifications()) {
+ for (const ArgumentModification &argMod : funcMod.argument_mods()) {
+ const int index = argMod.index();
+ if (index == 0) {
+ if (argMod.targetOwnerShip() == TypeSystem::CppOwnership)
+ invalidateReturn = true;
+ } else {
+ const int actualIndex = func->actualArgumentIndex(index - 1) + 1;
+ if (argMod.resetAfterUse() && !invalidateArgs.contains(actualIndex))
+ invalidateArgs.append(actualIndex);
+ }
+ }
+ }
+ std::sort(invalidateArgs.begin(), invalidateArgs.end());
+
+ auto arguments = func->arguments();
+ auto removedEnd = std::stable_partition(arguments.begin(), arguments.end(),
+ isArgumentNotRemoved);
+ if (func->isAbstract()) { // Base function is not called, indicate unused arguments.
+ for (auto it = removedEnd; it != arguments.end(); ++it)
+ s << sbkUnusedVariableCast(it->name());
+ }
+ arguments.erase(removedEnd, arguments.end());
+
+ // FIXME PYSIDE-7: new functions PyObject_Vectorcall() (since 3.9) and
+ // PyObject_CallNoArgs() (since 3.9, stable API since 3.10) might have
+ // become part of the stable API?
+
+ // Code snips might expect the args tuple, don't generate new code
+ const bool generateNewCall = snips.isEmpty();
+ const qsizetype argCount = arguments.size();
+ const char *newCallCondition = argCount == 0 ? noArgsCallCondition : vectorCallCondition;
+ if (generateNewCall) {
+ if (argCount > 0) {
+ s << newCallCondition;
+ writeVirtualMethodNativeVectorCallArgs(s, func, arguments, invalidateArgs);
+ s << "#else\n";
+ } else {
+ s << inverseNoArgsCallCondition;
+ }
+ }
+ writeVirtualMethodNativeArgs(s, func, arguments, invalidateArgs);
+ if (generateNewCall)
+ s << "#endif\n";
+ s << '\n';
+
+ if (!snips.isEmpty()) {
+ if (func->injectedCodeUsesPySelf())
+ s << "PyObject *pySelf = BindingManager::instance().retrieveWrapper(this);\n";
+
+ const AbstractMetaArgument *lastArg = func->arguments().isEmpty()
+ ? nullptr : &func->arguments().constLast();
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning,
+ TypeSystem::NativeCode, func, false, lastArg);
+ }
+
+ qsizetype returnIndirections = 0;
+
+ if (!func->injectedCodeCallsPythonOverride()) {
+ if (generateNewCall) {
+ s << newCallCondition << "Shiboken::AutoDecRef " << PYTHON_RETURN_VAR << '(';
+ if (argCount > 0) {
+ s << "PyObject_Vectorcall(" << PYTHON_OVERRIDE_VAR << ", "
+ << PYTHON_ARGS_ARRAY << ", " << argCount << ", nullptr));\n";
+ for (int argIndex : std::as_const(invalidateArgs)) {
+ s << "if (invalidateArg" << argIndex << ")\n" << indent
+ << "Shiboken::Object::invalidate(" << PYTHON_ARGS_ARRAY
+ << '[' << (argIndex - 1) << "]);\n" << outdent;
+ }
+ for (qsizetype i = 0, size = arguments.size(); i < size; ++i)
+ s << "Py_DECREF(" << PYTHON_ARGS_ARRAY << '[' << i << "]);\n";
+ } else {
+ s << "PyObject_CallNoArgs(" << PYTHON_OVERRIDE_VAR << "));\n";
+ }
+ s << "#else\n";
+ }
+ s << "Shiboken::AutoDecRef " << PYTHON_RETURN_VAR << "(PyObject_Call("
+ << PYTHON_OVERRIDE_VAR << ", " << PYTHON_ARGS << ", nullptr));\n";
+
+ for (int argIndex : std::as_const(invalidateArgs)) {
+ s << "if (invalidateArg" << argIndex << ")\n" << indent
+ << "Shiboken::Object::invalidate(PyTuple_GET_ITEM(" << PYTHON_ARGS
+ << ", " << (argIndex - 1) << "));\n" << outdent;
+ }
+ if (generateNewCall)
+ s << "#endif\n";
+
+ s << "if (" << PYTHON_RETURN_VAR << ".isNull()) {\n" << indent
+ << "// An error happened in python code!\n"
+ << "Shiboken::Errors::storePythonOverrideErrorOrPrint(\""
+ << func->ownerClass()->name() << "\", funcName);\n"
+ << returnStatement.statement << "\n" << outdent
+ << "}\n";
+
+ if (invalidateReturn) {
+ s << "bool invalidateArg0 = Py_REFCNT(" << PYTHON_RETURN_VAR << ") == 1;\n"
+ << "if (invalidateArg0)\n" << indent
+ << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR
+ << ".object());\n" << outdent;
+ }
+
+ if (!func->isVoid()) {
+
+ if (func->modifiedTypeName() != cPyObjectT) {
+
+ s << "// Check return type\n";
+
+ if (!func->isTypeModified()) {
+
+ s << PYTHON_TO_CPPCONVERSION_STRUCT << ' '
+ << PYTHON_TO_CPP_VAR << " =\n" << indent
+ << cpythonIsConvertibleFunction(func->type())
+ << PYTHON_RETURN_VAR << ");\n" << outdent
+ << "if (!" << PYTHON_TO_CPP_VAR << ") {\n" << indent
+ << "Shiboken::Warnings::warnInvalidReturnValue(\""
+ << func->ownerClass()->name() << "\", funcName, "
+ << getVirtualFunctionReturnTypeName(func) << ", "
+ << "Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);\n"
+ << returnStatement.statement << '\n' << outdent
+ << "}\n";
+
+ } else {
+
+ s << "bool typeIsValid = ";
+ if (func->isTypeModified()) {
+ writeTypeCheck(s, func->modifiedTypeName(), PYTHON_RETURN_VAR);
+ } else {
+ const bool numberType = isNumber(func->type().typeEntry());
+ writeTypeCheck(s, func->type(), PYTHON_RETURN_VAR, numberType);
+ }
+
+ s << ";\n";
+ s << "if (!typeIsValid";
+ if (func->type().isPointerToWrapperType())
+ s << " && " << PYTHON_RETURN_VAR << " != Py_None";
+ s << ") {\n" << indent
+ << "Shiboken::Warnings::warnInvalidReturnValue(\""
+ << func->ownerClass()->name() << "\", funcName, "
+ << getVirtualFunctionReturnTypeName(func) << ", "
+ << "Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);\n"
+ << returnStatement.statement << '\n' << outdent
+ << "}\n";
+
+ }
+ }
+
+ if (func->hasConversionRule(TypeSystem::NativeCode, 0)) {
+ writeConversionRule(s, func, TypeSystem::NativeCode, CPP_RETURN_VAR);
+ } else if (!func->injectedCodeHasReturnValueAttribution(TypeSystem::NativeCode)) {
+ returnIndirections = writePythonToCppTypeConversion(
+ s, func->type(), PYTHON_RETURN_VAR,
+ CPP_RETURN_VAR, func->implementingClass(), {});
+ }
+ }
+ }
+
+ for (const FunctionModification &funcMod : func->modifications()) {
+ for (const ArgumentModification &argMod : funcMod.argument_mods()) {
+ if (argMod.index() == 0
+ && argMod.nativeOwnership() == TypeSystem::CppOwnership) {
+ s << "if (Shiboken::Object::checkType(" << PYTHON_RETURN_VAR << "))\n" << indent
+ << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR << ");\n"
+ << outdent;
+ }
+ }
+ }
+
+ if (func->hasInjectedCode()) {
+ s << '\n';
+ const AbstractMetaArgument *lastArg = func->arguments().isEmpty()
+ ? nullptr : &func->arguments().constLast();
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd,
+ TypeSystem::NativeCode, func, false, lastArg);
+ }
+
+ if (!func->isVoid()) {
+ s << "return ";
+ TypeEntryCPtr retType = func->type().typeEntry();
+ if (avoidProtectedHack() && retType->isEnum()) {
+ auto metaEnum = api().findAbstractMetaEnum(retType);
+ bool isProtectedEnum = metaEnum.has_value() && metaEnum->isProtected();
+ if (isProtectedEnum) {
+ QString typeCast;
+ if (metaEnum->enclosingClass())
+ typeCast += getFullTypeName(metaEnum->enclosingClass());
+ typeCast += u"::"_s + metaEnum->name();
+ s << '(' << typeCast << ')';
+ }
+ }
+
+ if (returnIndirections > 0)
+ s << QByteArray(returnIndirections, '*');
+ s << CPP_RETURN_VAR << ";\n";
+ }
+
+ s << outdent << "}\n\n";
+}
+
+void CppGenerator::writeUserAddedPythonOverride(TextStream &s,
+ const AbstractMetaFunctionCPtr &func) const
+{
+ TypeEntryCPtr retType = func->type().typeEntry();
+ const QString funcName = func->isOperatorOverload()
+ ? pythonOperatorFunctionName(func) : func->definitionNames().constFirst();
+
+ const CodeSnipList snips = func->hasInjectedCode()
+ ? func->injectedCodeSnips() : CodeSnipList();
+
+ QString prefix = wrapperName(func->ownerClass()) + u"::"_s;
+ s << '\n' << functionSignature(func, prefix, QString(), Generator::SkipDefaultValues |
+ Generator::OriginalTypeDescription)
+ << "\n{\n" << indent << sbkUnusedVariableCast("gil");
+
+ writeFuncNameVar(s, func, funcName);
+
+ const auto returnStatement = virtualMethodReturn(api(), func,
+ func->modifications());
+ writeVirtualMethodPythonOverride(s, func, snips, returnStatement);
+}
+
+void CppGenerator::writeMetaObjectMethod(TextStream &s,
+ const GeneratorContext &classContext) const
+{
+
+ const QString wrapperClassName = classContext.wrapperName();
+ const QString qualifiedCppName = classContext.metaClass()->qualifiedCppName();
+ s << "const QMetaObject *" << wrapperClassName << "::metaObject() const\n{\n";
+ s << indent << "if (QObject::d_ptr->metaObject != nullptr)\n"
+ << indent << "return QObject::d_ptr->dynamicMetaObject();\n" << outdent
+ << "SbkObject *pySelf = Shiboken::BindingManager::instance().retrieveWrapper(this);\n"
+ << "if (pySelf == nullptr)\n"
+ << indent << "return " << qualifiedCppName << "::metaObject();\n" << outdent
+ << "return PySide::SignalManager::retrieveMetaObject("
+ "reinterpret_cast<PyObject *>(pySelf));\n"
+ << outdent << "}\n\n";
+
+ // qt_metacall function
+ s << "int " << wrapperClassName
+ << "::qt_metacall(QMetaObject::Call call, int id, void **args)\n";
+ s << "{\n" << indent;
+
+ const auto list = classContext.metaClass()->queryFunctionsByName(u"qt_metacall"_s);
+
+ CodeSnipList snips;
+ if (list.size() == 1) {
+ const auto &func = list.constFirst();
+ snips = func->injectedCodeSnips();
+ if (func->isUserAdded()) {
+ CodeSnipList snips = func->injectedCodeSnips();
+ const bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(func);
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionAny,
+ TypeSystem::NativeCode, func, usePyArgs, nullptr);
+ }
+ }
+
+ s << "int result = " << qualifiedCppName << "::qt_metacall(call, id, args);\n"
+ << "return result < 0 ? result : PySide::SignalManager::qt_metacall("
+ "this, call, id, args);\n"
+ << outdent << "}\n\n";
+
+ // qt_metacast function
+ writeMetaCast(s, classContext);
+}
+
+void CppGenerator::writeMetaCast(TextStream &s,
+ const GeneratorContext &classContext)
+{
+ const QString wrapperClassName = classContext.wrapperName();
+ const QString qualifiedCppName = classContext.metaClass()->qualifiedCppName();
+ s << "void *" << wrapperClassName << "::qt_metacast(const char *_clname)\n{\n"
+ << indent << "if (_clname == nullptr)\n" << indent << "return {};\n" << outdent
+ << "SbkObject *pySelf = Shiboken::BindingManager::instance().retrieveWrapper(this);\n"
+ << "if (pySelf != nullptr && PySide::inherits(Py_TYPE(pySelf), _clname))\n"
+ << indent << "return static_cast<void *>(const_cast< "
+ << wrapperClassName << " *>(this));\n" << outdent
+ << "return " << qualifiedCppName << "::qt_metacast(_clname);\n"
+ << outdent << "}\n\n";
+}
+
+static void generateDeprecatedValueWarnings(TextStream &c,
+ const AbstractMetaEnum &metaEnum,
+ bool useSurrogateName)
+{
+ EnumTypeEntryCPtr enumType = metaEnum.typeEntry();
+ const QString prefix = enumType->qualifiedCppName() + u"::"_s;
+ c << "switch (value) {\n";
+ const auto &deprecatedValues = metaEnum.deprecatedValues();
+ for (const auto &v : deprecatedValues) {
+ c << "case ";
+ if (useSurrogateName)
+ c << v.value().toString(); // Protected, use int representation
+ else
+ c << prefix << v.name();
+ c << ":\n" << indent
+ << "Shiboken::Warnings::warnDeprecatedEnumValue(\"" << enumType->name()
+ << "\", \"" << v.name() << "\");\nbreak;\n" << outdent;
+ }
+ if (deprecatedValues.size() < metaEnum.values().size())
+ c << "default:\n" << indent << "break;\n" << outdent;
+ c << "}\n";
+}
+
+void CppGenerator::writeEnumConverterFunctions(TextStream &s, const AbstractMetaEnum &metaEnum) const
+{
+ if (metaEnum.isPrivate() || metaEnum.isAnonymous())
+ return;
+ EnumTypeEntryCPtr enumType = metaEnum.typeEntry();
+ Q_ASSERT(enumType);
+ QString typeName = fixedCppTypeName(enumType);
+ QString enumPythonType = cpythonTypeNameExt(enumType);
+ const bool useSurrogateName = avoidProtectedHack() && metaEnum.isProtected();
+ QString cppTypeName = useSurrogateName
+ ? protectedEnumSurrogateName(metaEnum) : getFullTypeName(enumType).trimmed();
+
+ StringStream c(TextStream::Language::Cpp);
+ if (metaEnum.isDeprecated())
+ c << "Shiboken::Warnings::warnDeprecatedEnum(\"" << enumType->name() << "\");\n";
+
+ c << "const auto value = static_cast<" << cppTypeName
+ << ">(Shiboken::Enum::getValue(pyIn));\n";
+
+ // Warn about deprecated values unless it is protected+scoped (inaccessible values)
+ const bool valuesAcccessible = !useSurrogateName || metaEnum.enumKind() != EnumClass;
+ if (valuesAcccessible && metaEnum.hasDeprecatedValues())
+ generateDeprecatedValueWarnings(c, metaEnum, useSurrogateName);
+
+ c << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) = value;\n";
+
+ ConfigurableScope configScope(s, enumType);
+ writePythonToCppFunction(s, c.toString(), typeName, typeName);
+
+ QString pyTypeCheck = u"PyObject_TypeCheck(pyIn, "_s + enumPythonType + u')';
+ writeIsPythonConvertibleToCppFunction(s, typeName, typeName, pyTypeCheck);
+
+ c.clear();
+
+ c << "const int castCppIn = int(*reinterpret_cast<const "
+ << cppTypeName << " *>(cppIn));\n" << "return "
+ << "Shiboken::Enum::newItem(" << enumPythonType << ", castCppIn);\n";
+ writeCppToPythonFunction(s, c.toString(), typeName, typeName);
+ 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
+{
+ s << "// Type conversion functions.\n\n";
+
+ AbstractMetaEnumList classEnums = metaClass->enums();
+ auto typeEntry = metaClass->typeEntry();
+ metaClass->getEnumsFromInvisibleNamespacesToBeGenerated(&classEnums);
+ if (!classEnums.isEmpty())
+ s << "// Python to C++ enum conversion.\n";
+ for (const AbstractMetaEnum &metaEnum : std::as_const(classEnums))
+ writeEnumConverterFunctions(s, metaEnum);
+
+ if (metaClass->isNamespace())
+ return;
+
+ QString typeName;
+ if (!classContext.forSmartPointer())
+ typeName = getFullTypeName(metaClass);
+ else
+ typeName = getFullTypeName(classContext.preciseType());
+
+ QString cpythonType = cpythonTypeName(metaClass);
+
+ // Returns the C++ pointer of the Python wrapper.
+ s << "// Python to C++ pointer conversion - returns the C++ object of the Python wrapper (keeps object identity).\n";
+
+ QString sourceTypeName = metaClass->name();
+ QString targetTypeName = metaClass->name() + u"_PTR"_s;
+ StringStream c(TextStream::Language::Cpp);
+ c << "Shiboken::Conversions::pythonToCppPointer(" << cpythonType << ", pyIn, cppOut);";
+ writePythonToCppFunction(s, c.toString(), sourceTypeName, targetTypeName);
+
+ // "Is convertible" function for the Python object to C++ pointer conversion.
+ const QString pyTypeCheck = u"PyObject_TypeCheck(pyIn, "_s + cpythonType + u")"_s;
+ writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, pyTypeCheck, QString(), true);
+ s << '\n';
+
+ // C++ pointer to a Python wrapper, keeping identity.
+ s << "// C++ to Python pointer conversion - tries to find the Python wrapper for the C++ object (keeps object identity).\n";
+ c.clear();
+ if (usePySideExtensions() && isQObject(metaClass)) {
+ c << "return PySide::getWrapperForQObject(reinterpret_cast<"
+ << typeName << " *>(const_cast<void *>(cppIn)), " << cpythonType << ");\n";
+ } else {
+ writePointerToPythonConverter(c, metaClass, typeName, cpythonType);
+ }
+ std::swap(targetTypeName, sourceTypeName);
+ writeCppToPythonFunction(s, c.toString(), sourceTypeName, targetTypeName);
+
+ // The conversions for an Object Type end here.
+ if (!typeEntry->isValue() && !typeEntry->isSmartPointer()) {
+ s << '\n';
+ return;
+ }
+
+ // Always copies C++ value (not pointer, and not reference) to a new Python wrapper.
+ s << '\n' << "// C++ to Python copy conversion.\n";
+ targetTypeName = metaClass->name();
+
+ sourceTypeName = targetTypeName + u"_COPY"_s;
+
+ c.clear();
+
+ const bool isUniquePointer = classContext.forSmartPointer()
+ && typeEntry->isUniquePointer();
+
+ if (isUniquePointer) {
+ c << "auto *source = reinterpret_cast<" << typeName
+ << " *>(const_cast<void *>(cppIn));\n";
+ } else {
+ c << "auto *source = reinterpret_cast<const " << typeName << " *>(cppIn);\n";
+ }
+ c << "return Shiboken::Object::newObject(" << cpythonType
+ << ", new " << globalScopePrefix(classContext) << classContext.effectiveClassName() << '('
+ << (isUniquePointer ? "std::move(*source)" : "*source")
+ << "), true, true);";
+ writeCppToPythonFunction(s, c.toString(), sourceTypeName, targetTypeName);
+ s << '\n';
+
+ // Python to C++ copy conversion.
+ s << "// Python to C++ copy conversion.\n";
+ sourceTypeName = metaClass->name();
+
+ targetTypeName = sourceTypeName + "_COPY"_L1;
+ c.clear();
+
+ QString pyInVariable = u"pyIn"_s;
+ const QString outPtr = u"reinterpret_cast<"_s + typeName + u" *>(cppOut)"_s;
+ if (!classContext.forSmartPointer()) {
+ c << '*' << outPtr << " = *"
+ << cpythonWrapperCPtr(typeEntry, pyInVariable) << ';';
+ } else {
+ auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(typeEntry);
+ const QString resetMethod = ste->resetMethod();
+ c << "auto *ptr = " << outPtr << ";\n";
+ c << "if (" << pyInVariable << " == Py_None)\n" << indent;
+ if (resetMethod.isEmpty())
+ c << "*ptr = {};\n";
+ else
+ c << "ptr->" << resetMethod << "();\n";
+ const QString value = u'*' + cpythonWrapperCPtr(classContext.preciseType(), pyInVariable);
+ c << outdent << "else\n" << indent
+ << "*ptr = " << (isUniquePointer ? stdMove(value) : value) << ';';
+ }
+
+ writePythonToCppFunction(s, c.toString(), sourceTypeName, targetTypeName);
+
+ // "Is convertible" function for the Python object to C++ value copy conversion.
+ QString copyTypeCheck = pyTypeCheck;
+ if (classContext.forSmartPointer())
+ copyTypeCheck.prepend(pyInVariable + u" == Py_None || "_s);
+ writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, copyTypeCheck);
+ s << '\n';
+
+ // User provided implicit conversions.
+ // Implicit conversions.
+ const AbstractMetaFunctionCList implicitConvs = implicitConversions(typeEntry);
+
+ if (!implicitConvs.isEmpty())
+ s << "// Implicit conversions.\n";
+
+ AbstractMetaType targetType = AbstractMetaType::fromAbstractMetaClass(metaClass);
+ for (const auto &conv : std::as_const(implicitConvs)) {
+ if (conv->isModifiedRemoved())
+ continue;
+
+ QString typeCheck;
+ QString toCppConv;
+ QString toCppPreConv;
+ if (conv->isConversionOperator()) {
+ const auto sourceClass = conv->ownerClass();
+ typeCheck = u"PyObject_TypeCheck(pyIn, "_s
+ + cpythonTypeNameExt(sourceClass->typeEntry()) + u')';
+ toCppConv = u'*' + cpythonWrapperCPtr(sourceClass->typeEntry(),
+ pyInVariable);
+ } else {
+ // Constructor that does implicit conversion.
+ const auto &firstArg = conv->arguments().constFirst();
+ if (firstArg.isTypeModified() || conv->isModifiedToArray(1))
+ continue;
+ const AbstractMetaType &sourceType = firstArg.type();
+ if (sourceType.isWrapperType()) {
+ if (sourceType.referenceType() == LValueReference
+ || !sourceType.isPointerToWrapperType()) {
+ toCppConv = u" *"_s;
+ }
+ toCppConv += cpythonWrapperCPtr(sourceType.typeEntry(), pyInVariable);
+ }
+
+ typeCheck = cpythonCheckFunction(sourceType);
+ if (typeCheck.endsWith(u", ")) {
+ typeCheck += pyInVariable + u')';
+ } else if (typeCheck != u"true" && typeCheck != u"false") {
+ typeCheck += u'(' + pyInVariable + u')';
+ }
+
+ if (sourceType.isUserPrimitive()
+ || sourceType.isExtendedCppPrimitive()
+ || sourceType.typeEntry()->isContainer()
+ || sourceType.typeEntry()->isEnum()
+ || sourceType.typeEntry()->isFlags()) {
+ StringStream pc(TextStream::Language::Cpp);
+ pc << getFullTypeNameWithoutModifiers(sourceType) << " cppIn"
+ << minimalConstructorExpression(api(), sourceType) << ";\n";
+ writeToCppConversion(pc, sourceType, pyInVariable,
+ u"cppIn"_s);
+ pc << ';';
+ toCppPreConv = pc.toString();
+ toCppConv.append(u"cppIn"_s);
+ } else if (!sourceType.isWrapperType()) {
+ StringStream tcc(TextStream::Language::Cpp);
+ writeToCppConversion(tcc, sourceType, pyInVariable,
+ u"/*BOZO-1061*/"_s);
+ toCppConv = tcc.toString();
+ }
+ }
+ const AbstractMetaType sourceType = conv->isConversionOperator()
+ ? AbstractMetaType::fromAbstractMetaClass(conv->ownerClass())
+ : conv->arguments().constFirst().type();
+ writePythonToCppConversionFunctions(s, sourceType, targetType, typeCheck, toCppConv, toCppPreConv);
+ }
+
+ if (typeEntry->isValue()) {
+ auto vte = std::static_pointer_cast<const ValueTypeEntry>(typeEntry);
+ writeCustomConverterFunctions(s, vte->customConversion());
+ }
+}
+
+void CppGenerator::writeCustomConverterFunctions(TextStream &s,
+ const CustomConversionPtr &customConversion) const
+{
+ if (!customConversion)
+ return;
+ const TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions();
+ if (toCppConversions.isEmpty())
+ return;
+ auto ownerType = customConversion->ownerType();
+ s << "// Python to C++ conversions for type '" << ownerType->qualifiedCppName() << "'.\n";
+ for (const auto &toNative : toCppConversions)
+ writePythonToCppConversionFunctions(s, toNative, ownerType);
+ s << '\n';
+}
+
+void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClassCPtr &metaClass,
+ const GeneratorContext &classContext) const
+{
+ const auto typeEntry = metaClass->typeEntry();
+ if (typeEntry->isNamespace())
+ return;
+ s << "// Register Converter\n"
+ << "SbkConverter *converter = Shiboken::Conversions::createConverter(pyType,\n"
+ << indent;
+ QString sourceTypeName = metaClass->name();
+ QString targetTypeName = sourceTypeName + u"_PTR"_s;
+ s << pythonToCppFunctionName(sourceTypeName, targetTypeName) << ',' << '\n'
+ << convertibleToCppFunctionName(sourceTypeName, targetTypeName) << ',' << '\n';
+ std::swap(targetTypeName, sourceTypeName);
+ s << cppToPythonFunctionName(sourceTypeName, targetTypeName);
+ if (typeEntry->isValue() || typeEntry->isSmartPointer()) {
+ s << ',' << '\n';
+ sourceTypeName = metaClass->name() + u"_COPY"_s;
+ s << cppToPythonFunctionName(sourceTypeName, targetTypeName);
+ }
+ s << outdent << ");\n\n";
+
+ auto writeConversions = [&s](const QString &signature)
+ {
+ s << registerConverterName(signature) << registerConverterName(signature + u'*')
+ << registerConverterName(signature + u'&');
+ };
+
+ auto writeConversionsForType = [writeConversions](const QString &fullTypeName)
+ {
+ QStringList lst = fullTypeName.split(u"::"_s,
+ Qt::SkipEmptyParts);
+ while (!lst.isEmpty()) {
+ QString signature = lst.join(u"::"_s);
+ writeConversions(signature);
+ lst.removeFirst();
+ }
+ };
+
+
+ if (!classContext.forSmartPointer()) {
+ writeConversionsForType(metaClass->qualifiedCppName());
+ } else {
+ const QString &smartPointerType = classContext.preciseType().instantiations().at(0).cppSignature();
+ const QString &smartPointerName = classContext.preciseType().typeEntry()->name();
+
+ QStringList lst = smartPointerType.split(u"::"_s,
+ Qt::SkipEmptyParts);
+ while (!lst.isEmpty()) {
+ QString signature = lst.join(u"::"_s);
+ writeConversions(smartPointerName + u'<' + signature + u'>');
+ lst.removeFirst();
+ }
+
+ writeConversionsForType(smartPointerType);
+ }
+
+ s << "Shiboken::Conversions::registerConverterName(converter, typeid(" << m_gsp;
+ QString qualifiedCppNameInvocation;
+ if (!classContext.forSmartPointer())
+ qualifiedCppNameInvocation = metaClass->qualifiedCppName();
+ else
+ qualifiedCppNameInvocation = classContext.preciseType().cppSignature();
+
+ s << qualifiedCppNameInvocation << ").name());\n";
+
+ if (classContext.useWrapper()) {
+ s << "Shiboken::Conversions::registerConverterName(converter, typeid("
+ << classContext.wrapperName() << ").name());\n";
+ }
+
+ if (!typeEntry->isValue() && !typeEntry->isSmartPointer())
+ return;
+
+ // Python to C++ copy (value, not pointer neither reference) conversion.
+ s << "\n// Add Python to C++ copy (value, not pointer neither reference) conversion to type converter.\n";
+ sourceTypeName = metaClass->name();
+ targetTypeName = sourceTypeName + u"_COPY"_s;
+ QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName);
+ QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName);
+ writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv);
+
+ // User provided implicit conversions.
+
+ // Add implicit conversions.
+ const AbstractMetaFunctionCList implicitConvs = implicitConversions(typeEntry);
+
+ if (!implicitConvs.isEmpty())
+ s << "// Add implicit conversions to type converter.\n";
+
+ AbstractMetaType targetType = AbstractMetaType::fromAbstractMetaClass(metaClass);
+ for (const auto &conv : std::as_const(implicitConvs)) {
+ if (conv->isModifiedRemoved())
+ continue;
+ AbstractMetaType sourceType;
+ if (conv->isConversionOperator()) {
+ sourceType = AbstractMetaType::fromAbstractMetaClass(conv->ownerClass());
+ } else {
+ // Constructor that does implicit conversion.
+ const auto &firstArg = conv->arguments().constFirst();
+ if (firstArg.isTypeModified() || conv->isModifiedToArray(1))
+ continue;
+ sourceType = firstArg.type();
+ }
+ QString toCpp = pythonToCppFunctionName(sourceType, targetType);
+ QString isConv = convertibleToCppFunctionName(sourceType, targetType);
+ writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv);
+ }
+
+ if (typeEntry->isValue()) {
+ auto vte = std::static_pointer_cast<const ValueTypeEntry>(typeEntry);
+ writeCustomConverterRegister(s, vte->customConversion(), u"converter"_s);
+ }
+}
+
+void CppGenerator::writeCustomConverterRegister(TextStream &s,
+ const CustomConversionPtr &customConversion,
+ const QString &converterVar)
+{
+ if (!customConversion)
+ return;
+ const TargetToNativeConversions &toCppConversions =
+ customConversion->targetToNativeConversions();
+ if (toCppConversions.isEmpty())
+ return;
+ s << "// Add user defined implicit conversions to type converter.\n";
+ for (const auto &toNative : toCppConversions) {
+ QString toCpp = pythonToCppFunctionName(toNative, customConversion->ownerType());
+ QString isConv = convertibleToCppFunctionName(toNative, customConversion->ownerType());
+ writeAddPythonToCppConversion(s, converterVar, toCpp, isConv);
+ }
+}
+
+void CppGenerator::writeContainerConverterFunctions(TextStream &s,
+ const AbstractMetaType &containerType) const
+{
+ writeCppToPythonFunction(s, containerType);
+ writePythonToCppConversionFunctions(s, containerType);
+}
+
+bool CppGenerator::needsArgumentErrorHandling(const OverloadData &overloadData)
+{
+ if (overloadData.maxArgs() > 0)
+ return true;
+ // QObject constructors need error handling when passing properties as kwarg.
+ if (!usePySideExtensions())
+ return false;
+ auto rfunc = overloadData.referenceFunction();
+ return rfunc->functionType() == AbstractMetaFunction::ConstructorFunction
+ && isQObject(rfunc->ownerClass());
+}
+
+void CppGenerator::writeMethodWrapperPreamble(TextStream &s,
+ const OverloadData &overloadData,
+ const GeneratorContext &context,
+ ErrorReturn errorReturn)
+{
+ const auto rfunc = overloadData.referenceFunction();
+ const auto ownerClass = rfunc->targetLangOwner();
+ Q_ASSERT(ownerClass == context.metaClass());
+ int minArgs = overloadData.minArgs();
+ int maxArgs = overloadData.maxArgs();
+ bool initPythonArguments;
+
+ // If method is a constructor...
+ if (rfunc->isConstructor()) {
+ // Check if the right constructor was called.
+ if (!ownerClass->hasPrivateDestructor()) {
+ s << "if (Shiboken::Object::isUserType(self) && "
+ << "!Shiboken::ObjectType::canCallConstructor(self->ob_type, Shiboken::SbkType< "
+ << m_gsp;
+ QString qualifiedCppName;
+ if (!context.forSmartPointer())
+ qualifiedCppName = ownerClass->qualifiedCppName();
+ else
+ qualifiedCppName = context.preciseType().cppSignature();
+
+ s << qualifiedCppName << " >()))\n" << indent << errorReturn << outdent << '\n';
+ }
+ // Declare pointer for the underlying C++ object.
+ s << globalScopePrefix(context) << context.effectiveClassName() << " *cptr{};\n";
+
+ initPythonArguments = maxArgs > 0;
+
+ } else {
+ if (rfunc->implementingClass() &&
+ (!rfunc->implementingClass()->isNamespace() && overloadData.hasInstanceFunction())) {
+ CppSelfDefinitionFlags flags;
+ if (overloadData.hasStaticFunction())
+ flags.setFlag(CppSelfDefinitionFlag::HasStaticOverload);
+ if (overloadData.hasClassMethod())
+ flags.setFlag(CppSelfDefinitionFlag::HasClassMethodOverload);
+ writeCppSelfDefinition(s, rfunc, context, errorReturn, flags);
+ }
+ if (!rfunc->isInplaceOperator() && overloadData.hasNonVoidReturnType())
+ s << "PyObject *" << PYTHON_RETURN_VAR << "{};\n";
+
+ initPythonArguments = minArgs != maxArgs || maxArgs > 1;
+ }
+
+ if (needsArgumentErrorHandling(overloadData))
+ s << "Shiboken::AutoDecRef errInfo{};\n";
+
+ s << "static const char fullName[] = \"" << fullPythonFunctionName(rfunc, true)
+ << "\";\nSBK_UNUSED(fullName)\n"
+ << "Shiboken::PythonContextMarker pcm;\n";
+ // PYSIDE-2335: Mark blocking calls like `exec` or `run` as such.
+ bool isBlockingFunction = rfunc->name() == u"exec"_s || rfunc->name() == u"exec_"_s
+ || rfunc->name() == u"run"_s;
+ if (isBlockingFunction)
+ s << "pcm.setBlocking();\n";
+
+ if (maxArgs > 0) {
+ s << "int overloadId = -1;\n"
+ << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR;
+ if (overloadData.pythonFunctionWrapperUsesListOfArguments())
+ s << '[' << maxArgs << ']';
+ s << ";\n" << sbkUnusedVariableCast(PYTHON_TO_CPP_VAR);
+ }
+
+ if (initPythonArguments) {
+ s << "const Py_ssize_t numArgs = ";
+ if (minArgs == 0 && maxArgs == 1 && !rfunc->isConstructor()
+ && !overloadData.pythonFunctionWrapperUsesListOfArguments()) {
+ s << "(" << PYTHON_ARG << " == 0 ? 0 : 1);\n";
+ } else {
+ writeArgumentsInitializer(s, overloadData, errorReturn);
+ }
+ }
+}
+
+void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &overloadData,
+ const GeneratorContext &classContext) const
+{
+ const ErrorReturn errorReturn = ErrorReturn::MinusOne;
+
+ const auto rfunc = overloadData.referenceFunction();
+ const auto metaClass = rfunc->ownerClass();
+
+ s << "static int\n";
+ s << cpythonFunctionName(rfunc)
+ << "(PyObject *self, PyObject *args, PyObject *kwds)\n{\n" << indent;
+ if (overloadData.maxArgs() == 0 || metaClass->isAbstract())
+ s << sbkUnusedVariableCast("args");
+ s << sbkUnusedVariableCast("kwds");
+
+ const bool needsMetaObject = usePySideExtensions() && isQObject(metaClass);
+ if (needsMetaObject)
+ s << "const QMetaObject *metaObject;\n";
+
+ s << "auto *sbkSelf = reinterpret_cast<SbkObject *>(self);\n";
+
+ if (metaClass->isAbstract() || metaClass->baseClassNames().size() > 1) {
+ s << "PyTypeObject *type = self->ob_type;\n"
+ << "PyTypeObject *myType = "
+ << cpythonTypeNameExt(metaClass->typeEntry()) << ";\n";
+ }
+
+ if (metaClass->isAbstract()) {
+ // C++ Wrapper disabled: Abstract C++ class cannot be instantiated.
+ if (metaClass->typeEntry()->typeFlags().testFlag(ComplexTypeEntry::DisableWrapper)) {
+ s << sbkUnusedVariableCast("sbkSelf")
+ << sbkUnusedVariableCast("type")
+ << sbkUnusedVariableCast("myType");
+ if (needsMetaObject)
+ s << sbkUnusedVariableCast("metaObject");
+ s << "Shiboken::Errors::setInstantiateAbstractClassDisabledWrapper(\""
+ << metaClass->qualifiedCppName() << "\");\n" << errorReturn << outdent
+ << "}\n\n";
+ return;
+ }
+
+ // Refuse to instantiate Abstract C++ class (via C++ Wrapper) unless it is
+ // a Python-derived class for which type != myType.
+ s << "if (type == myType) {\n" << indent
+ << "Shiboken::Errors::setInstantiateAbstractClass(\"" << metaClass->qualifiedCppName()
+ << "\");\n" << errorReturn << outdent
+ << "}\n\n";
+ }
+
+ if (metaClass->baseClassNames().size() > 1) {
+ if (!metaClass->isAbstract())
+ s << "if (type != myType)\n" << indent;
+ s << "Shiboken::ObjectType::copyMultipleInheritance(type, myType);\n";
+ if (!metaClass->isAbstract())
+ s << outdent << '\n';
+ }
+
+ // PYSIDE-1478: Switching must also happen at object creation time.
+ if (usePySideExtensions() && !classContext.forSmartPointer())
+ s << "PySide::Feature::Select(self);\n";
+
+ writeMethodWrapperPreamble(s, overloadData, classContext, errorReturn);
+
+ s << '\n';
+
+ if (overloadData.maxArgs() > 0)
+ writeOverloadedFunctionDecisor(s, overloadData, errorReturn);
+
+ // Handles Python Multiple Inheritance
+ QString pre = needsMetaObject ? u"bool usesPyMI = "_s : u""_s;
+ s << "\n// PyMI support\n"
+ << pre << "Shiboken::callInheritedInit(self, args, kwds, fullName);\n"
+ << "if (" << shibokenErrorsOccurred << ")\n"
+ << indent << errorReturn << outdent << "\n";
+
+ writeFunctionCalls(s, overloadData, classContext, errorReturn);
+ s << '\n';
+
+ const QString typeName = classContext.forSmartPointer()
+ ? classContext.preciseType().cppSignature() : metaClass->qualifiedCppName();
+ s << "if (" << shibokenErrorsOccurred
+ << " || !Shiboken::Object::setCppPointer(sbkSelf, Shiboken::SbkType< "
+ << globalScopePrefix(classContext) << typeName << " >(), cptr)) {\n"
+ << indent << "delete cptr;\n" << errorReturn << outdent
+ << "}\n";
+ if (overloadData.maxArgs() > 0)
+ s << "if (cptr == nullptr)\n" << indent
+ << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n\n"
+ << outdent;
+
+ s << "Shiboken::Object::setValidCpp(sbkSelf, true);\n";
+ // If the created C++ object has a C++ wrapper the ownership is assigned to Python
+ // (first "1") and the flag indicating that the Python wrapper holds an C++ wrapper
+ // is marked as true (the second "1"). Otherwise the default values apply:
+ // Python owns it and C++ wrapper is false.
+ if (shouldGenerateCppWrapper(overloadData.referenceFunction()->ownerClass()))
+ s << "Shiboken::Object::setHasCppWrapper(sbkSelf, true);\n";
+ // Need to check if a wrapper for same pointer is already registered
+ // Caused by bug PYSIDE-217, where deleted objects' wrappers are not released
+ s << "if (Shiboken::BindingManager::instance().hasWrapper(cptr)) {\n" << indent
+ << "Shiboken::BindingManager::instance().releaseWrapper("
+ "Shiboken::BindingManager::instance().retrieveWrapper(cptr));\n" << outdent
+ << "}\nShiboken::BindingManager::instance().registerWrapper(sbkSelf, cptr);\n";
+
+ // Create metaObject and register signal/slot
+ if (needsMetaObject) {
+ s << "\n// QObject setup\n"
+ << "PySide::Signal::updateSourceObject(self);\n"
+ << "metaObject = cptr->metaObject(); // <- init python qt properties\n"
+ << "if (!errInfo.isNull() && PyDict_Check(errInfo.object())) {\n" << indent
+ << "if (!PySide::fillQtProperties(self, metaObject, errInfo, usesPyMI))\n" << indent
+ << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"
+ << outdent << outdent
+ << "};\n";
+ }
+
+ // Constructor code injections, position=end
+ bool hasCodeInjectionsAtEnd = false;
+ for (const auto &func : overloadData.overloads()) {
+ const CodeSnipList &injectedCodeSnips = func->injectedCodeSnips();
+ for (const CodeSnip &cs : injectedCodeSnips) {
+ if (cs.position == TypeSystem::CodeSnipPositionEnd) {
+ hasCodeInjectionsAtEnd = true;
+ break;
+ }
+ }
+ }
+ if (hasCodeInjectionsAtEnd) {
+ // FIXME: C++ arguments are not available in code injection on constructor when position = end.
+ s << "switch (overloadId) {\n";
+ for (const auto &func : overloadData.overloads()) {
+ s << indent;
+ const CodeSnipList &injectedCodeSnips = func->injectedCodeSnips();
+ for (const CodeSnip &cs : injectedCodeSnips) {
+ if (cs.position == TypeSystem::CodeSnipPositionEnd) {
+ s << "case " << metaClass->functions().indexOf(func) << ':' << '\n'
+ << "{\n" << indent;
+ writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionEnd,
+ TypeSystem::TargetLangCode, func,
+ true /* usesPyArgs */, nullptr);
+ s << outdent << "}\nbreak;\n";
+ break;
+ }
+ }
+ s << outdent;
+ }
+ s << "}\n";
+ }
+
+ s << "\n\nreturn 1;\n";
+ s<< outdent << "}\n\n";
+}
+
+void CppGenerator::writeMethodWrapper(TextStream &s, const OverloadData &overloadData,
+ const GeneratorContext &classContext) const
+{
+ const auto rfunc = overloadData.referenceFunction();
+
+ int maxArgs = overloadData.maxArgs();
+
+ s << "static PyObject *";
+ s << cpythonFunctionName(rfunc) << "(PyObject *self";
+ bool hasKwdArgs = false;
+ if (maxArgs > 0) {
+ s << ", PyObject *"
+ << (overloadData.pythonFunctionWrapperUsesListOfArguments() ? u"args"_s : PYTHON_ARG);
+ hasKwdArgs = overloadData.hasArgumentWithDefaultValue() || rfunc->isCallOperator();
+ if (hasKwdArgs)
+ s << ", PyObject *kwds";
+ }
+ s << ")\n{\n" << indent;
+ if (rfunc->ownerClass() == nullptr || overloadData.hasStaticFunction())
+ s << sbkUnusedVariableCast(PYTHON_SELF_VAR);
+ if (hasKwdArgs)
+ s << sbkUnusedVariableCast("kwds");
+
+ writeMethodWrapperPreamble(s, overloadData, classContext);
+
+ s << '\n';
+
+ // This code is intended for shift operations only: Make sure reverse <</>>
+ // operators defined in other classes (specially from other modules)
+ // are called. A proper and generic solution would require an reengineering
+ // in the operator system like the extended converters.
+ // Solves #119 - QDataStream <</>> operators not working for QPixmap.
+ const bool hasReturnValue = overloadData.hasNonVoidReturnType();
+
+ if (hasReturnValue && rfunc->functionType() == AbstractMetaFunction::ShiftOperator
+ && rfunc->isBinaryOperator()) {
+ // For custom classes, operations like __radd__ and __rmul__
+ // will enter an infinite loop.
+ const QString pythonOp = ShibokenGenerator::pythonOperatorFunctionName(rfunc);
+ s << "static PyObject *attrName = Shiboken::PyMagicName::r"
+ << pythonOp.mid(2, pythonOp.size() -4) << "();\n" // Strip __
+ << "if (!isReverse\n" << indent
+ << "&& Shiboken::Object::checkType(" << PYTHON_ARG << ")\n"
+ << "&& !PyObject_TypeCheck(" << PYTHON_ARG << ", self->ob_type)\n"
+ << "&& PyObject_HasAttr(" << PYTHON_ARG << ", attrName)) {\n"
+ << "PyObject *revOpMethod = PyObject_GetAttr(" << PYTHON_ARG << ", attrName);\n"
+ << "if (revOpMethod && PyCallable_Check(revOpMethod)) {\n" << indent
+ << PYTHON_RETURN_VAR << " = PyObject_CallFunction(revOpMethod, \"O\", self);\n"
+ << "if (" << shibokenErrorsOccurred
+ << " && (PyErr_ExceptionMatches(PyExc_NotImplementedError)"
+ << " || PyErr_ExceptionMatches(PyExc_AttributeError))) {\n" << indent
+ << "PyErr_Clear();\n"
+ << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");\n"
+ << PYTHON_RETURN_VAR << " = " << NULL_PTR << ";\n"
+ << outdent << "}\n"
+ << outdent << "}\n"
+ << "Py_XDECREF(revOpMethod);\n\n"
+ << outdent << "}\n\n"
+ << "// Do not enter here if other object has implemented a reverse operator.\n"
+ << "if (" << PYTHON_RETURN_VAR << " == nullptr) {\n" << indent;
+ if (maxArgs > 0)
+ writeOverloadedFunctionDecisor(s, overloadData, ErrorReturn::Default);
+ writeFunctionCalls(s, overloadData, classContext, ErrorReturn::Default);
+ s << outdent << '\n' << "} // End of \"if (!" << PYTHON_RETURN_VAR << ")\"\n";
+ } else { // binary shift operator
+ if (maxArgs > 0)
+ writeOverloadedFunctionDecisor(s, overloadData, ErrorReturn::Default);
+ writeFunctionCalls(s, overloadData, classContext, ErrorReturn::Default);
+ }
+
+ s << '\n';
+
+ writeFunctionReturnErrorCheckSection(s, ErrorReturn::Default,
+ hasReturnValue && !rfunc->isInplaceOperator());
+
+ if (hasReturnValue) {
+ if (rfunc->isInplaceOperator()) {
+ s << "Py_INCREF(self);\nreturn self;\n";
+ } else {
+ s << "return " << PYTHON_RETURN_VAR << ";\n";
+ }
+ } else {
+ s << "Py_RETURN_NONE;\n";
+ }
+
+ s<< outdent << "}\n\n";
+}
+
+void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData &overloadData,
+ ErrorReturn errorReturn)
+{
+ const auto rfunc = overloadData.referenceFunction();
+ s << "PyTuple_GET_SIZE(args);\n" << sbkUnusedVariableCast("numArgs");
+
+ int minArgs = overloadData.minArgs();
+ int maxArgs = overloadData.maxArgs();
+
+ s << "PyObject *";
+ s << PYTHON_ARGS << "[] = {"
+ << QByteArrayList(maxArgs, "nullptr").join(", ")
+ << "};\n\n";
+
+ if (overloadData.hasVarargs()) {
+ maxArgs--;
+ if (minArgs > maxArgs)
+ minArgs = maxArgs;
+
+ s << "PyObject *nonvarargs = PyTuple_GetSlice(args, 0, " << maxArgs << ");\n"
+ << "Shiboken::AutoDecRef auto_nonvarargs(nonvarargs);\n"
+ << PYTHON_ARGS << '[' << maxArgs << "] = PyTuple_GetSlice(args, "
+ << maxArgs << ", numArgs);\n"
+ << "Shiboken::AutoDecRef auto_varargs(" << PYTHON_ARGS << "["
+ << maxArgs << "]);\n\n";
+ }
+
+ bool usesNamedArguments = overloadData.hasArgumentWithDefaultValue();
+
+ s << "// invalid argument lengths\n";
+
+ // Disable argument count checks for QObject constructors to allow for
+ // passing properties as KW args.
+ const auto owner = rfunc->ownerClass();
+ bool isQObjectConstructor = owner && isQObject(owner)
+ && rfunc->functionType() == AbstractMetaFunction::ConstructorFunction;
+
+ if (usesNamedArguments && !isQObjectConstructor) {
+ s << "errInfo.reset(Shiboken::checkInvalidArgumentCount(numArgs, "
+ << minArgs << ", " << maxArgs << "));\n"
+ << "if (!errInfo.isNull())\n" << indent
+ << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"
+ << outdent;
+ }
+
+ const QList<int> invalidArgsLength = overloadData.invalidArgumentLengths();
+ if (!invalidArgsLength.isEmpty()) {
+ s << "if (";
+ for (qsizetype i = 0, size = invalidArgsLength.size(); i < size; ++i) {
+ if (i)
+ s << " || ";
+ s << "numArgs == " << invalidArgsLength.at(i);
+ }
+ s << ")\n" << indent
+ << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"
+ << outdent;
+ }
+ s << '\n';
+
+ QString funcName;
+ if (rfunc->isOperatorOverload())
+ funcName = ShibokenGenerator::pythonOperatorFunctionName(rfunc);
+ else
+ funcName = rfunc->name();
+
+ QString argsVar = overloadData.hasVarargs() ? u"nonvarargs"_s : u"args"_s;
+ s << "if (";
+ if (usesNamedArguments) {
+ s << "PyArg_ParseTuple(" << argsVar << ", \"|" << QByteArray(maxArgs, 'O')
+ << ':' << funcName << '"';
+ } else {
+ s << "PyArg_UnpackTuple(" << argsVar << ", \"" << funcName << "\", "
+ << minArgs << ", " << maxArgs;
+ }
+ for (int i = 0; i < maxArgs; i++)
+ s << ", &(" << PYTHON_ARGS << '[' << i << "])";
+ s << ") == 0)\n" << indent << errorReturn << outdent << '\n';
+}
+
+void CppGenerator::writeCppSelfConversion(TextStream &s, const GeneratorContext &context,
+ const QString &className, bool useWrapperClass)
+{
+ if (context.forSmartPointer()) {
+ writeSmartPointerCppSelfConversion(s, context);
+ return;
+ }
+
+ if (useWrapperClass)
+ s << "static_cast<" << className << " *>(";
+ s << cpythonWrapperCPtr(context.metaClass(), PYTHON_SELF_VAR);
+ if (useWrapperClass)
+ s << ')';
+}
+
+void CppGenerator::writeCppSelfVarDef(TextStream &s,
+ CppSelfDefinitionFlags flags)
+{
+ if (flags.testFlag(CppGenerator::CppSelfAsReference))
+ s << "auto &" << CPP_SELF_VAR << " = *";
+ else
+ s << "auto *" << CPP_SELF_VAR << " = ";
+}
+
+void CppGenerator::writeCppSelfDefinition(TextStream &s,
+ const GeneratorContext &context,
+ ErrorReturn errorReturn,
+ CppSelfDefinitionFlags flags)
+{
+ Q_ASSERT(!(flags.testFlag(CppSelfAsReference) && flags.testFlag(HasStaticOverload)));
+ if (context.forSmartPointer()) {
+ writeSmartPointerCppSelfDefinition(s, context, errorReturn, flags);
+ return;
+ }
+
+ AbstractMetaClassCPtr metaClass = context.metaClass();
+ const auto cppWrapper = context.metaClass()->cppWrapper();
+ // In the Python method, use the wrapper to access the protected
+ // functions.
+ const bool useWrapperClass = avoidProtectedHack()
+ && cppWrapper.testFlag(AbstractMetaClass::CppProtectedHackWrapper);
+ Q_ASSERT(!useWrapperClass || context.useWrapper());
+ const QString className = useWrapperClass
+ ? context.wrapperName() : getFullTypeName(metaClass);
+
+ writeInvalidPyObjectCheck(s, PYTHON_SELF_VAR, errorReturn);
+
+ if (flags.testFlag(CppSelfAsReference)) {
+ writeCppSelfVarDef(s, flags);
+ writeCppSelfConversion(s, context, className, useWrapperClass);
+ s << ";\n";
+ return;
+ }
+
+ if (!flags.testFlag(HasStaticOverload)) {
+ if (!flags.testFlag(HasClassMethodOverload)) {
+ // PYSIDE-131: The single case of a class method for now: tr().
+ writeCppSelfVarDef(s, flags);
+ writeCppSelfConversion(s, context, className, useWrapperClass);
+ s << ";\n" << sbkUnusedVariableCast(CPP_SELF_VAR);
+ }
+ return;
+ }
+
+ s << className << " *" << CPP_SELF_VAR << " = nullptr;\n"
+ << sbkUnusedVariableCast(CPP_SELF_VAR);
+
+ // Checks if the underlying C++ object is valid.
+ s << "if (self)\n" << indent
+ << CPP_SELF_VAR << " = ";
+ writeCppSelfConversion(s, context, className, useWrapperClass);
+ s << ";\n"<< outdent;
+}
+
+void CppGenerator::writeCppSelfDefinition(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ const GeneratorContext &context,
+ ErrorReturn errorReturn,
+ CppSelfDefinitionFlags flags)
+{
+ if (!func->ownerClass() || func->isConstructor())
+ return;
+
+ if (func->isOperatorOverload() && func->isBinaryOperator()) {
+ QString checkFunc = cpythonCheckFunction(func->ownerClass()->typeEntry());
+ s << "bool isReverse = " << checkFunc << PYTHON_ARG << ")\n"
+ << " && !" << checkFunc << "self);\n"
+ << "if (isReverse)\n" << indent
+ << "std::swap(self, " << PYTHON_ARG << ");\n" << outdent;
+ }
+
+ writeCppSelfDefinition(s, context, errorReturn, flags);
+}
+
+QString CppGenerator::returnErrorWrongArguments(const OverloadData &overloadData,
+ ErrorReturn errorReturn)
+{
+ const auto rfunc = overloadData.referenceFunction();
+ QString argsVar = overloadData.pythonFunctionWrapperUsesListOfArguments()
+ ? u"args"_s : PYTHON_ARG;
+ switch (errorReturn) {
+ case ErrorReturn::Default:
+ return u"Shiboken::returnWrongArguments("_s + argsVar + u", fullName, errInfo)"_s;
+ case ErrorReturn::Zero:
+ return u"Shiboken::returnWrongArguments_Zero("_s + argsVar + u", fullName, errInfo)"_s;
+ case ErrorReturn::MinusOne:
+ return u"Shiboken::returnWrongArguments_MinusOne("_s + argsVar + u", fullName, errInfo)"_s;
+ case ErrorReturn::Void:
+ Q_ASSERT(false);
+ }
+ return {};
+}
+
+void CppGenerator::writeFunctionReturnErrorCheckSection(TextStream &s,
+ ErrorReturn errorReturn,
+ bool hasReturnValue)
+{
+ s << "if (" << shibokenErrorsOccurred;
+ if (hasReturnValue)
+ s << " || " << PYTHON_RETURN_VAR << " == nullptr";
+ s << ") {\n" << indent;
+ if (hasReturnValue)
+ s << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");\n";
+ s << errorReturn << outdent << "}\n";
+}
+
+void CppGenerator::writeInvalidPyObjectCheck(TextStream &s, const QString &pyObj,
+ ErrorReturn errorReturn)
+{
+ s << "if (!Shiboken::Object::isValid(" << pyObj << "))\n"
+ << indent << errorReturn << outdent;
+}
+
+static QString pythonToCppConverterForArgumentName(const QString &argumentName)
+{
+ static const QRegularExpression pyArgsRegex(PYTHON_ARGS
+ + uR"((\[\d+[-]?\d*\]))"_s);
+ Q_ASSERT(pyArgsRegex.isValid());
+ const QRegularExpressionMatch match = pyArgsRegex.match(argumentName);
+ QString result = PYTHON_TO_CPP_VAR;
+ if (match.hasMatch())
+ result += match.captured(1);
+ return result;
+}
+
+void CppGenerator::writeTypeCheck(TextStream &s, const QString &customType,
+ const QString &argumentName)
+{
+ QString errorMessage;
+ const auto metaTypeOpt = AbstractMetaType::fromString(customType, &errorMessage);
+ if (!metaTypeOpt.has_value())
+ throw Exception(errorMessage);
+ writeTypeCheck(s, metaTypeOpt.value(), argumentName,
+ ShibokenGenerator::isNumber(metaTypeOpt.value()));
+}
+
+void CppGenerator::writeTypeCheck(TextStream &s, const AbstractMetaType &argType,
+ const QString &argumentName, bool isNumber,
+ bool rejectNull)
+{
+ // TODO-CONVERTER: merge this with the code below.
+ QString typeCheck = cpythonIsConvertibleFunction(argType);
+ if (typeCheck != u"true") // For PyObject, which is always true
+ typeCheck.append(u'(' +argumentName + u')');
+
+ // TODO-CONVERTER -----------------------------------------------------------------------
+ if (!argType.typeEntry()->isCustom()) {
+ typeCheck = u'(' + pythonToCppConverterForArgumentName(argumentName)
+ + u" = "_s + typeCheck + u"))"_s;
+ if (!isNumber && isCppPrimitive(argType.typeEntry())) {
+ typeCheck.prepend(cpythonCheckFunction(argType) + u'('
+ + argumentName + u") && "_s);
+ }
+ }
+ // TODO-CONVERTER -----------------------------------------------------------------------
+
+ if (rejectNull)
+ typeCheck = u'(' + argumentName + u" != Py_None && "_s + typeCheck + u')';
+
+ s << typeCheck;
+}
+
+static void checkTypeViability(const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaType &type, int argIdx)
+{
+ const bool modified = argIdx == 0
+ ? func->isTypeModified()
+ : func->arguments().at(argIdx -1).isTypeModified();
+ const bool isRemoved = argIdx == 0
+ ? func->argumentRemoved(0)
+ : func->arguments().at(argIdx -1).isModifiedRemoved();
+ if (type.isVoid()
+ || !type.typeEntry()->isPrimitive()
+ || type.indirections() == 0
+ || (type.indirections() == 1 && type.typeUsagePattern() == AbstractMetaType::NativePointerAsArrayPattern)
+ || type.isCString()
+ || isRemoved
+ || modified
+ || func->hasConversionRule(TypeSystem::All, argIdx)
+ || func->hasInjectedCode())
+ return;
+ QString message;
+ QTextStream str(&message);
+ str << func->sourceLocation()
+ << "There's no user provided way (conversion rule, argument"
+ " removal, custom code, etc) to handle the primitive ";
+ if (argIdx == 0)
+ str << "return type '" << type.cppSignature() << '\'';
+ else
+ str << "type '" << type.cppSignature() << "' of argument " << argIdx;
+ str << " in function '";
+ if (func->ownerClass())
+ str << func->ownerClass()->qualifiedCppName() << "::";
+ str << func->signature() << "'.";
+ qCWarning(lcShiboken).noquote().nospace() << message;
+}
+
+static void checkTypeViability(const AbstractMetaFunctionCPtr &func)
+{
+ if (func->isUserAdded())
+ return;
+ checkTypeViability(func, func->type(), 0);
+ for (qsizetype i = 0; i < func->arguments().size(); ++i)
+ checkTypeViability(func, func->arguments().at(i).type(), int(i + 1));
+}
+
+void CppGenerator::writeTypeCheck(TextStream &s,
+ const std::shared_ptr<OverloadDataNode> &overloadData,
+ const QString &argumentName)
+{
+ QSet<TypeEntryCPtr> numericTypes;
+ const OverloadDataList &siblings = overloadData->parent()->children();
+ for (const auto &sibling : siblings) {
+ for (const auto &func : sibling->overloads()) {
+ checkTypeViability(func);
+ const AbstractMetaType &argType = sibling->overloadArgument(func)->type();
+ if (!argType.isPrimitive())
+ continue;
+ if (ShibokenGenerator::isNumber(argType.typeEntry()))
+ numericTypes << argType.typeEntry();
+ }
+ }
+
+ // This condition trusts that the OverloadData object will arrange for
+ // PyLong type to come after the more precise numeric types (e.g. float and bool)
+ AbstractMetaType argType = overloadData->modifiedArgType();
+ if (auto viewOn = argType.viewOn())
+ argType = *viewOn;
+ const bool numberType = numericTypes.size() == 1 || ShibokenGenerator::isPyInt(argType);
+ bool rejectNull =
+ shouldRejectNullPointerArgument(overloadData->referenceFunction(), overloadData->argPos());
+ writeTypeCheck(s, argType, argumentName, numberType, rejectNull);
+}
+
+qsizetype CppGenerator::writeArgumentConversion(TextStream &s,
+ const AbstractMetaType &argType,
+ const QString &argName,
+ const QString &pyArgName,
+ ErrorReturn errorReturn,
+ const AbstractMetaClassCPtr &context,
+ const QString &defaultValue,
+ bool castArgumentAsUnused) const
+{
+ qsizetype result = 0;
+ if (argType.typeEntry()->isCustom() || argType.typeEntry()->isVarargs())
+ return result;
+ if (argType.isWrapperType())
+ writeInvalidPyObjectCheck(s, pyArgName, errorReturn);
+ result = writePythonToCppTypeConversion(s, argType, pyArgName, argName, context, defaultValue);
+ if (castArgumentAsUnused)
+ s << sbkUnusedVariableCast(argName);
+ return result;
+}
+
+AbstractMetaType
+ CppGenerator::getArgumentType(const AbstractMetaFunctionCPtr &func, int index)
+{
+ if (index < 0 || index >= func->arguments().size()) {
+ qCWarning(lcShiboken).noquote().nospace()
+ << "Argument index for function '" << func->signature() << "' out of range.";
+ return {};
+ }
+
+ auto argType = func->arguments().at(index).modifiedType();
+ return argType.viewOn() ? *argType.viewOn() : argType;
+}
+
+static inline QString arrayHandleType(const AbstractMetaTypeList &nestedArrayTypes)
+{
+ switch (nestedArrayTypes.size()) {
+ case 1:
+ return "Shiboken::Conversions::ArrayHandle<"_L1
+ + nestedArrayTypes.constLast().minimalSignature() + u'>';
+ case 2:
+ return "Shiboken::Conversions::Array2Handle<"_L1
+ + nestedArrayTypes.constLast().minimalSignature()
+ + ", "_L1
+ + QString::number(nestedArrayTypes.constFirst().arrayElementCount())
+ + u'>';
+ }
+ return QString();
+}
+
+// Helper to write argument initialization code for a function argument
+// in case it has a default value.
+template <class Type> // AbstractMetaType/TypeEntry
+static void writeMinimalConstructorExpression(TextStream &s,
+ const ApiExtractorResult &api,
+ Type type,
+ bool isPrimitive,
+ const QString &defaultValue)
+{
+ if (defaultValue.isEmpty()) {
+ s << ShibokenGenerator::minimalConstructorExpression(api, type);
+ return;
+ }
+ // Use assignment to avoid "Most vexing parse" if it looks like
+ // a function call, or for primitives/pointers
+ const bool isDefault = defaultValue == u"{}";
+ if ((isPrimitive && !isDefault)
+ || defaultValue == u"nullptr" || defaultValue.contains(u'(')) {
+ s << " = " << defaultValue;
+ return;
+ }
+ if (isDefault) {
+ s << defaultValue;
+ return;
+ }
+ s << '(' << defaultValue << ')';
+}
+
+qsizetype CppGenerator::writePythonToCppTypeConversion(TextStream &s,
+ const AbstractMetaType &type,
+ const QString &pyIn,
+ const QString &cppOut,
+ const AbstractMetaClassCPtr &context,
+ const QString &defaultValue) const
+{
+ TypeEntryCPtr typeEntry = type.typeEntry();
+ if (typeEntry->isCustom() || typeEntry->isVarargs())
+ return 0;
+
+ const auto arg = GeneratorArgument::fromMetaType(type);
+ const bool isPrimitive = arg.type == GeneratorArgument::Type::Primitive;
+
+ QString cppOutAux = cppOut + u"_local"_s;
+
+ QString typeName = arg.type == GeneratorArgument::Type::CppPrimitiveArray
+ ? arrayHandleType(type.nestedArrayTypes())
+ : getFullTypeNameWithoutModifiers(type);
+
+ bool isProtectedEnum = false;
+ if (arg.type == GeneratorArgument::Type::Enum && avoidProtectedHack()) {
+ auto metaEnum = api().findAbstractMetaEnum(type.typeEntry());
+ if (metaEnum.has_value() && metaEnum->isProtected()) {
+ typeName = wrapperName(context) + u"::"_s
+ + metaEnum.value().name();
+ isProtectedEnum = true;
+ }
+ }
+
+ s << typeName;
+ switch (arg.conversion) {
+ case GeneratorArgument::Conversion::CppPrimitiveArray:
+ s << ' ' << cppOut;
+ break;
+ case GeneratorArgument::Conversion::ValueOrPointer: {
+ // Generate either value conversion for &cppOutAux or pointer
+ // conversion for &cppOut
+ s << ' ' << cppOutAux;
+ // No default value for containers which can also be passed by pointer.
+ if (arg.type != GeneratorArgument::Type::Container || type.indirections() == 0)
+ writeMinimalConstructorExpression(s, api(), type, isPrimitive, defaultValue);
+ s << ";\n" << typeName << " *" << cppOut << " = &" << cppOutAux;
+ }
+ break;
+ case GeneratorArgument::Conversion::Pointer: {
+ s << " *" << cppOut;
+ if (!defaultValue.isEmpty()) {
+ const bool needsConstCast = !isNullPtr(defaultValue)
+ && type.indirections() == 1 && type.isConstant()
+ && type.referenceType() == NoReference;
+ s << " = ";
+ if (needsConstCast)
+ s << "const_cast<" << typeName << " *>(";
+ s << defaultValue;
+ if (needsConstCast)
+ s << ')';
+ }
+ }
+ break;
+ case GeneratorArgument::Conversion::Default:
+ s << ' ' << cppOut;
+ if (isProtectedEnum && avoidProtectedHack()) {
+ s << " = ";
+ if (defaultValue.isEmpty())
+ s << "{}";
+ else
+ s << defaultValue;
+ } else if (type.isUserPrimitive()
+ || arg.type == GeneratorArgument::Type::Enum
+ || arg.type == GeneratorArgument::Type::Flags) {
+ writeMinimalConstructorExpression(s, api(), typeEntry, isPrimitive, defaultValue);
+ } else if ((!type.isContainer() || type.indirections() == 0) && !type.isSmartPointer()) {
+ writeMinimalConstructorExpression(s, api(), type, isPrimitive, defaultValue);
+ }
+ break;
+ }
+ s << ";\n";
+
+ QString pythonToCppFunc = pythonToCppConverterForArgumentName(pyIn);
+
+ QString pythonToCppCall = pythonToCppFunc + u'(' + pyIn + u", &"_s
+ + cppOut + u')';
+ if (arg.conversion != GeneratorArgument::Conversion::ValueOrPointer) {
+ // pythonToCppFunc may be 0 when less parameters are passed and
+ // the defaultValue takes effect.
+ if (!defaultValue.isEmpty())
+ s << "if (" << pythonToCppFunc << ")\n" << indent;
+ s << pythonToCppCall << ";\n";
+ if (!defaultValue.isEmpty())
+ s << outdent;
+ return arg.indirections;
+ }
+
+ // pythonToCppFunc may be 0 when less parameters are passed and
+ // the defaultValue takes effect.
+ if (!defaultValue.isEmpty())
+ s << "if (" << pythonToCppFunc << ") {\n" << indent;
+
+ s << "if (" << pythonToCppFunc << ".isValue())\n"
+ << indent << pythonToCppFunc << '(' << pyIn << ", &" << cppOutAux << ");\n"
+ << outdent << "else\n" << indent
+ << pythonToCppCall << ";\n" << outdent;
+
+ if (defaultValue.isEmpty())
+ s << '\n';
+ else
+ s << "}\n" << outdent;
+
+ return arg.indirections;
+}
+
+static void addConversionRuleCodeSnippet(CodeSnipList &snippetList, QString &rule,
+ TypeSystem::Language /* conversionLanguage */,
+ TypeSystem::Language snippetLanguage,
+ const QString &outputName = QString(),
+ const QString &inputName = QString())
+{
+ if (rule.isEmpty())
+ return;
+ if (snippetLanguage == TypeSystem::TargetLangCode) {
+ rule.replace(u"%in"_s, inputName);
+ rule.replace(u"%out"_s, outputName + u"_out"_s);
+ } else {
+ rule.replace(u"%out"_s, outputName);
+ }
+ CodeSnip snip(snippetLanguage);
+ snip.position = (snippetLanguage == TypeSystem::NativeCode) ? TypeSystem::CodeSnipPositionAny : TypeSystem::CodeSnipPositionBeginning;
+ snip.addCode(rule);
+ snippetList << snip;
+}
+
+void CppGenerator::writeConversionRule(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ TypeSystem::Language language, bool usesPyArgs) const
+{
+
+ CodeSnipList snippets;
+ const AbstractMetaArgumentList &arguments = func->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+ QString rule = func->conversionRule(language, arg.argumentIndex() + 1);
+ addConversionRuleCodeSnippet(snippets, rule, language, TypeSystem::TargetLangCode,
+ arg.name(), arg.name());
+ }
+ writeCodeSnips(s, snippets, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode,
+ func, usesPyArgs, nullptr);
+}
+
+void CppGenerator::writeConversionRule(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ TypeSystem::Language language, const QString &outputVar) const
+{
+ CodeSnipList snippets;
+ QString rule = func->conversionRule(language, 0);
+ addConversionRuleCodeSnippet(snippets, rule, language, language, outputVar);
+ writeCodeSnips(s, snippets, TypeSystem::CodeSnipPositionAny, language,
+ func, false /* uses PyArgs */, nullptr);
+}
+
+void CppGenerator::writeNoneReturn(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ bool thereIsReturnValue)
+{
+ if (thereIsReturnValue && (func->isVoid() || func->argumentRemoved(0))
+ && !func->injectedCodeHasReturnValueAttribution()) {
+ s << PYTHON_RETURN_VAR << " = Py_None;\n"
+ << "Py_INCREF(Py_None);\n";
+ }
+}
+
+void CppGenerator::writeOverloadedFunctionDecisor(TextStream &s,
+ const OverloadData &overloadData,
+ ErrorReturn errorReturn) const
+{
+ s << "// Overloaded function decisor\n";
+ const auto rfunc = overloadData.referenceFunction();
+ const AbstractMetaFunctionCList &functionOverloads = overloadData.overloads();
+ for (qsizetype i = 0; i < functionOverloads.size(); ++i) {
+ const auto func = functionOverloads.at(i);
+ s << "// " << i << ": ";
+ if (func->isStatic())
+ s << "static ";
+ if (const auto &decl = func->declaringClass())
+ s << decl->name() << "::";
+ s << func->signatureComment() << '\n';
+ }
+ writeOverloadedFunctionDecisorEngine(s, overloadData, &overloadData);
+ s << '\n';
+
+ // Ensure that the direct overload that called this reverse
+ // is called.
+ if (rfunc->isOperatorOverload() && !rfunc->isCallOperator()) {
+ s << "if (isReverse && overloadId == -1) {\n" << indent
+ << "Shiboken::Errors::setReverseOperatorNotImplemented();\n"
+ << "return {};\n" << outdent
+ << "}\n\n";
+ }
+
+ s << "// Function signature not found.\n"
+ << "if (overloadId == -1)\n" << indent
+ << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n\n"
+ << outdent;
+}
+
+void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s,
+ const OverloadData &overloadData,
+ const OverloadDataRootNode *node) const
+{
+ bool hasDefaultCall = node->nextArgumentHasDefaultValue();
+ auto referenceFunction = node->referenceFunction();
+
+ // If the next argument has not an argument with a default value, it is still possible
+ // that one of the overloads for the current overload data has its final occurrence here.
+ // If found, the final occurrence of a method is attributed to the referenceFunction
+ // variable to be used further on this method on the conditional that identifies default
+ // method calls.
+ if (!hasDefaultCall) {
+ for (const auto &func : node->overloads()) {
+ if (node->isFinalOccurrence(func)) {
+ referenceFunction = func;
+ hasDefaultCall = true;
+ break;
+ }
+ }
+ }
+
+ const int maxArgs = overloadData.maxArgs();
+ // Python constructors always receive multiple arguments.
+ const bool usePyArgs = overloadData.pythonFunctionWrapperUsesListOfArguments();
+
+ // Functions without arguments are identified right away.
+ if (maxArgs == 0) {
+ s << "overloadId = " << overloadData.functionNumber(referenceFunction)
+ << "; // " << referenceFunction->minimalSignature() << '\n';
+ return;
+
+ }
+ // To decide if a method call is possible at this point the current overload
+ // data object cannot be the head, since it is just an entry point, or a root,
+ // for the tree of arguments and it does not represent a valid method call.
+ if (!node->isRoot()) {
+ const bool isLastArgument = node->children().isEmpty();
+ const bool signatureFound = node->overloads().size() == 1;
+
+ // The current overload data describes the last argument of a signature,
+ // so the method can be identified right now.
+ if (isLastArgument || (signatureFound && !hasDefaultCall)) {
+ const auto func = node->referenceFunction();
+ s << "overloadId = " << overloadData.functionNumber(func)
+ << "; // " << func->minimalSignature() << '\n';
+ return;
+ }
+ }
+
+ bool isFirst = true;
+
+ // If the next argument has a default value the decisor can perform a method call;
+ // it just need to check if the number of arguments received from Python are equal
+ // to the number of parameters preceding the argument with the default value.
+ const OverloadDataList &children = node->children();
+ if (hasDefaultCall) {
+ isFirst = false;
+ int numArgs = node->argPos() + 1;
+ s << "if (numArgs == " << numArgs << ") {\n" << indent;
+ auto func = referenceFunction;
+ for (const auto &child : children) {
+ const auto defValFunc = child->getFunctionWithDefaultValue();
+ if (defValFunc) {
+ func = defValFunc;
+ break;
+ }
+ }
+ s << "overloadId = " << overloadData.functionNumber(func)
+ << "; // " << func->minimalSignature() << '\n' << outdent << '}';
+ }
+
+ for (auto child : children) {
+ bool signatureFound = child->overloads().size() == 1
+ && !child->getFunctionWithDefaultValue()
+ && !child->findNextArgWithDefault();
+
+ const auto refFunc = child->referenceFunction();
+
+ QStringList typeChecks;
+
+ QString pyArgName = (usePyArgs && maxArgs > 1)
+ ? pythonArgsAt(child->argPos())
+ : PYTHON_ARG;
+ auto od = child;
+ int startArg = od->argPos();
+ int sequenceArgCount = 0;
+ while (od && !od->argType().isVarargs()) {
+ const bool typeReplacedByPyObject = od->isTypeModified()
+ && od->modifiedArgType().name() == cPyObjectT;
+ if (!typeReplacedByPyObject) {
+ if (usePyArgs)
+ pyArgName = pythonArgsAt(od->argPos());
+ StringStream tck(TextStream::Language::Cpp);
+ auto func = od->referenceFunction();
+
+ if (func->isConstructor() && func->arguments().size() == 1) {
+ AbstractMetaClassCPtr ownerClass = func->ownerClass();
+ ComplexTypeEntryCPtr baseContainerType = ownerClass->typeEntry()->baseContainerType();
+ if (baseContainerType && baseContainerType == func->arguments().constFirst().type().typeEntry()
+ && ownerClass->isCopyable()) {
+ tck << '!' << cpythonCheckFunction(ownerClass->typeEntry())
+ << pyArgName << ")\n" << indent << "&& " << outdent;
+ }
+ }
+ writeTypeCheck(tck, od, pyArgName);
+ typeChecks << tck.toString();
+ }
+
+ sequenceArgCount++;
+
+ if (od->children().isEmpty()
+ || od->nextArgumentHasDefaultValue()
+ || od->children().size() != 1
+ || od->overloads().size() != od->children().constFirst()->overloads().size()) {
+ child = od;
+ od = nullptr;
+ } else {
+ od = od->children().constFirst();
+ }
+ }
+
+ if (usePyArgs && signatureFound) {
+ AbstractMetaArgumentList args = refFunc->arguments();
+ const bool isVarargs = args.size() > 1 && args.constLast().type().isVarargs();
+ int numArgs = args.size() - OverloadData::numberOfRemovedArguments(refFunc);
+ if (isVarargs)
+ --numArgs;
+ QString check = (isVarargs ? u"numArgs >= "_s : u"numArgs == "_s)
+ + QString::number(numArgs);
+ typeChecks.prepend(check);
+ } else if (usePyArgs && sequenceArgCount > 0) {
+ typeChecks.prepend(u"numArgs >= "_s + QString::number(startArg + sequenceArgCount));
+ } else if (refFunc->isOperatorOverload() && !refFunc->isCallOperator()) {
+ QString check;
+ if (!refFunc->isReverseOperator())
+ check.append(u'!');
+ check.append(u"isReverse"_s);
+ typeChecks.prepend(check);
+ }
+
+ if (isFirst) {
+ isFirst = false;
+ } else {
+ s << " else ";
+ }
+ s << "if (";
+ if (typeChecks.isEmpty()) {
+ s << "true";
+ } else {
+ s << indent << typeChecks.join(u"\n&& "_s) << outdent;
+ }
+ s << ") {\n" << indent;
+ writeOverloadedFunctionDecisorEngine(s, overloadData, child.get());
+ s << outdent << '}';
+ }
+ s << '\n';
+}
+
+void CppGenerator::writeFunctionCalls(TextStream &s, const OverloadData &overloadData,
+ const GeneratorContext &context,
+ ErrorReturn errorReturn) const
+{
+ const AbstractMetaFunctionCList &overloads = overloadData.overloads();
+ s << "// Call function/method\n"
+ << (overloads.size() > 1 ? "switch (overloadId) " : "") << "{\n" << indent;
+ if (overloads.size() == 1) {
+ writeSingleFunctionCall(s, overloadData, overloads.constFirst(), context,
+ errorReturn);
+ } else {
+ for (qsizetype i = 0; i < overloads.size(); ++i) {
+ const auto func = overloads.at(i);
+ s << "case " << i << ": // " << func->signature() << "\n{\n" << indent;
+ writeSingleFunctionCall(s, overloadData, func, context, errorReturn);
+ s << "break;\n" << outdent << "}\n";
+ }
+ }
+ s << outdent << "}\n";
+}
+
+static void writeDeprecationWarning(TextStream &s,
+ const GeneratorContext &context,
+ const AbstractMetaFunctionCPtr &func,
+ CppGenerator::ErrorReturn errorReturn)
+{
+ s << "Shiboken::Warnings::warnDeprecated(\"";
+ if (const auto cls = context.metaClass())
+ s << cls->name() << "\", ";
+ // Check error in case "warning-as-error" is set.
+ s << '"' << func->signature().replace(u"::"_s, u"."_s) << "\");\n"
+ << "if (" << shibokenErrorsOccurred << ")\n"
+ << indent << errorReturn << outdent;
+}
+
+void CppGenerator::writeSingleFunctionCall(TextStream &s,
+ const OverloadData &overloadData,
+ const AbstractMetaFunctionCPtr &func,
+ const GeneratorContext &context,
+ ErrorReturn errorReturn) const
+{
+ if (func->isDeprecated())
+ writeDeprecationWarning(s, context, func, errorReturn);
+
+ if (func->functionType() == AbstractMetaFunction::EmptyFunction) {
+ s << "Shiboken::Errors::setPrivateMethod(\""
+ << func->signature().replace(u"::"_s, u"."_s) << "\");\n"
+ << errorReturn;
+ return;
+ }
+
+ const bool usePyArgs = overloadData.pythonFunctionWrapperUsesListOfArguments();
+
+ // Handle named arguments.
+ writeNamedArgumentResolution(s, func, usePyArgs, overloadData, errorReturn);
+
+ bool injectCodeCallsFunc = injectedCodeCallsCppFunction(context, func);
+ bool mayHaveUnunsedArguments = !func->isUserAdded() && func->hasInjectedCode() && injectCodeCallsFunc;
+ int removedArgs = 0;
+
+ const auto argCount = func->arguments().size();
+ QList<qsizetype> indirections(argCount, 0);
+ for (qsizetype argIdx = 0; argIdx < argCount; ++argIdx) {
+ const bool hasConversionRule =
+ func->hasConversionRule(TypeSystem::NativeCode, int(argIdx + 1));
+ const AbstractMetaArgument &arg = func->arguments().at(argIdx);
+ if (arg.isModifiedRemoved()) {
+ if (!arg.defaultValueExpression().isEmpty()) {
+ const QString cppArgRemoved = CPP_ARG_REMOVED(argIdx);
+ s << getFullTypeName(arg.type()) << ' ' << cppArgRemoved;
+ s << " = " << arg.defaultValueExpression() << ";\n"
+ << sbkUnusedVariableCast(cppArgRemoved);
+ } else if (!injectCodeCallsFunc && !func->isUserAdded() && !hasConversionRule) {
+ // When an argument is removed from a method signature and no other means of calling
+ // the method are provided (as with code injection) the generator must abort.
+ QString m;
+ QTextStream(&m) << "No way to call '" << func->ownerClass()->name()
+ << "::" << func->signature()
+ << "' with the modifications described in the type system.";
+ throw Exception(m);
+ }
+ removedArgs++;
+ continue;
+ }
+ if (hasConversionRule)
+ continue;
+ if (mayHaveUnunsedArguments && !func->injectedCodeUsesArgument(argIdx))
+ continue;
+ auto argType = getArgumentType(func, argIdx);
+ int argPos = argIdx - removedArgs;
+ QString pyArgName = usePyArgs ? pythonArgsAt(argPos) : PYTHON_ARG;
+ indirections[argIdx] =
+ writeArgumentConversion(s, argType, CPP_ARG_N(argPos), pyArgName, errorReturn,
+ func->implementingClass(), arg.defaultValueExpression(),
+ func->isUserAdded());
+ }
+
+ s << '\n';
+
+ int numRemovedArgs = OverloadData::numberOfRemovedArguments(func);
+
+ s << "if (Shiboken::Errors::occurred() == nullptr) {\n" << indent;
+ writeMethodCall(s, func, context,
+ overloadData.pythonFunctionWrapperUsesListOfArguments(),
+ func->arguments().size() - numRemovedArgs, indirections, errorReturn);
+
+ if (!func->isConstructor())
+ writeNoneReturn(s, func, overloadData.hasNonVoidReturnType());
+ s << outdent << "}\n";
+}
+
+QString CppGenerator::cppToPythonFunctionName(const QString &sourceTypeName, QString targetTypeName)
+{
+ if (targetTypeName.isEmpty())
+ targetTypeName = sourceTypeName;
+ return sourceTypeName + u"_CppToPython_"_s + targetTypeName;
+}
+
+QString CppGenerator::pythonToCppFunctionName(const QString &sourceTypeName, const QString &targetTypeName)
+{
+ return sourceTypeName + u"_PythonToCpp_"_s + targetTypeName;
+}
+QString CppGenerator::pythonToCppFunctionName(const AbstractMetaType &sourceType, const AbstractMetaType &targetType)
+{
+ return pythonToCppFunctionName(fixedCppTypeName(sourceType), fixedCppTypeName(targetType));
+}
+QString CppGenerator::pythonToCppFunctionName(const TargetToNativeConversion &toNative,
+ const TypeEntryCPtr &targetType)
+{
+ return pythonToCppFunctionName(fixedCppTypeName(toNative), fixedCppTypeName(targetType));
+}
+
+QString CppGenerator::convertibleToCppFunctionName(const QString &sourceTypeName, const QString &targetTypeName)
+{
+ return u"is_"_s + sourceTypeName + u"_PythonToCpp_"_s
+ + targetTypeName + u"_Convertible"_s;
+}
+QString CppGenerator::convertibleToCppFunctionName(const AbstractMetaType &sourceType, const AbstractMetaType &targetType)
+{
+ return convertibleToCppFunctionName(fixedCppTypeName(sourceType), fixedCppTypeName(targetType));
+}
+QString CppGenerator::convertibleToCppFunctionName(const TargetToNativeConversion &toNative,
+ const TypeEntryCPtr &targetType)
+{
+ return convertibleToCppFunctionName(fixedCppTypeName(toNative), fixedCppTypeName(targetType));
+}
+
+void CppGenerator::writeCppToPythonFunction(TextStream &s, const QString &code, const QString &sourceTypeName,
+ QString targetTypeName) const
+{
+
+ QString prettyCode = code;
+ const QString funcName = cppToPythonFunctionName(sourceTypeName, targetTypeName);
+ processCodeSnip(prettyCode, funcName);
+
+ s << "static PyObject *" << funcName
+ << "(const void *cppIn)\n{\n" << indent << prettyCode
+ << ensureEndl << outdent << "}\n";
+}
+
+static QString writeCppInRef(const QString &typeName, bool constRef)
+{
+ QString result;
+ QTextStream str(&result);
+ if (constRef)
+ str << "const ";
+ str << "auto &cppInRef = *reinterpret_cast<";
+ if (constRef)
+ str << "const ";
+ str << typeName << " *>("
+ << (constRef ? "cppIn" : "const_cast<void *>(cppIn)") << ");";
+ return result;
+}
+
+static void replaceCppToPythonVariables(QString &code, const QString &typeName,
+ bool constRef = false)
+{
+ CodeSnipAbstract::prependCode(&code, writeCppInRef(typeName, constRef));
+ code.replace(u"%INTYPE"_s, typeName);
+ code.replace(u"%OUTTYPE"_s, u"PyObject *"_s);
+ code.replace(u"%in"_s, u"cppInRef"_s);
+ code.replace(u"%out"_s, u"pyOut"_s);
+}
+
+void CppGenerator::writeCppToPythonFunction(TextStream &s,
+ const CustomConversionPtr &customConversion) const
+{
+ QString code = customConversion->nativeToTargetConversion();
+ auto ownerType = customConversion->ownerType();
+ const bool constRef = !ownerType->isPrimitive(); // PyCapsule needs a non-const ref
+ replaceCppToPythonVariables(code, getFullTypeName(ownerType), constRef);
+ writeCppToPythonFunction(s, code, fixedCppTypeName(customConversion->ownerType()));
+}
+
+QString CppGenerator::containerNativeToTargetTypeName(const ContainerTypeEntryCPtr &type)
+{
+ QString result = type->targetLangApiName();
+ if (result != cPyObjectT) {
+ result = containerCpythonBaseName(type);
+ if (result == cPySequenceT)
+ result = cPyListT;
+ }
+ return result;
+}
+
+void CppGenerator::writeCppToPythonFunction(TextStream &s, const AbstractMetaType &containerType) const
+{
+ Q_ASSERT(containerType.typeEntry()->isContainer());
+ auto cte = std::static_pointer_cast<const ContainerTypeEntry>(containerType.typeEntry());
+ if (!cte->hasCustomConversion()) {
+ QString m;
+ QTextStream(&m) << "Can't write the C++ to Python conversion function for container type '"
+ << containerType.typeEntry()->qualifiedCppName()
+ << "' - no conversion rule was defined for it in the type system.";
+ throw Exception(m);
+ }
+ const auto customConversion = cte->customConversion();
+ QString code = customConversion->nativeToTargetConversion();
+ for (qsizetype i = 0; i < containerType.instantiations().size(); ++i) {
+ const AbstractMetaType &type = containerType.instantiations().at(i);
+ QString typeName = getFullTypeName(type);
+ if (type.isConstant())
+ typeName = u"const "_s + typeName;
+ code.replace(u"%INTYPE_"_s + QString::number(i), typeName);
+ }
+ replaceCppToPythonVariables(code, getFullTypeNameWithoutModifiers(containerType), true);
+ processCodeSnip(code, containerType.typeEntry()->qualifiedCppName());
+ writeCppToPythonFunction(s, code, fixedCppTypeName(containerType),
+ containerNativeToTargetTypeName(cte));
+}
+
+void CppGenerator::writePythonToCppFunction(TextStream &s, const QString &code, const QString &sourceTypeName,
+ const QString &targetTypeName) const
+{
+ QString prettyCode = code;
+ const QString funcName = pythonToCppFunctionName(sourceTypeName, targetTypeName);
+ processCodeSnip(prettyCode, funcName);
+ s << "static void " << funcName
+ << "(PyObject *pyIn, void *cppOut)\n{\n" << indent << prettyCode
+ << ensureEndl << outdent << "}\n";
+}
+
+void CppGenerator::writeIsPythonConvertibleToCppFunction(TextStream &s,
+ const QString &sourceTypeName,
+ const QString &targetTypeName,
+ const QString &condition,
+ QString pythonToCppFuncName,
+ bool acceptNoneAsCppNull)
+{
+ if (pythonToCppFuncName.isEmpty())
+ pythonToCppFuncName = pythonToCppFunctionName(sourceTypeName, targetTypeName);
+
+ s << "static PythonToCppFunc " << convertibleToCppFunctionName(sourceTypeName, targetTypeName);
+ s << "(PyObject *pyIn)\n{\n" << indent;
+ if (acceptNoneAsCppNull) {
+ s << "if (pyIn == Py_None)\n" << indent
+ << "return Shiboken::Conversions::nonePythonToCppNullPtr;\n" << outdent;
+ } else {
+ if (!condition.contains(u"pyIn"))
+ s << sbkUnusedVariableCast("pyIn");
+ }
+ s << "if (" << condition << ")\n" << indent
+ << "return " << pythonToCppFuncName << ";\n" << outdent
+ << "return {};\n" << outdent << "}\n";
+}
+
+void CppGenerator::writePythonToCppConversionFunctions(TextStream &s,
+ const AbstractMetaType &sourceType,
+ const AbstractMetaType &targetType,
+ QString typeCheck,
+ QString conversion,
+ const QString &preConversion) const
+{
+ QString sourcePyType = cpythonTypeNameExt(sourceType);
+
+ // Python to C++ conversion function.
+ StringStream c(TextStream::Language::Cpp);
+ if (conversion.isEmpty())
+ conversion = u'*' + cpythonWrapperCPtr(sourceType, u"pyIn"_s);
+ if (!preConversion.isEmpty())
+ c << preConversion << '\n';
+ const QString fullTypeName = targetType.isSmartPointer()
+ ? targetType.cppSignature()
+ : getFullTypeName(targetType.typeEntry());
+ c << "*reinterpret_cast<" << fullTypeName << " *>(cppOut) = "
+ << fullTypeName << '('
+ << (sourceType.isUniquePointer() ? stdMove(conversion) : conversion)
+ << ");";
+ QString sourceTypeName = fixedCppTypeName(sourceType);
+ QString targetTypeName = fixedCppTypeName(targetType);
+ writePythonToCppFunction(s, c.toString(), sourceTypeName, targetTypeName);
+
+ // Python to C++ convertible check function.
+ if (typeCheck.isEmpty())
+ typeCheck = u"PyObject_TypeCheck(pyIn, "_s + sourcePyType + u')';
+ writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, typeCheck);
+ s << '\n';
+}
+
+void CppGenerator::writePythonToCppConversionFunctions(TextStream &s,
+ const TargetToNativeConversion &toNative,
+ const TypeEntryCPtr &targetType) const
+{
+ // Python to C++ conversion function.
+ QString code = toNative.conversion();
+ QString inType;
+ if (toNative.sourceType())
+ inType = cpythonTypeNameExt(toNative.sourceType());
+ else
+ inType = u'(' + toNative.sourceTypeName() + u"_TypeF())"_s;
+ code.replace(u"%INTYPE"_s, inType);
+ code.replace(u"%OUTTYPE"_s, targetType->qualifiedCppName());
+ code.replace(u"%in"_s, u"pyIn"_s);
+ code.replace(u"%out"_s,
+ u"*reinterpret_cast<"_s + getFullTypeName(targetType) + u" *>(cppOut)"_s);
+
+ QString sourceTypeName = fixedCppTypeName(toNative);
+ QString targetTypeName = fixedCppTypeName(targetType);
+ writePythonToCppFunction(s, code, sourceTypeName, targetTypeName);
+
+ // Python to C++ convertible check function.
+ QString typeCheck = toNative.sourceTypeCheck();
+ if (typeCheck.isEmpty()) {
+ QString pyTypeName = toNative.sourceTypeName();
+ if (pyTypeName == u"Py_None" || pyTypeName == u"PyNone")
+ typeCheck = u"%in == Py_None"_s;
+ else if (pyTypeName == u"SbkObject")
+ typeCheck = u"Shiboken::Object::checkType(%in)"_s;
+ }
+ if (typeCheck.isEmpty()) {
+ if (!toNative.sourceType() || toNative.sourceType()->isPrimitive()) {
+ QString m;
+ QTextStream(&m) << "User added implicit conversion for C++ type '" << targetType->qualifiedCppName()
+ << "' must provide either an input type check function or a non primitive type entry.";
+ throw Exception(m);
+ }
+ typeCheck = u"PyObject_TypeCheck(%in, "_s
+ + cpythonTypeNameExt(toNative.sourceType()) + u')';
+ }
+ typeCheck.replace(u"%in"_s, u"pyIn"_s);
+ processCodeSnip(typeCheck, targetType->qualifiedCppName());
+ writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, typeCheck);
+}
+
+void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, const AbstractMetaType &containerType) const
+{
+ Q_ASSERT(containerType.typeEntry()->isContainer());
+ const auto cte = std::static_pointer_cast<const ContainerTypeEntry>(containerType.typeEntry());
+ const auto customConversion = cte->customConversion();
+ for (const auto &conv : customConversion->targetToNativeConversions())
+ writePythonToCppConversionFunction(s, containerType, conv);
+}
+
+void CppGenerator::writePythonToCppConversionFunction(TextStream &s,
+ const AbstractMetaType &containerType,
+ const TargetToNativeConversion &conv) const
+{
+ // Python to C++ conversion function.
+ QString cppTypeName = getFullTypeNameWithoutModifiers(containerType);
+ QString code = conv.conversion();
+ const QString line = u"auto &cppOutRef = *reinterpret_cast<"_s
+ + cppTypeName + u" *>(cppOut);"_s;
+ CodeSnipAbstract::prependCode(&code, line);
+ for (qsizetype i = 0; i < containerType.instantiations().size(); ++i) {
+ const AbstractMetaType &type = containerType.instantiations().at(i);
+ QString typeName = getFullTypeName(type);
+ // Containers of opaque containers are not handled here.
+ const auto generatorArg = GeneratorArgument::fromMetaType(type);
+ if (generatorArg.indirections > 0 && !type.generateOpaqueContainer()) {
+ for (int pos = 0; ; ) {
+ const QRegularExpressionMatch match = convertToCppRegEx().match(code, pos);
+ if (!match.hasMatch())
+ break;
+ pos = match.capturedEnd();
+ const QString varName = match.captured(1);
+ QString rightCode = code.mid(pos);
+ rightCode.replace(varName, u'*' + varName);
+ code.replace(pos, code.size() - pos, rightCode);
+ }
+ typeName.append(u" *"_s);
+ }
+ code.replace(u"%OUTTYPE_"_s + QString::number(i), typeName);
+ }
+ code.replace(u"%OUTTYPE"_s, cppTypeName);
+ code.replace(u"%in"_s, u"pyIn"_s);
+ code.replace(u"%out"_s, u"cppOutRef"_s);
+ QString typeName = fixedCppTypeName(containerType);
+ const QString &sourceTypeName = conv.sourceTypeName();
+ writePythonToCppFunction(s, code, sourceTypeName, typeName);
+
+ // Python to C++ convertible check function.
+ QString typeCheck = cpythonCheckFunction(containerType);
+ if (typeCheck.isEmpty())
+ typeCheck = u"false"_s;
+ else
+ typeCheck = typeCheck + u"pyIn)"_s;
+ writeIsPythonConvertibleToCppFunction(s, sourceTypeName, typeName, typeCheck);
+ s << '\n';
+}
+
+static void writeSetConverterFunction(TextStream &s,
+ const char *function,
+ const QString &converterVar,
+ const QString &pythonToCppFunc,
+ const QString &isConvertibleFunc)
+{
+ s << "Shiboken::Conversions::" << function << '(' << converterVar << ',' << '\n'
+ << indent << pythonToCppFunc << ',' << '\n' << isConvertibleFunc
+ << outdent << ");\n";
+}
+
+void CppGenerator::writeAddPythonToCppConversion(TextStream &s, const QString &converterVar,
+ const QString &pythonToCppFunc,
+ const QString &isConvertibleFunc)
+{
+ writeSetConverterFunction(s, "addPythonToCppValueConversion",
+ converterVar, pythonToCppFunc, isConvertibleFunc);
+}
+
+void CppGenerator::writeSetPythonToCppPointerConversion(TextStream &s,
+ const QString &converterVar,
+ const QString &pythonToCppFunc,
+ const QString &isConvertibleFunc)
+{
+ writeSetConverterFunction(s, "setPythonToCppPointerFunctions",
+ converterVar, pythonToCppFunc, isConvertibleFunc);
+}
+
+// PYSIDE-1986: Some QObject derived classes, (QVBoxLayout) do not have default
+// arguments, which breaks setting properties by named arguments. Force the
+// handling code to be generated nevertheless for applicable widget classes,
+// so that the mechanism of falling through to the error handling to set
+// the properties works nevertheless.
+static bool forceQObjectNamedArguments(const AbstractMetaFunctionCPtr &func)
+{
+ if (func->functionType() != AbstractMetaFunction::ConstructorFunction)
+ return false;
+ const auto owner = func->ownerClass();
+ Q_ASSERT(owner);
+ if (!isQObject(owner))
+ return false;
+ const QString &name = owner->name();
+ return name == u"QVBoxLayout" || name == u"QHBoxLayout"
+ || name == u"QSplitterHandle" || name == u"QSizeGrip";
+}
+
+void CppGenerator::writeNamedArgumentResolution(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ bool usePyArgs,
+ const OverloadData &overloadData,
+ ErrorReturn errorReturn)
+{
+ const AbstractMetaArgumentList &args = OverloadData::getArgumentsWithDefaultValues(func);
+ const bool hasDefaultArguments = !args.isEmpty();
+ const bool force = !hasDefaultArguments && usePySideExtensions()
+ && forceQObjectNamedArguments(func);
+ if (!hasDefaultArguments && !force) {
+ if (overloadData.hasArgumentWithDefaultValue()) {
+ // PySide-535: Allow for empty dict instead of nullptr in PyPy
+ s << "if (kwds != nullptr && PyDict_Size(kwds) > 0) {\n" << indent
+ << "errInfo.reset(kwds);\n"
+ << "Py_INCREF(errInfo.object());\n"
+ << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"
+ << outdent << "}\n";
+ }
+ return;
+ }
+
+ // PySide-535: Allow for empty dict instead of nullptr in PyPy
+ s << "if (kwds && PyDict_Size(kwds) > 0) {\n" << indent;
+ if (!force)
+ s << "PyObject *value{};\n";
+ s << "Shiboken::AutoDecRef kwds_dup(PyDict_Copy(kwds));\n";
+ for (const AbstractMetaArgument &arg : args) {
+ const int pyArgIndex = arg.argumentIndex()
+ - OverloadData::numberOfRemovedArguments(func, arg.argumentIndex());
+ QString pyArgName = usePyArgs ? pythonArgsAt(pyArgIndex)
+ : PYTHON_ARG;
+ QString pyKeyName = u"key_"_s + arg.name();
+ s << "static PyObject *const " << pyKeyName
+ << " = Shiboken::String::createStaticString(\"" << arg.name() << "\");\n"
+ << "if (PyDict_Contains(kwds, " << pyKeyName << ") != 0) {\n" << indent
+ << "value = PyDict_GetItem(kwds, " << pyKeyName << ");\n"
+ << "if (value != nullptr && " << pyArgName << " != nullptr ) {\n"
+ << indent << "errInfo.reset(" << pyKeyName << ");\n"
+ << "Py_INCREF(errInfo.object());\n"
+ << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"
+ << outdent << "}\nif (value != nullptr) {\n" << indent
+ << pyArgName << " = value;\nif (!";
+ const auto &type = arg.modifiedType();
+ writeTypeCheck(s, type, pyArgName, isNumber(type.typeEntry()), {});
+ s << ")\n" << indent
+ << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"
+ << outdent << outdent
+ << "}\nPyDict_DelItem(kwds_dup, " << pyKeyName << ");\n"
+ << outdent << "}\n";
+ }
+ // PYSIDE-1305: Handle keyword args correctly.
+ // Normal functions handle their parameters immediately.
+ // For constructors that are QObject, we need to delay that
+ // until extra keyword signals and properties are handled.
+ s << "if (PyDict_Size(kwds_dup) > 0) {\n" << indent
+ << "errInfo.reset(kwds_dup.release());\n";
+ if (!(func->isConstructor() && isQObject(func->ownerClass())))
+ s << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n";
+ else
+ s << "// fall through to handle extra keyword signals and properties\n";
+ s << outdent << "}\n"
+ << outdent << "}\n";
+}
+
+QString CppGenerator::argumentNameFromIndex(const ApiExtractorResult &api,
+ const AbstractMetaFunctionCPtr &func, int argIndex)
+{
+ switch (argIndex) {
+ case -1:
+ return PYTHON_SELF_VAR;
+ case 0:
+ return PYTHON_RETURN_VAR;
+ case 1: { // Single argument?
+ OverloadData data(getFunctionGroups(func->implementingClass()).value(func->name()), api);
+ if (!data.pythonFunctionWrapperUsesListOfArguments())
+ return PYTHON_ARG;
+ break;
+ }
+ }
+ return pythonArgsAt(argIndex - 1);
+}
+
+AbstractMetaClassCPtr
+CppGenerator::argumentClassFromIndex(const ApiExtractorResult &api,
+ const AbstractMetaFunctionCPtr &func, int argIndex)
+{
+ if (argIndex == -1)
+ return func->implementingClass();
+
+ AbstractMetaType type;
+ if (argIndex == 0) {
+ type = func->type();
+ } else {
+ const int arg = argIndex - 1;
+ const int realIndex = arg - OverloadData::numberOfRemovedArguments(func, arg);
+ type = func->arguments().at(realIndex).type();
+ }
+
+ if (type.typeEntry()->isContainer()) {
+ // only support containers with 1 type
+ if (type.instantiations().size() == 1)
+ type = type.instantiations().constFirst();
+ }
+
+ auto te = type.typeEntry();
+ if (type.isVoid() || !te->isComplex())
+ throw Exception(msgInvalidArgumentModification(func, argIndex));
+ const auto result = AbstractMetaClass::findClass(api.classes(), te);
+ if (!result)
+ throw Exception(msgClassNotFound(te));
+ return result;
+}
+
+const char tryBlock[] = R"(
+PyObject *errorType{};
+PyObject *errorString{};
+try {
+)";
+
+const char defaultExceptionHandling[] = R"(} catch (const std::exception &e) {
+ errorType = PyExc_RuntimeError;
+ errorString = Shiboken::String::fromCString(e.what());
+} catch (...) {
+ errorType = PyExc_RuntimeError;
+ errorString = Shiboken::Messages::unknownException();
+}
+)";
+
+const char propagateException[] = R"(
+if (errorType != nullptr)
+ PyErr_SetObject(errorType, errorString);
+)";
+
+static QString explicitConversion(const QString &v, const AbstractMetaType &t)
+{
+ return t.plainType().cppSignature() + u'(' + v + u')';
+}
+
+void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ const GeneratorContext &context, bool usesPyArgs,
+ int maxArgs,
+ const QList<qsizetype> &argumentIndirections,
+ ErrorReturn errorReturn) const
+{
+ s << "// " << func->minimalSignature() << (func->isReverseOperator() ? " [reverse operator]": "") << '\n';
+ if (func->isConstructor()) {
+ const CodeSnipList &snips = func->injectedCodeSnips();
+ for (const CodeSnip &cs : snips) {
+ if (cs.position == TypeSystem::CodeSnipPositionEnd) {
+ auto klass = func->ownerClass();
+ s << "overloadId = "
+ << klass->functions().indexOf(func)
+ << ";\n";
+ break;
+ }
+ }
+ }
+
+ if (func->isAbstract()) {
+ s << "if (Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(self))) {\n"
+ << indent << "Shiboken::Errors::setPureVirtualMethodError(\""
+ << func->ownerClass()->name() << '.' << func->name() << "\");\n"
+ << errorReturn << outdent << "}\n";
+ }
+
+ // Used to provide contextual information to custom code writer function.
+ const AbstractMetaArgument *lastArg = nullptr;
+
+ CodeSnipList snips;
+ if (func->hasInjectedCode()) {
+ snips = func->injectedCodeSnips();
+
+ // Find the last argument available in the method call to provide
+ // the injected code writer with information to avoid invalid replacements
+ // on the %# variable.
+ if (maxArgs > 0 && maxArgs < func->arguments().size() - OverloadData::numberOfRemovedArguments(func)) {
+ int removedArgs = 0;
+ for (int i = 0; i < maxArgs + removedArgs; i++) {
+ if (func->arguments().at(i).isModifiedRemoved())
+ removedArgs++;
+ }
+ } else if (maxArgs != 0 && !func->arguments().isEmpty()) {
+ lastArg = &func->arguments().constLast();
+ }
+
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode, func,
+ usesPyArgs, lastArg);
+ }
+
+ writeConversionRule(s, func, TypeSystem::NativeCode, usesPyArgs);
+
+ bool generateExceptionHandling = false;
+
+ if (!func->isUserAdded()) {
+ QStringList userArgs;
+ if (func->functionType() != AbstractMetaFunction::CopyConstructorFunction) {
+ int removedArgs = 0;
+ for (int i = 0; i < maxArgs + removedArgs; i++) {
+ const AbstractMetaArgument &arg = func->arguments().at(i);
+ const bool hasConversionRule =
+ func->hasConversionRule(TypeSystem::NativeCode, arg.argumentIndex() + 1);
+ if (arg.isModifiedRemoved()) {
+ // If some argument with default value is removed from a
+ // method signature, the said value must be explicitly
+ // added to the method call.
+ removedArgs++;
+
+ // If have conversion rules I will use this for removed args
+ if (hasConversionRule)
+ userArgs << arg.name() + CONV_RULE_OUT_VAR_SUFFIX;
+ else if (!arg.defaultValueExpression().isEmpty())
+ userArgs.append(CPP_ARG_REMOVED(i));
+ } else {
+ if (hasConversionRule) {
+ userArgs.append(arg.name() + CONV_RULE_OUT_VAR_SUFFIX);
+ } else {
+ const int idx = arg.argumentIndex() - removedArgs;
+ const auto deRef = argumentIndirections.at(i);
+ QString argName = AbstractMetaType::dereferencePrefix(deRef)
+ + CPP_ARG_N(idx);
+ userArgs.append(argName);
+ }
+ }
+ // "Pass unique ptr by value" pattern: Apply std::move()
+ auto type = arg.type();
+ if (type.useStdMove())
+ userArgs.last() = stdMove(userArgs.constLast());
+ else if (type.viewOn() != nullptr)
+ userArgs.last() = explicitConversion(userArgs.constLast(), type);
+ }
+
+ // If any argument's default value was modified the method must be called
+ // with this new value whenever the user doesn't pass an explicit value to it.
+ // Also, any unmodified default value coming after the last user specified
+ // argument and before the modified argument must be explicitly stated.
+ QStringList otherArgs;
+ bool otherArgsModified = false;
+ bool argsClear = true;
+ for (auto i = func->arguments().size() - 1; i >= maxArgs + removedArgs; i--) {
+ const AbstractMetaArgument &arg = func->arguments().at(i);
+ const bool defValModified = arg.hasModifiedDefaultValueExpression();
+ const bool hasConversionRule =
+ func->hasConversionRule(TypeSystem::NativeCode, arg.argumentIndex() + 1);
+ if (argsClear && !defValModified && !hasConversionRule)
+ continue;
+ argsClear = false;
+ otherArgsModified |= defValModified || hasConversionRule || arg.isModifiedRemoved();
+ if (hasConversionRule)
+ otherArgs.prepend(arg.name() + CONV_RULE_OUT_VAR_SUFFIX);
+ else
+ otherArgs.prepend(CPP_ARG_REMOVED(i));
+ }
+ if (otherArgsModified)
+ userArgs << otherArgs;
+ }
+
+ bool isCtor = false;
+ StringStream mc(TextStream::Language::Cpp);
+
+ StringStream uva(TextStream::Language::Cpp);
+ if (func->isOperatorOverload() && !func->isCallOperator()) {
+ QString firstArg(u'(');
+ if (!func->isPointerOperator()) // no de-reference operator
+ firstArg += u'*';
+ firstArg += CPP_SELF_VAR;
+ firstArg += u')';
+ QString secondArg = CPP_ARG0;
+ if (!func->isUnaryOperator())
+ AbstractMetaType::applyDereference(&secondArg, argumentIndirections.at(0));
+
+ if (func->isUnaryOperator())
+ std::swap(firstArg, secondArg);
+
+ QString op = func->originalName();
+ op.remove(0, int(std::strlen("operator")));
+
+ if (func->isBinaryOperator()) {
+ if (func->isReverseOperator())
+ std::swap(firstArg, secondArg);
+
+ // Emulate operator+=/-= (__iadd__, __isub__) by using ++/--
+ if (((op == u"++") || (op == u"--")) && !func->isReverseOperator()) {
+ s << "\nfor (int i = 0; i < " << secondArg
+ << "; ++i, " << op << firstArg << ");\n";
+ mc << firstArg;
+ } else {
+ mc << firstArg << ' ' << op << ' ' << secondArg;
+ }
+ } else {
+ mc << op << ' ' << secondArg;
+ }
+ } else if (!injectedCodeCallsCppFunction(context, func)) {
+ if (func->isConstructor()) {
+ isCtor = true;
+ const auto owner = func->ownerClass();
+ Q_ASSERT(owner == context.metaClass());
+ if (func->functionType() == AbstractMetaFunction::CopyConstructorFunction
+ && maxArgs == 1) {
+ mc << "new " << globalScopePrefix(context) << context.effectiveClassName()
+ << "(*" << CPP_ARG0 << ')';
+ } else {
+ const QString ctorCall = context.effectiveClassName() + u'('
+ + userArgs.join(u", "_s) + u')';
+ if (usePySideExtensions() && isQObject(owner)) {
+ s << "void *addr = PySide::nextQObjectMemoryAddr();\n";
+ uva << "if (addr != nullptr) {\n" << indent
+ << "cptr = new (addr) " << globalScopePrefix(context) << ctorCall
+ << ";\nPySide::setNextQObjectMemoryAddr(nullptr);\n" << outdent
+ << "} else {\n" << indent
+ << "cptr = new " << globalScopePrefix(context) << ctorCall << ";\n"
+ << outdent << "}\n";
+ } else {
+ mc << "new " << globalScopePrefix(context) << ctorCall;
+ }
+ }
+ } else {
+ QString methodCallClassName;
+ if (context.forSmartPointer())
+ methodCallClassName = context.preciseType().cppSignature();
+ else if (func->ownerClass())
+ methodCallClassName = func->ownerClass()->qualifiedCppName();
+
+ if (auto ownerClass = func->ownerClass()) {
+ const bool hasWrapper = shouldGenerateCppWrapper(ownerClass);
+ if (!avoidProtectedHack() || !func->isProtected() || !hasWrapper) {
+ if (func->isStatic()) {
+ mc << m_gsp << methodCallClassName << "::";
+ } else {
+ const QString cppSelfVar = CPP_SELF_VAR;
+ const QString selfVarCast = func->ownerClass() == func->implementingClass()
+ ? cppSelfVar
+ : u"reinterpret_cast<"_s + methodCallClassName
+ + u" *>("_s + cppSelfVar + u')';
+ if (func->isConstant()) {
+ if (avoidProtectedHack()) {
+ mc << "const_cast<const " << globalScopePrefix(context);
+ if (ownerClass->cppWrapper().testFlag(AbstractMetaClass::CppProtectedHackWrapper)) {
+ // PYSIDE-500: Need a special wrapper cast when inherited
+ const QString selfWrapCast = ownerClass == func->implementingClass()
+ ? cppSelfVar
+ : u"reinterpret_cast<"_s + wrapperName(ownerClass)
+ + u" *>("_s + cppSelfVar + u')';
+ mc << wrapperName(ownerClass);
+ mc << " *>(" << selfWrapCast << ")->";
+ }
+ else {
+ mc << methodCallClassName;
+ mc << " *>(" << selfVarCast << ")->";
+ }
+ } else {
+ mc << "const_cast<const " << m_gsp << methodCallClassName;
+ mc << " *>(" << selfVarCast << ")->";
+ }
+ } else {
+ mc << selfVarCast << "->";
+ }
+ }
+
+ if (!func->isAbstract() && func->isVirtual())
+ mc << "::%CLASS_NAME::";
+
+ mc << func->originalName();
+ } else {
+ if (!func->isStatic()) {
+ const bool directInheritance = context.metaClass() == ownerClass;
+ mc << (directInheritance ? "static_cast" : "reinterpret_cast")
+ << '<' << wrapperName(ownerClass) << " *>("
+ << CPP_SELF_VAR << ")->";
+ }
+
+ if (!func->isAbstract())
+ mc << (func->isProtected() ? wrapperName(func->ownerClass()) :
+ m_gsp + methodCallClassName) << "::";
+ mc << func->originalName() << "_protected";
+ }
+ } else {
+ mc << func->originalName();
+ }
+ mc << '(' << userArgs.join(u", "_s) << ')';
+ if (!func->isAbstract() && func->isVirtual()) {
+ if (!avoidProtectedHack() || !func->isProtected()) {
+ QString virtualCall = mc;
+ QString normalCall = virtualCall;
+ virtualCall.replace(u"%CLASS_NAME"_s,
+ methodCallClassName);
+ normalCall.remove(u"::%CLASS_NAME::"_s);
+ mc.clear();
+ mc << "Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(self))\n"
+ << " ? " << virtualCall << '\n'
+ << " : " << normalCall;
+ }
+ }
+ }
+ }
+
+ if (!injectedCodeCallsCppFunction(context, func)) {
+ const bool allowThread = func->allowThread();
+ generateExceptionHandling = func->generateExceptionHandling();
+ if (generateExceptionHandling) {
+ s << tryBlock << indent;
+ if (allowThread) {
+ s << "Shiboken::ThreadStateSaver threadSaver;\n"
+ << "threadSaver.save();\n";
+ }
+ } else if (allowThread) {
+ s << BEGIN_ALLOW_THREADS << '\n';
+ }
+ if (isCtor) {
+ if (uva.size() > 0)
+ s << uva.toString() << '\n';
+ else
+ s << "cptr = " << mc.toString() << ";\n";
+ } else if (!func->isVoid() && !func->isInplaceOperator()) {
+ bool writeReturnType = true;
+ if (avoidProtectedHack()) {
+ auto metaEnum = api().findAbstractMetaEnum(func->type().typeEntry());
+ if (metaEnum.has_value()) {
+ QString enumName;
+ if (metaEnum->isProtected()) {
+ enumName = context.wrapperName() + u"::"_s
+ + metaEnum.value().name();
+ } else {
+ enumName = func->type().cppSignature();
+ }
+ const QString methodCall = enumName + u'('
+ + mc.toString() + u')';
+ mc.clear();
+ mc << methodCall;
+ s << enumName;
+ writeReturnType = false;
+ }
+ }
+ QString methodCall = mc.toString();
+ if (writeReturnType) {
+ s << func->type().cppSignature();
+ if (func->type().isObjectTypeUsedAsValueType()) {
+ s << '*';
+ methodCall = u"new "_s
+ + func->type().typeEntry()->qualifiedCppName()
+ + u'(' + mc.toString() + u')';
+ }
+ }
+ s << " " << CPP_RETURN_VAR << " = " << methodCall << ";\n";
+ } else {
+ s << mc.toString() << ";\n";
+ }
+
+ if (allowThread) {
+ s << (generateExceptionHandling
+ ? u"threadSaver.restore();"_s : END_ALLOW_THREADS) << '\n';
+ }
+
+ // Convert result
+ const auto funcType = func->type();
+ if (func->hasConversionRule(TypeSystem::TargetLangCode, 0)) {
+ writeConversionRule(s, func, TypeSystem::TargetLangCode,
+ PYTHON_RETURN_VAR);
+ } else if (!isCtor && !func->isInplaceOperator() && !func->isVoid()
+ && !func->injectedCodeHasReturnValueAttribution(TypeSystem::TargetLangCode)) {
+ if (func->type().isObjectTypeUsedAsValueType()) {
+ s << PYTHON_RETURN_VAR << " = Shiboken::Object::newObject("
+ << cpythonTypeNameExt(func->type().typeEntry())
+ << ", " << CPP_RETURN_VAR << ", true, true)";
+ } else if (func->generateOpaqueContainerReturn()) {
+ const QString creationFunc = opaqueContainerCreationFunc(funcType);
+ writeOpaqueContainerCreationFuncDecl(s, creationFunc, funcType);
+ s << PYTHON_RETURN_VAR << " = " << creationFunc
+ << "(&" << CPP_RETURN_VAR << ");\n";
+ } else {
+ s << PYTHON_RETURN_VAR << " = ";
+ writeToPythonConversion(s, funcType, func->ownerClass(),
+ CPP_RETURN_VAR);
+ }
+ s << ";\n";
+ }
+
+ if (generateExceptionHandling) { // "catch" code
+ s << outdent << defaultExceptionHandling;
+ }
+ } // !injected code calls C++ function
+ } // !userAdded
+
+ if (func->hasInjectedCode() && !func->isConstructor())
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd,
+ TypeSystem::TargetLangCode, func, usesPyArgs, lastArg);
+
+ bool hasReturnPolicy = false;
+
+ // Ownership transference between C++ and Python.
+ QList<ArgumentModification> ownership_mods;
+ // Python object reference management.
+ QList<ArgumentModification> refcount_mods;
+ for (const auto &func_mod : func->modifications()) {
+ for (const ArgumentModification &arg_mod : func_mod.argument_mods()) {
+ if (arg_mod.targetOwnerShip() != TypeSystem::UnspecifiedOwnership)
+ ownership_mods.append(arg_mod);
+ else if (!arg_mod.referenceCounts().isEmpty())
+ refcount_mods.append(arg_mod);
+ }
+ }
+
+ // If there's already a setParent(return, me), don't use the return heuristic!
+ if (func->argumentOwner(func->ownerClass(), -1).index == 0)
+ hasReturnPolicy = true;
+
+ if (!ownership_mods.isEmpty()) {
+ s << '\n' << "// Ownership transferences.\n";
+ for (const ArgumentModification &arg_mod : std::as_const(ownership_mods)) {
+ const int argIndex = arg_mod.index();
+ const QString pyArgName = argumentNameFromIndex(api(), func, argIndex);
+
+ if (argIndex == 0 || arg_mod.owner().index == 0)
+ hasReturnPolicy = true;
+
+ // The default ownership does nothing. This is useful to avoid automatic heuristically
+ // based generation of code defining parenting.
+ const auto ownership = arg_mod.targetOwnerShip();
+ if (ownership == TypeSystem::DefaultOwnership)
+ continue;
+
+ s << "Shiboken::Object::";
+ if (ownership == TypeSystem::TargetLangOwnership) {
+ s << "getOwnership(" << pyArgName << ");";
+ } else if (auto ac = argumentClassFromIndex(api(), func, argIndex);
+ ac && ac->hasVirtualDestructor()) {
+ s << "releaseOwnership(" << pyArgName << ");";
+ } else {
+ s << "invalidate(" << pyArgName << ");";
+ }
+ s << '\n';
+ }
+
+ } else if (!refcount_mods.isEmpty()) {
+ for (const ArgumentModification &arg_mod : std::as_const(refcount_mods)) {
+ ReferenceCount refCount = arg_mod.referenceCounts().constFirst();
+ if (refCount.action != ReferenceCount::Set
+ && refCount.action != ReferenceCount::Remove
+ && refCount.action != ReferenceCount::Add) {
+ qCWarning(lcShiboken) << "\"set\", \"add\" and \"remove\" are the only values supported by Shiboken for action attribute of reference-count tag.";
+ continue;
+ }
+ const int argIndex = arg_mod.index();
+ const QString pyArgName = refCount.action == ReferenceCount::Remove
+ ? u"Py_None"_s : argumentNameFromIndex(api(), func, argIndex);
+
+ if (refCount.action == ReferenceCount::Add || refCount.action == ReferenceCount::Set)
+ s << "Shiboken::Object::keepReference(";
+ else
+ s << "Shiboken::Object::removeReference(";
+
+ s << "reinterpret_cast<SbkObject *>(self), \"";
+ QString varName = arg_mod.referenceCounts().constFirst().varName;
+ if (varName.isEmpty())
+ varName = func->minimalSignature() + QString::number(argIndex);
+
+ s << varName << "\", " << pyArgName
+ << (refCount.action == ReferenceCount::Add ? ", true" : "")
+ << ");\n";
+
+ if (argIndex == 0)
+ hasReturnPolicy = true;
+ }
+ }
+ writeParentChildManagement(s, func, usesPyArgs, !hasReturnPolicy);
+
+ if (generateExceptionHandling)
+ s << propagateException;
+}
+
+QStringList CppGenerator::getAncestorMultipleInheritance(const AbstractMetaClassCPtr &metaClass)
+{
+ QStringList result;
+ const auto &baseClases = metaClass->typeSystemBaseClasses();
+ if (!baseClases.isEmpty()) {
+ for (const auto &baseClass : baseClases) {
+ QString offset;
+ QTextStream(&offset) << "reinterpret_cast<uintptr_t>(static_cast<const "
+ << baseClass->qualifiedCppName() << " *>(class_ptr)) - base";
+ result.append(offset);
+ offset.clear();
+ QTextStream(&offset) << "reinterpret_cast<uintptr_t>(static_cast<const "
+ << baseClass->qualifiedCppName() << " *>(static_cast<const "
+ << metaClass->qualifiedCppName()
+ << " *>(static_cast<const void *>(class_ptr)))) - base";
+ result.append(offset);
+ }
+
+ for (const auto &baseClass : baseClases)
+ result.append(getAncestorMultipleInheritance(baseClass));
+ }
+ return result;
+}
+
+void CppGenerator::writeMultipleInheritanceInitializerFunction(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass)
+{
+ QString className = metaClass->qualifiedCppName();
+ const QStringList ancestors = getAncestorMultipleInheritance(metaClass);
+ s << "int *\n"
+ << multipleInheritanceInitializerFunctionName(metaClass) << "(const void *cptr)\n"
+ << "{\n" << indent;
+ s << "static int mi_offsets[] = {-2";
+ for (qsizetype i = 0; i < ancestors.size(); i++)
+ s << ", 0";
+ s << "};\n"
+ << "if (mi_offsets[0] == -2) {\n" << indent
+ << "const auto *class_ptr = reinterpret_cast<const " << className << " *>(cptr);\n"
+ << "const auto base = reinterpret_cast<uintptr_t>(class_ptr);\n"
+ << "int *p = mi_offsets;\n";
+
+ for (const QString &ancestor : ancestors)
+ s << "*p++ = int(" << ancestor << ");\n";
+ s << "std::sort(mi_offsets, p);\n"
+ << "auto *end = std::unique(mi_offsets, p);\n"
+ << "*end++ = -1;\n"
+ << "if (mi_offsets[0] == 0)\n"
+ << indent
+ << "std::memmove(&mi_offsets[0], &mi_offsets[1], (end - mi_offsets - 1) * sizeof(int));\n"
+ << outdent << outdent
+ << "}\nreturn mi_offsets;\n" << outdent << "}\n";
+}
+
+void CppGenerator::writeSpecialCastFunction(TextStream &s, const AbstractMetaClassCPtr &metaClass)
+{
+ QString className = metaClass->qualifiedCppName();
+ s << "static void * " << cpythonSpecialCastFunctionName(metaClass)
+ << "(void *obj, PyTypeObject *desiredType)\n{\n" << indent
+ << "auto me = reinterpret_cast< " << m_gsp << className << " *>(obj);\n";
+ bool firstClass = true;
+ const auto &allAncestors = metaClass->allTypeSystemAncestors();
+ for (const auto &baseClass : allAncestors) {
+ if (!firstClass)
+ s << "else ";
+ s << "if (desiredType == " << cpythonTypeNameExt(baseClass->typeEntry())
+ << ")\n" << indent
+ << "return static_cast< " << getFullTypeName(baseClass) << " *>(me);\n"
+ << outdent;
+ firstClass = false;
+ }
+ s << "return me;\n" << outdent << "}\n\n";
+}
+
+void CppGenerator::writePrimitiveConverterInitialization(TextStream &s,
+ const CustomConversionPtr &customConversion)
+{
+ TypeEntryCPtr type = customConversion->ownerType();
+ QString converter = converterObject(type);
+ s << "// Register converter for type '" << type->qualifiedTargetLangName() << "'.\n"
+ << converter << " = Shiboken::Conversions::createConverter(";
+ if (!type->hasTargetLangApiType())
+ s << "nullptr";
+ else if (type->targetLangApiName() == cPyObjectT)
+ s << "&PyBaseObject_Type";
+ else
+ s << '&' << type->targetLangApiName() << "_Type";
+ QString typeName = fixedCppTypeName(type);
+ s << ", " << cppToPythonFunctionName(typeName, typeName) << ");\n"
+ << registerConverterName(type->qualifiedCppName(), converter);
+ writeCustomConverterRegister(s, customConversion, converter);
+}
+
+static void registerConverterInScopes(TextStream &s, QStringView signature,
+ QAnyStringView varName = converterVar)
+{
+ while (true) {
+ s << registerConverterName(signature, varName);
+ const auto qualifierPos = signature.indexOf("::"_L1);
+ if (qualifierPos == -1)
+ break;
+ signature = signature.sliced(qualifierPos + 2);
+ }
+}
+
+void CppGenerator::writeEnumConverterInitialization(TextStream &s, const AbstractMetaEnum &metaEnum)
+{
+ if (metaEnum.isPrivate() || metaEnum.isAnonymous())
+ return;
+ EnumTypeEntryCPtr enumType = metaEnum.typeEntry();
+ Q_ASSERT(enumType);
+
+ static const char enumPythonVar[] = "EType";
+
+ s << "// Register converter for enum '" << enumType->qualifiedCppName()
+ << "'.\n{\n" << indent;
+
+ const QString typeName = fixedCppTypeName(enumType);
+ s << "SbkConverter *converter = Shiboken::Conversions::createConverter("
+ << enumPythonVar << ',' << '\n' << indent
+ << cppToPythonFunctionName(typeName, typeName) << ");\n" << outdent;
+
+ const QString toCpp = pythonToCppFunctionName(typeName, typeName);
+ const QString isConv = convertibleToCppFunctionName(typeName, typeName);
+ writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv);
+ s << "Shiboken::Enum::setTypeConverter(" << enumPythonVar
+ << ", converter);\n";
+
+ registerConverterInScopes(s, enumType->qualifiedCppName());
+ if (auto flags = enumType->flags())
+ s << "// Register converter for flag '" << flags->qualifiedCppName() << "'.\n"
+ << registerConverterName(flags->name()) // QMetaType
+ << registerConverterName(flags->originalName()); // Signals with flags
+
+ s << outdent << "}\n";
+}
+
+QString CppGenerator::writeContainerConverterInitialization(TextStream &s,
+ const AbstractMetaType &type,
+ const ApiExtractorResult &api)
+{
+ const auto cppSignature =
+ QString::fromUtf8(QMetaObject::normalizedSignature(type.cppSignature().toUtf8()));
+ s << "// Register converter for type '" << cppSignature << "'.\n";
+ const QString converter = converterObject(type);
+ s << converter << " = Shiboken::Conversions::createConverter(";
+
+ Q_ASSERT(type.typeEntry()->isContainer());
+ const auto typeEntry = std::static_pointer_cast<const ContainerTypeEntry>(type.typeEntry());
+
+ const QString targetTypeName = containerNativeToTargetTypeName(typeEntry);
+ if (targetTypeName == cPyObjectT) {
+ s << "&PyBaseObject_Type";
+ } else {
+ s << '&' << targetTypeName << "_Type";
+ }
+
+ const QString typeName = fixedCppTypeName(type);
+ s << ", " << cppToPythonFunctionName(typeName, targetTypeName) << ");\n";
+
+ s << registerConverterName(cppSignature, converter);
+ if (usePySideExtensions() && cppSignature.startsWith("const "_L1)
+ && cppSignature.endsWith(u'&')) {
+ auto underlyingType = QStringView{cppSignature}.sliced(6, cppSignature.size() - 7);
+ s << registerConverterName(underlyingType, converter);
+ }
+
+ for (const auto &conv : typeEntry->customConversion()->targetToNativeConversions()) {
+ const QString &sourceTypeName = conv.sourceTypeName();
+ QString toCpp = pythonToCppFunctionName(sourceTypeName, typeName);
+ QString isConv = convertibleToCppFunctionName(sourceTypeName, typeName);
+ writeAddPythonToCppConversion(s, converter, toCpp, isConv);
+ }
+
+ auto typedefItPair = api.typedefTargetToName().equal_range(type.cppSignature());
+ if (typedefItPair.first != typedefItPair.second) {
+ auto *typeDb = TypeDatabase::instance();
+ s << "// Register converters for type aliases of " << cppSignature << "'.\n";
+ for (auto it = typedefItPair.first; it != typedefItPair.second; ++it) {
+ if (!typeDb->findType(it.value()))
+ s << registerConverterName(it.value(), converter);
+ }
+ }
+
+ 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)
+{
+ s << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName()
+ << ".\n";
+ for (const auto &sourceClass : conversions) {
+ 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, typeInitStruct(externalType), toCpp, isConv);
+ }
+}
+
+QString CppGenerator::multipleInheritanceInitializerFunctionName(const AbstractMetaClassCPtr &metaClass)
+{
+ return cpythonBaseName(metaClass->typeEntry()) + u"_mi_init"_s;
+}
+
+bool CppGenerator::supportsMappingProtocol(const AbstractMetaClassCPtr &metaClass)
+{
+ for (const auto &m : mappingProtocols()) {
+ if (metaClass->hasFunction(m.name))
+ return true;
+ }
+
+ return false;
+}
+
+bool CppGenerator::supportsNumberProtocol(const AbstractMetaClassCPtr &metaClass)
+{
+ return metaClass->hasArithmeticOperatorOverload()
+ || metaClass->hasIncDecrementOperatorOverload()
+ || metaClass->hasLogicalOperatorOverload()
+ || metaClass->hasBitwiseOperatorOverload()
+ || hasBoolCast(metaClass);
+}
+
+bool CppGenerator::supportsSequenceProtocol(const AbstractMetaClassCPtr &metaClass)
+{
+ for (const auto &seq : sequenceProtocols()) {
+ if (metaClass->hasFunction(seq.name))
+ return true;
+ }
+
+ ComplexTypeEntryCPtr baseType = metaClass->typeEntry()->baseContainerType();
+ return baseType && baseType->isContainer();
+}
+
+bool CppGenerator::shouldGenerateGetSetList(const AbstractMetaClassCPtr &metaClass)
+{
+ for (const AbstractMetaField &f : metaClass->fields()) {
+ if (!f.isStatic())
+ return true;
+ }
+ // Generate all user-added properties unless Pyside extensions are used,
+ // in which only the explicitly specified ones are generated (rest is handled
+ // in libpyside).
+ return usePySideExtensions()
+ ? std::any_of(metaClass->propertySpecs().cbegin(), metaClass->propertySpecs().cend(),
+ [] (const QPropertySpec &s) { return s.generateGetSetDef(); })
+ : !metaClass->propertySpecs().isEmpty();
+ return false;
+}
+
+struct pyTypeSlotEntry
+{
+ explicit pyTypeSlotEntry(QAnyStringView name, QAnyStringView function) :
+ m_name(name), m_function(function) {}
+
+ QAnyStringView m_name;
+ QAnyStringView m_function;
+};
+
+TextStream &operator<<(TextStream &str, const pyTypeSlotEntry &e)
+{
+ if (!e.m_function.isEmpty()) {
+ str << '{' << e.m_name << ',' << Pad(' ', qMax(0, 18 - e.m_name.size()))
+ << "reinterpret_cast<void *>(" << e.m_function << ")},\n";
+ }
+ return str;
+}
+
+void CppGenerator::writeClassDefinition(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass,
+ const GeneratorContext &classContext)
+{
+ QString tp_init;
+ QString tp_new;
+ QString tp_dealloc;
+ QString tp_hash;
+ QString tp_call;
+ const QString className = chopType(cpythonTypeName(metaClass));
+ AbstractMetaFunctionCList ctors;
+ const auto &allCtors = metaClass->queryFunctions(FunctionQueryOption::AnyConstructor);
+ for (const auto &f : allCtors) {
+ if (!f->isPrivate() && !f->isModifiedRemoved()
+ && f->functionType() != AbstractMetaFunction::MoveConstructorFunction) {
+ ctors.append(f);
+ }
+ }
+
+ bool onlyPrivCtor = !metaClass->hasNonPrivateConstructor();
+
+ const bool isQApp = usePySideExtensions()
+ && inheritsFrom(metaClass, u"QCoreApplication"_s);
+
+ QString tp_flags = u"Py_TPFLAGS_DEFAULT"_s;
+ if (!metaClass->attributes().testFlag(AbstractMetaClass::FinalCppClass))
+ tp_flags += u"|Py_TPFLAGS_BASETYPE"_s;
+ if (metaClass->isNamespace() || metaClass->hasPrivateDestructor()) {
+ tp_dealloc = metaClass->hasPrivateDestructor() ?
+ u"SbkDeallocWrapperWithPrivateDtor"_s :
+ u"Sbk_object_dealloc /* PYSIDE-832: Prevent replacement of \"0\" with subtype_dealloc. */"_s;
+ tp_init.clear();
+ } else {
+ tp_dealloc = isQApp
+ ? u"&SbkDeallocQAppWrapper"_s : u"&SbkDeallocWrapper"_s;
+ if (!onlyPrivCtor && !ctors.isEmpty())
+ tp_init = cpythonFunctionName(ctors.constFirst());
+ }
+
+ const AttroCheck attroCheck = checkAttroFunctionNeeds(metaClass);
+ const QString tp_getattro = (attroCheck & AttroCheckFlag::GetattroMask) != 0
+ ? cpythonGetattroFunctionName(metaClass) : QString();
+ const QString tp_setattro = (attroCheck & AttroCheckFlag::SetattroMask) != 0
+ ? cpythonSetattroFunctionName(metaClass) : QString();
+
+ if (metaClass->hasPrivateDestructor() || onlyPrivCtor) {
+ // tp_flags = u"Py_TPFLAGS_DEFAULT"_s;
+ // This is not generally possible, because PySide does not care about
+ // privacy the same way. This worked before the heap types were used,
+ // because inheritance is not really checked for static types.
+ // Instead, we check this at runtime, see SbkObjectType_tp_new.
+ if (metaClass->fullName().startsWith(u"PySide6.Qt")) {
+ // PYSIDE-595: No idea how to do non-inheritance correctly.
+ // Since that is only relevant in shiboken, I used a shortcut for
+ // PySide.
+ tp_new = u"SbkObject_tp_new"_s;
+ }
+ else {
+ tp_new = u"SbkDummyNew /* PYSIDE-595: Prevent replacement "
+ "of \"0\" with base->tp_new. */"_s;
+ }
+ }
+ else if (isQApp) {
+ tp_new = u"SbkQApp_tp_new"_s; // PYSIDE-571: need singleton app
+ }
+ else {
+ tp_new = u"SbkObject_tp_new"_s;
+ }
+ tp_flags.append(u"|Py_TPFLAGS_HAVE_GC"_s);
+
+ QString tp_richcompare;
+ if (generateRichComparison(classContext))
+ tp_richcompare = cpythonBaseName(metaClass) + u"_richcompare"_s;
+
+ const bool isSmartPointer = classContext.forSmartPointer();
+ QString tp_getset;
+ if (shouldGenerateGetSetList(metaClass) && !isSmartPointer)
+ tp_getset = cpythonGettersSettersDefinitionName(metaClass);
+
+ // search for special functions
+ clearTpFuncs();
+ for (const auto &func : metaClass->functions()) {
+ // Special non-operator functions identified by name
+ auto it = m_tpFuncs.find(func->name());
+ if (it != m_tpFuncs.end())
+ it.value() = cpythonFunctionName(func);
+ else if ( it = m_nbFuncs.find(func->name()); it != m_nbFuncs.end() )
+ it.value() = cpythonFunctionName(func);
+ }
+ if (m_tpFuncs.value(REPR_FUNCTION).isEmpty()
+ && (isSmartPointer || metaClass->hasToStringCapability())) {
+ const QString name = isSmartPointer
+ ? writeSmartPointerReprFunction(s, classContext)
+ : writeReprFunction(s, classContext, metaClass->toStringCapabilityIndirections());
+ m_tpFuncs[REPR_FUNCTION] = name;
+ }
+
+ // class or some ancestor has multiple inheritance
+ const auto miClass = getMultipleInheritingClass(metaClass);
+ if (miClass) {
+ if (metaClass == miClass)
+ writeMultipleInheritanceInitializerFunction(s, metaClass);
+ writeSpecialCastFunction(s, metaClass);
+ s << '\n';
+ }
+
+ s << "// Class Definition -----------------------------------------------\n"
+ "extern \"C\" {\n";
+
+ if (hasHashFunction(metaClass))
+ tp_hash = u'&' + cpythonBaseName(metaClass) + u"_HashFunc"_s;
+
+ const auto callOp = metaClass->findFunction("operator()");
+ if (callOp && !callOp->isModifiedRemoved())
+ tp_call = u'&' + cpythonFunctionName(callOp);
+
+ const QString typePtr = u"_"_s + className
+ + u"_Type"_s;
+ s << "static PyTypeObject *" << typePtr << " = nullptr;\n"
+ << "static PyTypeObject *" << className << "_TypeF(void)\n"
+ << "{\n" << indent << "return " << typePtr << ";\n" << outdent
+ << "}\n\nstatic PyType_Slot " << className << "_slots[] = {\n" << indent
+ << "{Py_tp_base, nullptr}, // inserted by introduceWrapperType\n"
+ << pyTypeSlotEntry("Py_tp_dealloc", tp_dealloc)
+ << pyTypeSlotEntry("Py_tp_repr", m_tpFuncs.value(REPR_FUNCTION))
+ << pyTypeSlotEntry("Py_tp_hash", tp_hash)
+ << pyTypeSlotEntry("Py_tp_call", tp_call)
+ << pyTypeSlotEntry("Py_tp_str", m_tpFuncs.value(u"__str__"_s))
+ << pyTypeSlotEntry("Py_tp_getattro", tp_getattro)
+ << pyTypeSlotEntry("Py_tp_setattro", tp_setattro)
+ << pyTypeSlotEntry("Py_tp_traverse", className + u"_traverse"_s)
+ << pyTypeSlotEntry("Py_tp_clear", className + u"_clear"_s)
+ << pyTypeSlotEntry("Py_tp_richcompare", tp_richcompare)
+ << pyTypeSlotEntry("Py_tp_iter", m_tpFuncs.value(u"__iter__"_s))
+ << pyTypeSlotEntry("Py_tp_iternext", m_tpFuncs.value(u"__next__"_s))
+ << pyTypeSlotEntry("Py_tp_methods", className + u"_methods"_s)
+ << pyTypeSlotEntry("Py_tp_getset", tp_getset)
+ << pyTypeSlotEntry("Py_tp_init", tp_init)
+ << pyTypeSlotEntry("Py_tp_new", tp_new);
+ if (supportsSequenceProtocol(metaClass)) {
+ s << "// type supports sequence protocol\n";
+ writeTypeAsSequenceDefinition(s, metaClass);
+ }
+ if (supportsMappingProtocol(metaClass)) {
+ s << "// type supports mapping protocol\n";
+ writeTypeAsMappingDefinition(s, metaClass);
+ }
+ if (supportsNumberProtocol(metaClass)) {
+ s << "// type supports number protocol\n";
+ writeTypeAsNumberDefinition(s, metaClass);
+ }
+ s << "{0, " << NULL_PTR << "}\n" << outdent << "};\n";
+
+ int packageLevel = packageName().count(u'.') + 1;
+ s << "static PyType_Spec " << className << "_spec = {\n" << indent
+ << '"' << packageLevel << ':' << getClassTargetFullName(metaClass) << "\",\n"
+ << "sizeof(SbkObject),\n0,\n" << tp_flags << ",\n"
+ << className << "_slots\n" << outdent
+ << "};\n\n} //extern \"C\"\n";
+}
+
+void CppGenerator::writeMappingMethods(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass,
+ const GeneratorContext &context) const
+{
+ for (const auto & m : mappingProtocols()) {
+ const auto func = metaClass->findFunction(m.name);
+ if (!func)
+ continue;
+ QString funcName = cpythonFunctionName(func);
+ CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode);
+ s << m.returnType << ' ' << funcName << '(' << m.arguments << ")\n{\n" << indent;
+
+ writeCppSelfDefinition(s, func, context, ErrorReturn::Default);
+
+ const AbstractMetaArgument *lastArg = func->arguments().isEmpty()
+ ? nullptr : &func->arguments().constLast();
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionAny,
+ TypeSystem::TargetLangCode, func, false, lastArg);
+ s << outdent << "}\n\n";
+ }
+}
+
+void CppGenerator::writeSequenceMethods(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass,
+ const GeneratorContext &context) const
+{
+ bool injectedCode = false;
+
+ for (const auto &seq : sequenceProtocols()) {
+ const auto func = metaClass->findFunction(seq.name);
+ if (!func)
+ continue;
+ injectedCode = true;
+ QString funcName = cpythonFunctionName(func);
+
+ CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode);
+ s << seq.returnType << ' ' << funcName << '(' << seq.arguments << ")\n{\n" << indent;
+
+ writeCppSelfDefinition(s, func, context, ErrorReturn::Default);
+
+ const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast();
+ writeCodeSnips(s, snips,TypeSystem::CodeSnipPositionAny,
+ TypeSystem::TargetLangCode, func, false /* uses PyArgs */, lastArg);
+ s<< outdent << "}\n\n";
+ }
+
+ if (!injectedCode)
+ writeDefaultSequenceMethods(s, context);
+}
+
+// Sequence protocol structure member names
+static const QHash<QString, QString> &sqFuncs()
+{
+ static const QHash<QString, QString> result = {
+ {u"__concat__"_s, u"Py_sq_concat"_s},
+ {u"__contains__"_s, u"Py_sq_contains"_s},
+ {u"__getitem__"_s, u"Py_sq_item"_s},
+ {u"__getslice__"_s, u"Py_sq_slice"_s},
+ {u"__len__"_s, u"Py_sq_length"_s},
+ {u"__setitem__"_s, u"Py_sq_ass_item"_s},
+ {u"__setslice__"_s, u"Py_sq_ass_slice"_s}
+ };
+ return result;
+}
+
+void CppGenerator::writeTypeAsSequenceDefinition(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass)
+{
+ bool hasFunctions = false;
+ QMap<QString, QString> funcs;
+ for (const auto &seq : sequenceProtocols()) {
+ const auto func = metaClass->findFunction(seq.name);
+ if (func) {
+ funcs.insert(seq.name, u'&' + cpythonFunctionName(func));
+ hasFunctions = true;
+ }
+ }
+
+ QString baseName = cpythonBaseName(metaClass);
+
+ //use default implementation
+ if (!hasFunctions) {
+ funcs[u"__len__"_s] = baseName + u"__len__"_s;
+ funcs[u"__getitem__"_s] = baseName + u"__getitem__"_s;
+ funcs[u"__setitem__"_s] = baseName + u"__setitem__"_s;
+ }
+
+ for (auto it = sqFuncs().cbegin(), end = sqFuncs().cend(); it != end; ++it) {
+ const QString &sqName = it.key();
+ auto fit = funcs.constFind(sqName);
+ if (fit != funcs.constEnd())
+ s << pyTypeSlotEntry(it.value(), fit.value());
+ }
+}
+
+void CppGenerator::writeTypeAsMappingDefinition(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass)
+{
+ // Sequence protocol structure members names
+ static const QHash<QString, QString> mpFuncs{
+ {u"__mlen__"_s, u"Py_mp_length"_s},
+ {u"__mgetitem__"_s, u"Py_mp_subscript"_s},
+ {u"__msetitem__"_s, u"Py_mp_ass_subscript"_s},
+ };
+ QMap<QString, QString> funcs;
+ for (const auto &m : mappingProtocols()) {
+ const auto func = metaClass->findFunction(m.name);
+ if (func) {
+ const QString entry = u"reinterpret_cast<void *>(&"_s
+ + cpythonFunctionName(func) + u')';
+ funcs.insert(m.name, entry);
+ }
+ }
+
+ for (auto it = mpFuncs.cbegin(), end = mpFuncs.cend(); it != end; ++it) {
+ const auto fit = funcs.constFind(it.key());
+ if (fit != funcs.constEnd())
+ s << pyTypeSlotEntry(it.value(), fit.value());
+ }
+}
+
+// Number protocol structure members names
+static const QHash<QString, QString> &nbFuncs()
+{
+ static const QHash<QString, QString> result = {
+ {u"__abs__"_s, u"Py_nb_absolute"_s},
+ {u"__add__"_s, u"Py_nb_add"_s},
+ {u"__sub__"_s, u"Py_nb_subtract"_s},
+ {u"__mul__"_s, u"Py_nb_multiply"_s},
+ {u"__div__"_s, u"Py_nb_true_divide"_s},
+ {u"__mod__"_s, u"Py_nb_remainder"_s},
+ {u"__neg__"_s, u"Py_nb_negative"_s},
+ {u"__pos__"_s, u"Py_nb_positive"_s},
+ {u"__pow__"_s, u"Py_nb_power"_s},
+ {u"__invert__"_s, u"Py_nb_invert"_s},
+ {u"__lshift__"_s, u"Py_nb_lshift"_s},
+ {u"__rshift__"_s, u"Py_nb_rshift"_s},
+ {u"__and__"_s, u"Py_nb_and"_s},
+ {u"__xor__"_s, u"Py_nb_xor"_s},
+ {u"__or__"_s, u"Py_nb_or"_s},
+ {u"__iadd__"_s, u"Py_nb_inplace_add"_s},
+ {u"__isub__"_s, u"Py_nb_inplace_subtract"_s},
+ {u"__imul__"_s, u"Py_nb_inplace_multiply"_s},
+ {u"__imod__"_s, u"Py_nb_inplace_remainder"_s},
+ {u"__ilshift__"_s, u"Py_nb_inplace_lshift"_s},
+ {u"__irshift__"_s, u"Py_nb_inplace_rshift"_s},
+ {u"__iand__"_s, u"Py_nb_inplace_and"_s},
+ {u"__ixor__"_s, u"Py_nb_inplace_xor"_s},
+ {u"__ior__"_s, u"Py_nb_inplace_or"_s},
+ {u"__bool__"_s, u"Py_nb_bool"_s},
+ {u"__int__"_s, u"Py_nb_int"_s},
+ {u"__float__"_s, u"Py_nb_float"_s}
+ };
+ return result;
+}
+
+void CppGenerator::writeTypeAsNumberDefinition(TextStream &s, const AbstractMetaClassCPtr &metaClass) const
+{
+ QMap<QString, QString> nb;
+
+ const QList<AbstractMetaFunctionCList> opOverloads = numberProtocolOperators(metaClass);
+ for (const auto &opOverload : opOverloads) {
+ const auto rfunc = opOverload.at(0);
+ QString opName = ShibokenGenerator::pythonOperatorFunctionName(rfunc);
+ nb[opName] = cpythonFunctionName(rfunc);
+ }
+
+ for (auto it = m_nbFuncs.cbegin(), end = m_nbFuncs.cend(); it != end; ++it) {
+ if (!it.value().isEmpty())
+ nb.insert(it.key(), it.value());
+ }
+
+ QString baseName = cpythonBaseName(metaClass);
+
+ if (hasBoolCast(metaClass))
+ nb.insert(u"__bool__"_s, baseName + u"___nb_bool"_s);
+
+ for (auto it = nbFuncs().cbegin(), end = nbFuncs().cend(); it != end; ++it) {
+ const QString &nbName = it.key();
+ const auto nbIt = nb.constFind(nbName);
+ if (nbIt != nb.constEnd())
+ s << pyTypeSlotEntry(it.value(), nbIt.value());
+ }
+}
+
+void CppGenerator::writeTpTraverseFunction(TextStream &s, const AbstractMetaClassCPtr &metaClass)
+{
+ QString baseName = cpythonBaseName(metaClass);
+ s << "static int " << baseName
+ << "_traverse(PyObject *self, visitproc visit, void *arg)\n{\n" << indent
+ << "auto traverseProc = "
+ << pyTypeGetSlot("traverseproc", sbkObjectTypeF, "Py_tp_traverse") << ";\n"
+ << "return traverseProc(self, visit, arg);\n"
+ << outdent << "}\n";
+}
+
+void CppGenerator::writeTpClearFunction(TextStream &s, const AbstractMetaClassCPtr &metaClass)
+{
+ QString baseName = cpythonBaseName(metaClass);
+ s << "static int " << baseName << "_clear(PyObject *self)\n{\n" << indent
+ << "auto clearProc = "
+ << pyTypeGetSlot("inquiry", sbkObjectTypeF, "Py_tp_clear") << ";\n"
+ << "return clearProc(self);\n"
+ << outdent << "}\n";
+}
+
+QString CppGenerator::writeCopyFunction(TextStream &s,
+ TextStream &definitionStream,
+ TextStream &signatureStream,
+ const GeneratorContext &context)
+{
+ const auto metaClass = context.metaClass();
+ const QString className = chopType(cpythonTypeName(metaClass));
+ const QString funcName = className + u"__copy__"_s;
+
+ signatureStream << fullPythonClassName(metaClass) << ".__copy__()\n";
+ definitionStream << PyMethodDefEntry{u"__copy__"_s, funcName, {"METH_NOARGS"_ba}, {}}
+ << ",\n";
+
+ s << "static PyObject *" << funcName << "(PyObject *self)\n"
+ << "{\n" << indent;
+ writeCppSelfDefinition(s, context, ErrorReturn::Default, CppSelfDefinitionFlag::CppSelfAsReference);
+ QString conversionCode;
+ if (!context.forSmartPointer())
+ conversionCode = cpythonToPythonConversionFunction(metaClass);
+ else
+ conversionCode = cpythonToPythonConversionFunction(context.preciseType());
+
+ s << "PyObject *" << PYTHON_RETURN_VAR << " = " << conversionCode
+ << CPP_SELF_VAR << ");\n";
+ writeFunctionReturnErrorCheckSection(s, ErrorReturn::Default);
+ s << "return " << PYTHON_RETURN_VAR << ";\n" << outdent
+ << "}\n\n";
+
+ return funcName;
+}
+
+static inline void writeGetterFunctionStart(TextStream &s, const QString &funcName)
+{
+ s << "static PyObject *" << funcName << "(PyObject *self, void * /* closure */)\n"
+ << "{\n" << indent;
+}
+
+QString CppGenerator::cppFieldAccess(const AbstractMetaField &metaField,
+ const GeneratorContext &context)
+{
+ QString result;
+ QTextStream str(&result);
+ if (avoidProtectedHack() && metaField.isProtected())
+ str << "static_cast<" << context.wrapperName() << " *>(" << CPP_SELF_VAR << ')';
+ else
+ str << CPP_SELF_VAR;
+ str << "->" << metaField.originalName();
+ return result;
+}
+
+void CppGenerator::writeGetterFunction(TextStream &s,
+ const AbstractMetaField &metaField,
+ const GeneratorContext &context)
+{
+ writeGetterFunctionStart(s, cpythonGetterFunctionName(metaField));
+
+ writeCppSelfDefinition(s, context);
+
+ const AbstractMetaType &fieldType = metaField.type();
+ // Force use of pointer to return internal variable memory
+ bool newWrapperSameObject = !fieldType.isConstant() && fieldType.isWrapperType()
+ && !fieldType.isPointer();
+
+ QString cppField = cppFieldAccess(metaField, context);
+
+ if (metaField.generateOpaqueContainer()
+ && fieldType.generateOpaqueContainer()) {
+ const QString creationFunc = opaqueContainerCreationFunc(fieldType);
+ writeOpaqueContainerCreationFuncDecl(s, creationFunc, fieldType);
+ s << "PyObject *pyOut = " << creationFunc
+ << "(&" << cppField << ");\nPy_IncRef(pyOut);\n"
+ << "return pyOut;\n" << outdent << "}\n";
+ return;
+ }
+
+ if (newWrapperSameObject) {
+ cppField.prepend(u"&(");
+ cppField.append(u')');
+ }
+
+ if (fieldType.isCppIntegralPrimitive() || fieldType.isEnum()) {
+ s << getFullTypeNameWithoutModifiers(fieldType) << " cppOut_local = " << cppField << ";\n";
+ cppField = u"cppOut_local"_s;
+ }
+
+ s << "PyObject *pyOut = {};\n";
+ if (newWrapperSameObject) {
+ // Special case colocated field with same address (first field in a struct)
+ s << "if (reinterpret_cast<void *>("
+ << cppField << ") == reinterpret_cast<void *>("
+ << CPP_SELF_VAR << ")) {\n" << indent
+ << "pyOut = reinterpret_cast<PyObject *>(Shiboken::Object::findColocatedChild("
+ << "reinterpret_cast<SbkObject *>(self), "
+ << cpythonTypeNameExt(fieldType) << "));\n"
+ << "if (pyOut != nullptr) {\n" << indent
+ << "Py_IncRef(pyOut);\n"
+ << "return pyOut;\n"
+ << outdent << "}\n";
+ // Check if field wrapper has already been created.
+ s << outdent << "} else if (Shiboken::BindingManager::instance().hasWrapper("
+ << cppField << ")) {" << "\n" << indent
+ << "pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper("
+ << cppField << "));" << "\n"
+ << "Py_IncRef(pyOut);" << "\n"
+ << "return pyOut;" << "\n"
+ << outdent << "}\n";
+ // Create and register new wrapper. We force a pointer conversion also
+ // for wrapped value types so that they refer to the struct member,
+ // avoiding any trouble copying them. Add a parent relationship to
+ // properly notify if the struct is deleted (see protected_test.py,
+ // testProtectedValueTypeProperty()). Note that this has currently
+ // unsolved issues when using temporary Python lists of structs
+ // which can cause elements to be reported deleted in expressions like
+ // "foo.list_of_structs[2].field".
+ s << "pyOut = "
+ << "Shiboken::Object::newObject(" << cpythonTypeNameExt(fieldType)
+ << ", " << cppField << ", false, true);\n"
+ << "Shiboken::Object::setParent(self, pyOut)";
+ } else {
+ s << "pyOut = ";
+ writeToPythonConversion(s, fieldType, metaField.enclosingClass(), cppField);
+ }
+ s << ";\nreturn pyOut;\n" << outdent << "}\n";
+}
+
+// Write a getter for QPropertySpec
+void CppGenerator::writeGetterFunction(TextStream &s,
+ const QPropertySpec &property,
+ const GeneratorContext &context)
+{
+ writeGetterFunctionStart(s, cpythonGetterFunctionName(property, context.metaClass()));
+ writeCppSelfDefinition(s, context);
+ const QString value = "value"_L1;
+ s << "auto " << value << " = " << CPP_SELF_VAR << "->" << property.read() << "();\n"
+ << "auto *pyResult = ";
+ writeToPythonConversion(s, property.type(), context.metaClass(), value);
+ s << ";\nif (" << shibokenErrorsOccurred << " || pyResult == nullptr) {\n"
+ << indent << "Py_XDECREF(pyResult);\nreturn {};\n" << outdent
+ << "}\nreturn pyResult;\n" << outdent << "}\n\n";
+}
+
+// Write setter function preamble (type checks on "pyIn")
+void CppGenerator::writeSetterFunctionPreamble(TextStream &s,
+ const QString &name,
+ const QString &funcName,
+ const AbstractMetaType &type,
+ const GeneratorContext &context)
+{
+ s << "static int " << funcName << "(PyObject *self, PyObject *pyIn, void * /* closure */)\n"
+ << "{\n" << indent;
+
+ writeCppSelfDefinition(s, context, ErrorReturn::Zero);
+
+ s << "if (pyIn == " << NULL_PTR << ") {\n" << indent
+ << "Shiboken::Errors::setInvalidTypeDeletion(\"" << name << "\");\n"
+ << "return -1;\n"
+ << outdent << "}\n";
+
+ s << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR << ";\n"
+ << "if (!";
+ writeTypeCheck(s, type, u"pyIn"_s, isNumber(type.typeEntry()));
+ s << ") {\n" << indent
+ << "Shiboken::Errors::setSetterTypeError(\"" << name << "\", \""
+ << type.name() << "\");\n"
+ << "return -1;\n"
+ << outdent << "}\n\n";
+}
+
+void CppGenerator::writeSetterFunction(TextStream &s,
+ const AbstractMetaField &metaField,
+ const GeneratorContext &context)
+{
+ const AbstractMetaType &fieldType = metaField.type();
+ writeSetterFunctionPreamble(s, metaField.name(), cpythonSetterFunctionName(metaField),
+ fieldType, context);
+
+
+ const QString cppField = cppFieldAccess(metaField, context);
+
+ if (fieldType.isCppIntegralPrimitive() || fieldType.typeEntry()->isEnum()
+ || fieldType.typeEntry()->isFlags()) {
+ s << "auto cppOut_local = " << cppField << ";\n"
+ << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut_local);\n"
+ << cppField << " = cppOut_local";
+ } else {
+ if (fieldType.isPointerToConst())
+ s << "const ";
+ s << "auto " << QByteArray(fieldType.indirections(), '*')
+ << "&cppOut_ptr = " << cppField << ";\n"
+ << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut_ptr)";
+ }
+ s << ";\n\n";
+
+ if (fieldType.isPointerToWrapperType()) {
+ s << "Shiboken::Object::keepReference(reinterpret_cast<SbkObject *>(self), \""
+ << metaField.name() << "\", pyIn);\n";
+ }
+
+ s << "return 0;\n" << outdent << "}\n";
+}
+
+// Write a setter for QPropertySpec
+void CppGenerator::writeSetterFunction(TextStream &s,
+ const QPropertySpec &property,
+ const GeneratorContext &context)
+{
+ writeSetterFunctionPreamble(s,
+ property.name(),
+ cpythonSetterFunctionName(property, context.metaClass()),
+ property.type(), context);
+
+ s << "auto cppOut = " << CPP_SELF_VAR << "->" << property.read() << "();\n"
+ << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut);\n"
+ << "if (" << shibokenErrorsOccurred << ")\n" << indent
+ << "return -1;\n" << outdent
+ << CPP_SELF_VAR << "->" << property.write() << "(cppOut);\n"
+ << "return 0;\n" << outdent << "}\n\n";
+}
+
+void CppGenerator::writeRichCompareFunctionHeader(TextStream &s,
+ const QString &baseName,
+ const GeneratorContext &context)
+{
+ s << "static PyObject * ";
+ s << baseName << "_richcompare(PyObject *self, PyObject *" << PYTHON_ARG
+ << ", int op)\n{\n" << indent;
+ writeCppSelfDefinition(s, context, ErrorReturn::Default, CppSelfDefinitionFlag::CppSelfAsReference);
+ s << sbkUnusedVariableCast(CPP_SELF_VAR)
+ << "PyObject *" << PYTHON_RETURN_VAR << "{};\n"
+ << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR << ";\n"
+ << sbkUnusedVariableCast(PYTHON_TO_CPP_VAR) << '\n';
+}
+
+void CppGenerator::writeRichCompareFunction(TextStream &s,
+ const GeneratorContext &context) const
+{
+ const auto metaClass = context.metaClass();
+ QString baseName = cpythonBaseName(metaClass);
+ writeRichCompareFunctionHeader(s, baseName, context);
+
+ s << "switch (op) {\n" << indent;
+ const QList<AbstractMetaFunctionCList> &groupedFuncs =
+ filterGroupedOperatorFunctions(metaClass, OperatorQueryOption::ComparisonOp);
+ for (const AbstractMetaFunctionCList &overloads : groupedFuncs) {
+ const auto rfunc = overloads[0];
+
+ const auto op = rfunc->comparisonOperatorType().value();
+ s << "case " << AbstractMetaFunction::pythonRichCompareOpCode(op)
+ << ":\n" << indent;
+
+ int alternativeNumericTypes = 0;
+ for (const auto &func : overloads) {
+ if (!func->isStatic() &&
+ ShibokenGenerator::isNumber(func->arguments().at(0).type().typeEntry()))
+ alternativeNumericTypes++;
+ }
+
+ bool first = true;
+ OverloadData overloadData(overloads, api());
+ const OverloadDataList &nextOverloads = overloadData.children();
+ for (const auto &od : nextOverloads) {
+ const auto func = od->referenceFunction();
+ if (func->isStatic())
+ continue;
+ auto argType = getArgumentType(func, 0);
+ if (!first) {
+ s << " else ";
+ } else {
+ first = false;
+ }
+ s << "if (";
+ writeTypeCheck(s, argType, PYTHON_ARG,
+ alternativeNumericTypes == 1 || isPyInt(argType));
+ s << ") {\n" << indent
+ << "// " << func->signature() << '\n';
+ writeArgumentConversion(s, argType, CPP_ARG0,
+ PYTHON_ARG, ErrorReturn::Default,
+ metaClass,
+ QString(), func->isUserAdded());
+ // If the function is user added, use the inject code
+ bool generateOperatorCode = true;
+ if (func->isUserAdded()) {
+ CodeSnipList snips = func->injectedCodeSnips();
+ if (!snips.isEmpty()) {
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionAny,
+ TypeSystem::TargetLangCode, func,
+ false /* uses PyArgs */, &func->arguments().constLast());
+ generateOperatorCode = false;
+ }
+ }
+ if (generateOperatorCode) {
+ if (!func->isVoid())
+ s << func->type().cppSignature() << " " << CPP_RETURN_VAR << " = ";
+ // expression
+ if (func->isPointerOperator())
+ s << '&';
+ s << CPP_SELF_VAR << ' '
+ << AbstractMetaFunction::cppComparisonOperator(op) << " (";
+ auto generatorArg = GeneratorArgument::fromMetaType(argType);
+ if (generatorArg.indirections != 0)
+ s << QByteArray(generatorArg.indirections, '*');
+ s << CPP_ARG0 << ");\n"
+ << PYTHON_RETURN_VAR << " = ";
+ if (!func->isVoid()) {
+ writeToPythonConversion(s, func->type(), metaClass,
+ CPP_RETURN_VAR);
+ } else {
+ s << "Py_None;\n" << "Py_INCREF(Py_None)";
+ }
+ s << ";\n";
+ }
+ s << outdent << '}';
+ }
+
+ s << " else {\n";
+ if (op == AbstractMetaFunction::OperatorEqual ||
+ op == AbstractMetaFunction::OperatorNotEqual) {
+ s << indent << PYTHON_RETURN_VAR << " = "
+ << (op == AbstractMetaFunction::OperatorEqual ? "Py_False" : "Py_True") << ";\n"
+ << "Py_INCREF(" << PYTHON_RETURN_VAR << ");\n" << outdent;
+ } else {
+ s << indent << "return Shiboken::returnFromRichCompare("
+ << PYTHON_RETURN_VAR << ");\n" << outdent;
+ }
+ s << "}\n\n";
+
+ s << "break;\n" << outdent;
+ }
+ s << "default:\n" << indent
+ << richCompareComment
+ << "return FallbackRichCompare(self, " << PYTHON_ARG << ", op);\n"
+ << outdent << outdent << "}\n\n"
+ << "return Shiboken::returnFromRichCompare(" << PYTHON_RETURN_VAR << ");\n" << outdent
+ << "}\n\n";
+}
+
+// Return a flag combination for PyMethodDef
+QByteArrayList CppGenerator::methodDefinitionParameters(const OverloadData &overloadData) const
+{
+ const bool usePyArgs = overloadData.pythonFunctionWrapperUsesListOfArguments();
+ int min = overloadData.minArgs();
+ int max = overloadData.maxArgs();
+
+ QByteArrayList result;
+ if ((min == max) && (max < 2) && !usePyArgs) {
+ result.append(max == 0 ? QByteArrayLiteral("METH_NOARGS")
+ : QByteArrayLiteral("METH_O"));
+ } else {
+ result.append(QByteArrayLiteral("METH_VARARGS"));
+ if (overloadData.hasArgumentWithDefaultValue())
+ result.append(QByteArrayLiteral("METH_KEYWORDS"));
+ }
+ // METH_STATIC causes a crash when used for global functions (also from
+ // invisible namespaces).
+ const auto ownerClass = overloadData.referenceFunction()->ownerClass();
+ if (ownerClass
+ && !invisibleTopNamespaces().contains(std::const_pointer_cast<AbstractMetaClass>(ownerClass))) {
+ if (overloadData.hasStaticFunction())
+ result.append(QByteArrayLiteral("METH_STATIC"));
+ if (overloadData.hasClassMethod())
+ result.append(QByteArrayLiteral("METH_CLASS"));
+ }
+ return result;
+}
+
+QList<PyMethodDefEntry>
+ CppGenerator::methodDefinitionEntries(const OverloadData &overloadData) const
+{
+
+ const QStringList names = overloadData.referenceFunction()->definitionNames();
+ const QString funcName = cpythonFunctionName(overloadData.referenceFunction());
+ const QByteArrayList parameters = methodDefinitionParameters(overloadData);
+
+ QList<PyMethodDefEntry> result;
+ result.reserve(names.size());
+ for (const auto &name : names)
+ result.append({name, funcName, parameters, {}});
+ return result;
+}
+
+QString CppGenerator::pythonSignature(const AbstractMetaType &type) const
+{
+ if (type.isSmartPointer() && !type.instantiations().isEmpty()) {
+ const auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(type.typeEntry());
+ const auto instantiationTe = type.instantiations().constFirst().typeEntry();
+ if (auto opt = api().findSmartPointerInstantiation(ste, instantiationTe))
+ return opt->specialized->typeEntry()->qualifiedTargetLangName();
+ }
+ return type.pythonSignature();
+}
+
+// Format the type signature of a function parameter
+QString CppGenerator::signatureParameter(const AbstractMetaArgument &arg) const
+{
+ QString result;
+ QTextStream s(&result);
+
+ auto metaType = arg.type();
+ if (auto viewOn = metaType.viewOn())
+ metaType = *viewOn;
+ s << arg.name() << ':';
+
+ QStringList signatures(pythonSignature(metaType));
+
+ // Implicit conversions (C++): Check for converting constructors
+ // "QColor(Qt::GlobalColor)" or conversion operators
+ const AbstractMetaFunctionCList conversions =
+ api().implicitConversions(metaType);
+ for (const auto &f : conversions) {
+ 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 << pythonSignature(f->arguments().constFirst().type());
+ }
+ } else if (f->isConversionOperator()) {
+ signatures << f->ownerClass()->fullName();
+ }
+ }
+
+ const qsizetype size = signatures.size();
+ if (size > 1)
+ s << "typing.Union[";
+ for (qsizetype i = 0; i < size; ++i) {
+ if (i > 0)
+ s << ", ";
+ s << signatures.at(i);
+ }
+ if (size > 1)
+ s << ']';
+
+ return result;
+}
+
+void CppGenerator::writeSignatureInfo(TextStream &s, const OverloadData &overloadData) const
+{
+ const auto rfunc = overloadData.referenceFunction();
+ QString funcName = fullPythonFunctionName(rfunc, false);
+
+ int idx = overloadData.overloads().length() - 1;
+ bool multiple = idx > 0;
+
+ for (const auto &f : overloadData.overloads()) {
+ QStringList args;
+ // PYSIDE-1328: `self`-ness cannot be computed in Python because there are mixed cases.
+ // Toplevel functions like `PySide6.QtCore.QEnum` are always self-less.
+ if (!(f->isStatic()) && f->ownerClass())
+ args << PYTHON_SELF_VAR;
+ const auto &arguments = f->arguments();
+ for (qsizetype i = 0, size = arguments.size(); i < size; ++i) {
+ const auto n = i + 1;
+ const auto &arg = arguments.at(i);
+ if (!f->argumentRemoved(n)) {
+ QString t = f->pyiTypeReplaced(n);
+ if (t.isEmpty()) {
+ t = signatureParameter(arg);
+ } else {
+ t.prepend(u':');
+ t.prepend(arg.name());
+ }
+ QString defaultValue = arg.defaultValueExpression();
+ if (!defaultValue.isEmpty())
+ t += u'=' + defaultValue.replace(u"::"_s, u"."_s);
+ args.append(t);
+ }
+ }
+
+ // mark the multiple signatures as such, to make it easier to generate different code
+ if (multiple)
+ s << idx-- << ':';
+ s << funcName << '(' << args.join(u',') << ')';
+
+ QString returnType = f->pyiTypeReplaced(0); // pyi or modified type
+ if (returnType.isEmpty() && !f->isVoid())
+ returnType = pythonSignature(f->type());
+ if (!returnType.isEmpty())
+ s << "->" << returnType;
+
+ s << '\n';
+ }
+}
+
+void CppGenerator::writeEnumsInitialization(TextStream &s, AbstractMetaEnumList &enums)
+{
+ if (enums.isEmpty())
+ return;
+ bool preambleWritten = false;
+ bool etypeUsed = false;
+
+ for (const AbstractMetaEnum &cppEnum : std::as_const(enums)) {
+ if (cppEnum.isPrivate())
+ continue;
+ if (!preambleWritten) {
+ s << "// Initialization of enums.\n"
+ << "Shiboken::AutoDecRef tpDict{};\n"
+ << "PyTypeObject *EType{};\n\n";
+ preambleWritten = true;
+ }
+ ConfigurableScope configScope(s, cppEnum.typeEntry());
+ etypeUsed |= writeEnumInitialization(s, cppEnum);
+ }
+ if (preambleWritten && !etypeUsed)
+ s << sbkUnusedVariableCast("EType");
+}
+
+static qsizetype maxLineLength(const QStringList &list)
+{
+ qsizetype result = 0;
+ for (const auto &s : list) {
+ if (auto len = s.size(); len > result)
+ result = len;
+ }
+ return result;
+}
+
+bool CppGenerator::writeEnumInitialization(TextStream &s, const AbstractMetaEnum &cppEnum)
+{
+ const auto enclosingClass = cppEnum.targetLangEnclosingClass();
+ const bool hasUpperEnclosingClass = enclosingClass
+ && enclosingClass->targetLangEnclosingClass();
+ EnumTypeEntryCPtr enumTypeEntry = cppEnum.typeEntry();
+ QString enclosingObjectVariable;
+ if (enclosingClass)
+ enclosingObjectVariable = cpythonTypeName(enclosingClass);
+ else if (hasUpperEnclosingClass)
+ enclosingObjectVariable = u"enclosingClass"_s;
+ else
+ enclosingObjectVariable = u"module"_s;
+
+ s << "// Initialization of ";
+ s << (cppEnum.isAnonymous() ? "anonymous enum identified by enum value" : "enum");
+ s << " '" << cppEnum.name() << "'.\n";
+
+ const QString userType = cppEnum.typeEntry()->cppType();
+ const bool isSigned = cppEnum.isSigned() && !userType.contains(u"unsigned"_s);
+ const bool isAccessible = !avoidProtectedHack() || !cppEnum.isProtected();
+ const auto enumValues = cppEnum.nonRejectedValues();
+
+ const QString prefix = cppEnum.name();
+
+ const QString intType = userType.isEmpty() ? cppEnum.underlyingType() : userType;
+
+ // Create a list of values
+ const QString initializerValues = prefix + u"_InitializerValues"_s;
+ const QString initializerName = prefix + u"_Initializer"_s;
+
+ // Build maybe array of enum names.
+ if (cppEnum.enumKind() != AnonymousEnum) {
+ s << "const char *" << initializerName << "[] = {\n" << indent;
+ for (const auto &enumValue : enumValues) {
+ QString name = mangleName(enumValue.name());
+ s << '\"' << name << "\",\n";
+ }
+ s << "nullptr};\n" << outdent;
+ }
+
+ int targetHexLen = 0;
+ QString usedIntType = userType;
+ if (usedIntType.isEmpty()) {
+ const int usedBits = cppEnum.usedBits();
+ targetHexLen = usedBits / 4;
+ usedIntType = AbstractMetaEnum::intTypeForSize(usedBits, cppEnum.isSigned());
+ }
+
+ if (usedIntType != intType)
+ s << "// \"" << usedIntType << "\" used instead of \"" << intType << "\"\n";
+
+ // Calculating formatting columns
+ QString enumValuePrefix;
+ if (isAccessible) {
+ if (cppEnum.enclosingClass())
+ enumValuePrefix += cppEnum.enclosingClass()->qualifiedCppName() + u"::"_s;
+ if (!cppEnum.isAnonymous())
+ enumValuePrefix += cppEnum.name() + u"::"_s;
+ }
+
+ // Build array of enum values
+ if (enumValues.isEmpty()) {
+ s << "const " << usedIntType << " *" << initializerValues << "{};\n";
+ } else {
+ QStringList values;
+ values.reserve(enumValues.size());
+ s << "constexpr " << usedIntType << ' ' << initializerValues << "[] = {\n" << indent;
+ for (qsizetype idx = 0, last = enumValues.size() - 1; idx <= last; ++idx) {
+ const auto &enumValue = enumValues.at(idx);
+ QString line = usedIntType + u'(' + (isAccessible
+ ? enumValuePrefix + enumValue.name()
+ : enumValue.value().toString()) + u')';
+ if (idx != last)
+ line += u',';
+ values.append(line);
+ }
+
+ const auto len = maxLineLength(values) + 1;
+ for (qsizetype idx = 0, size = enumValues.size(); idx < size; ++idx) {
+ const auto &enumValue = enumValues.at(idx).value();
+ const char *numberSpace = enumValue.isNegative() ? " " : " ";
+ s << values.at(idx) << Pad(' ', len - values.at(idx).size())
+ << "//" << numberSpace << enumValue.toHex(targetHexLen)
+ << numberSpace << enumValue.toString() << '\n';
+ }
+ s << "};\n" << outdent;
+ }
+
+ // Build initialization of anonymous enums
+ if (cppEnum.enumKind() == AnonymousEnum) {
+ int idx = 0;
+ for (const auto &enumValue : enumValues) {
+ const QString mangledName = mangleName(enumValue.name());
+ const QString pyValue = initializerValues + u'[' + QString::number(idx++) + u']';
+ if (enclosingClass || hasUpperEnclosingClass) {
+ s << "tpDict.reset(PepType_GetDict(reinterpret_cast<PyTypeObject *>("
+ << enclosingObjectVariable << ")));\n"
+ << "PyDict_SetItemString(tpDict.object(), \"" << mangledName << "\",\n"
+ << indent << (isSigned ? "PyLong_FromLongLong" : "PyLong_FromUnsignedLongLong")
+ << "(" << pyValue << "));\n" << outdent;
+ } else {
+ s << "PyModule_AddObject(module, \"" << mangledName << "\",\n" << indent
+ << (isSigned ? "PyLong_FromLongLong" : "PyLong_FromUnsignedLongLong") << "("
+ << pyValue << "));\n" << outdent;
+ }
+ }
+ }
+
+ bool etypeUsed = false;
+
+ QString enumVarTypeObj = cpythonTypeNameExtSet(enumTypeEntry);
+ if (!cppEnum.isAnonymous()) {
+ int packageLevel = packageName().count(u'.') + 1;
+ s << "EType = Shiboken::Enum::"
+ << "createPythonEnum"
+ << '(' << enclosingObjectVariable << ",\n" << indent
+ << '"' << packageLevel << ':' << getClassTargetFullName(cppEnum) << "\",\n"
+ << initializerName << ", " << initializerValues << ");\n" << outdent
+ << enumVarTypeObj << " = EType;\n";
+ etypeUsed = true;
+ }
+
+ if (cppEnum.typeEntry()->flags()) {
+ s << "// PYSIDE-1735: Mapping the flags class to the same enum class.\n"
+ << cpythonTypeNameExtSet(cppEnum.typeEntry()->flags()) << " =\n"
+ << indent << "EType;\n" << outdent;
+ }
+ writeEnumConverterInitialization(s, cppEnum);
+
+ s << "// End of '" << cppEnum.name() << "' enum";
+ if (cppEnum.typeEntry()->flags())
+ s << "/flags";
+ s << ".\n\n";
+
+ return etypeUsed;
+}
+
+void CppGenerator::writeSignalInitialization(TextStream &s, const AbstractMetaClassCPtr &metaClass)
+{
+ // Try to check something and print some warnings
+ const auto &signalFuncs = metaClass->cppSignalFunctions();
+ for (const auto &cppSignal : signalFuncs) {
+ if (cppSignal->declaringClass() != metaClass)
+ continue;
+ const AbstractMetaArgumentList &arguments = cppSignal->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+ AbstractMetaType metaType = arg.type();
+ const QByteArray origType =
+ QMetaObject::normalizedType(qPrintable(metaType.originalTypeDescription()));
+ const QByteArray cppSig =
+ QMetaObject::normalizedType(qPrintable(metaType.cppSignature()));
+ if ((origType != cppSig) && (!metaType.isFlags())) {
+ qCWarning(lcShiboken).noquote().nospace()
+ << "Typedef used on signal " << metaClass->qualifiedCppName() << "::"
+ << cppSignal->signature();
+ }
+ }
+ }
+
+ s << "PySide::Signal::registerSignals(pyType, &" << m_gsp
+ << metaClass->qualifiedCppName() << "::staticMetaObject);\n";
+}
+
+QString CppGenerator::getSimpleClassInitFunctionName(const AbstractMetaClassCPtr &metaClass)
+{
+ QString initFunctionName;
+ // Disambiguate namespaces per module to allow for extending them.
+ if (metaClass->isNamespace())
+ initFunctionName += moduleName();
+ initFunctionName += metaClass->qualifiedCppName();
+ initFunctionName.replace(u"::"_s, u"_"_s);
+ return initFunctionName;
+}
+
+QString
+ CppGenerator::getSimpleClassStaticFieldsInitFunctionName(const AbstractMetaClassCPtr &metaClass)
+{
+ return u"init_"_s + getSimpleClassInitFunctionName(metaClass)
+ + u"StaticFields"_s;
+}
+
+QString CppGenerator::getInitFunctionName(const GeneratorContext &context)
+{
+ return getSimpleClassInitFunctionName(context.metaClass());
+}
+
+void CppGenerator::writeSignatureStrings(TextStream &s,
+ const QString &signatures,
+ const QString &arrayName,
+ const char *comment)
+{
+ s << "// The signatures string for the " << comment << ".\n"
+ << "// Multiple signatures have their index \"n:\" in front.\n"
+ << "static const char *" << arrayName << "_SignatureStrings[] = {\n" << indent;
+ const auto lines = QStringView{signatures}.split(u'\n', Qt::SkipEmptyParts);
+ for (auto line : lines) {
+ // must anything be escaped?
+ if (line.contains(u'"') || line.contains(u'\\'))
+ s << "R\"CPP(" << line << ")CPP\",\n";
+ else
+ s << '"' << line << "\",\n";
+ }
+ s << NULL_PTR << "}; // Sentinel\n" << outdent << '\n';
+}
+
+// Return the class name for which to invoke the destructor
+QString CppGenerator::destructorClassName(const AbstractMetaClassCPtr &metaClass,
+ const GeneratorContext &classContext)
+{
+ if (metaClass->isNamespace() || metaClass->hasPrivateDestructor())
+ return {};
+ if (classContext.forSmartPointer())
+ return classContext.effectiveClassName();
+ const bool isValue = metaClass->typeEntry()->isValue();
+ const bool hasProtectedDestructor = metaClass->hasProtectedDestructor();
+ if (((avoidProtectedHack() && hasProtectedDestructor) || isValue)
+ && classContext.useWrapper()) {
+ return classContext.wrapperName();
+ }
+ if (avoidProtectedHack() && hasProtectedDestructor)
+ return {}; // Cannot call (happens with "disable-wrapper").
+ return metaClass->qualifiedCppName();
+}
+
+// Return the base type entries for introduceWrapperType()
+static ComplexTypeEntryCList pyBaseTypeEntries(const AbstractMetaClassCPtr &metaClass)
+{
+ ComplexTypeEntryCList result;
+ if (metaClass->isNamespace()) {
+ if (auto extended = metaClass->extendedNamespace())
+ result.append(extended->typeEntry());
+ return result;
+ }
+
+ const auto &baseClasses = metaClass->typeSystemBaseClasses();
+ for (auto base : baseClasses) {
+ for (; base != nullptr; base = base->baseClass()) { // Find a type that is not disabled.
+ const auto ct = base->typeEntry()->codeGeneration();
+ if (ct == TypeEntry::GenerateCode || ct == TypeEntry::GenerateForSubclass)
+ break;
+ }
+ result.append(base->typeEntry());
+ }
+ return result;
+}
+
+// Return the base type strings for introduceWrapperType()
+QStringList CppGenerator::pyBaseTypes(const AbstractMetaClassCPtr &metaClass)
+{
+ const auto &baseEntries = pyBaseTypeEntries(metaClass);
+ QStringList result;
+ for (const auto &baseEntry : baseEntries)
+ result.append("reinterpret_cast<PyObject *>("_L1 + cpythonTypeNameExt(baseEntry) + u')');
+ if (result.isEmpty()) // no base classes -> SbkObjectType.
+ result.append(sbkObjectTypeF);
+ 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,
+ const QString &signatures) const
+{
+ ComplexTypeEntryCPtr classTypeEntry = metaClass->typeEntry();
+
+ AbstractMetaClassCPtr enc = metaClass->targetLangEnclosingClass();
+ QString enclosingObjectVariable = enc ? u"enclosingClass"_s : u"module"_s;
+
+ QString pyTypeName = cpythonTypeName(metaClass);
+ QString initFunctionName = getInitFunctionName(classContext);
+
+ // PYSIDE-510: Create a signatures string for the introspection feature.
+ writeSignatureStrings(s, signatures, initFunctionName, "functions");
+ s << "PyTypeObject *init_" << initFunctionName
+ << "(PyObject *" << enclosingObjectVariable << ")\n{\n" << indent;
+
+ const QString globalTypeVarExpr = !classContext.forSmartPointer()
+ ? cpythonTypeNameExtSet(classTypeEntry)
+ : cpythonTypeNameExtSet(classContext.preciseType());
+ s << "if (" << globalTypeVarExpr << " != nullptr)\n" << indent
+ << "return " << globalTypeVarExpr << ";\n\n" << outdent;
+
+ // Multiple inheritance
+ QString pyTypeBasesVariable = chopType(pyTypeName) + u"_Type_bases"_s;
+ const QStringList pyBases = pyBaseTypes(metaClass);
+ s << "Shiboken::AutoDecRef " << pyTypeBasesVariable << "(PyTuple_Pack("
+ << pyBases.size() << ",\n" << indent;
+ for (qsizetype i = 0, size = pyBases.size(); i < size; ++i) {
+ if (i)
+ s << ",\n";
+ s << pyBases.at(i);
+ }
+ s << "));\n\n" << outdent;
+
+ // Create type and insert it in the module or enclosing class.
+ const QString typePtr = u"_"_s + chopType(pyTypeName)
+ + u"_Type"_s;
+
+ s << typePtr << " = Shiboken::ObjectType::introduceWrapperType(\n" << indent;
+ // 1:enclosingObject
+ s << enclosingObjectVariable << ",\n";
+
+ // 2:typeName
+ s << "\"" << metaClass->name() << "\",\n";
+
+ // 3:originalName
+ s << "\"";
+ if (!classContext.forSmartPointer()) {
+ s << metaClass->qualifiedCppName();
+ if (classTypeEntry->isObject())
+ s << '*';
+ } else {
+ s << classContext.preciseType().cppSignature();
+ }
+
+ s << "\",\n";
+ // 4:typeSpec
+ s << '&' << chopType(pyTypeName) << "_spec,\n";
+
+ // 5:cppObjDtor
+ QString dtorClassName = destructorClassName(metaClass, classContext);
+ if (dtorClassName.isEmpty())
+ s << "nullptr,\n";
+ else
+ s << "&Shiboken::callCppDestructor< " << globalScopePrefix(classContext)
+ << dtorClassName << " >,\n";
+
+ // 7:baseTypes
+ s << pyTypeBasesVariable << ".object()," << '\n';
+
+ // 8:wrapperflags
+ QByteArrayList wrapperFlags;
+ if (enc)
+ wrapperFlags.append(QByteArrayLiteral("Shiboken::ObjectType::WrapperFlags::InnerClass"));
+ if (metaClass->deleteInMainThread())
+ wrapperFlags.append(QByteArrayLiteral("Shiboken::ObjectType::WrapperFlags::DeleteInMainThread"));
+ if (classTypeEntry->isValue())
+ wrapperFlags.append("Shiboken::ObjectType::WrapperFlags::Value"_ba);
+ if (wrapperFlags.isEmpty())
+ s << '0';
+ else
+ s << wrapperFlags.join(" | ");
+
+ s << outdent << ");\nauto *pyType = " << pyTypeName << "; // references "
+ << typePtr << "\n"
+ << "InitSignatureStrings(pyType, " << initFunctionName << "_SignatureStrings);\n";
+
+ if (usePySideExtensions() && !classContext.forSmartPointer())
+ s << "SbkObjectType_SetPropertyStrings(pyType, "
+ << chopType(pyTypeName) << "_PropertyStrings);\n";
+ s << globalTypeVarExpr << " = pyType;\n\n";
+
+ // Register conversions for the type.
+ writeConverterRegister(s, metaClass, classContext);
+ s << '\n';
+
+ if (classContext.forSmartPointer())
+ writeSmartPointerConverterInitialization(s, classContext.preciseType());
+
+ // class inject-code target/beginning
+ if (!classTypeEntry->codeSnips().isEmpty()) {
+ writeClassCodeSnips(s, classTypeEntry->codeSnips(),
+ TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode,
+ classContext);
+ s << '\n';
+ }
+
+ // Fill multiple inheritance data, if needed.
+ const auto miClass = getMultipleInheritingClass(metaClass);
+ if (miClass) {
+ s << "MultipleInheritanceInitFunction func = ";
+ if (miClass == metaClass) {
+ s << multipleInheritanceInitializerFunctionName(miClass) << ";\n";
+ } else {
+ s << "Shiboken::ObjectType::getMultipleInheritanceFunction("
+ << cpythonTypeNameExt(miClass->typeEntry()) << ");\n";
+ }
+ s << "Shiboken::ObjectType::setMultipleInheritanceFunction("
+ << cpythonTypeName(metaClass) << ", func);\n"
+ << "Shiboken::ObjectType::setCastFunction(" << cpythonTypeName(metaClass)
+ << ", &" << cpythonSpecialCastFunctionName(metaClass) << ");\n";
+ }
+
+ // Set typediscovery struct or fill the struct of another one
+ if (needsTypeDiscoveryFunction(metaClass)) {
+ s << "Shiboken::ObjectType::setTypeDiscoveryFunctionV2(\n" << indent
+ << cpythonTypeName(metaClass)
+ << ", &" << cpythonBaseName(metaClass) << "_typeDiscovery);" << outdent << "\n\n";
+ }
+
+ AbstractMetaEnumList classEnums = metaClass->enums();
+ metaClass->getEnumsFromInvisibleNamespacesToBeGenerated(&classEnums);
+
+ if (!classContext.forSmartPointer() && !classEnums.isEmpty())
+ s << "// Pass the ..._EnumFlagInfo to the class.\n"
+ << "SbkObjectType_SetEnumFlagInfo(pyType, " << chopType(pyTypeName)
+ << "_EnumFlagInfo);\n\n";
+ writeEnumsInitialization(s, classEnums);
+
+ if (metaClass->hasSignals())
+ writeSignalInitialization(s, metaClass);
+
+ // class inject-code target/end
+ if (!classTypeEntry->codeSnips().isEmpty()) {
+ s << '\n';
+ writeClassCodeSnips(s, classTypeEntry->codeSnips(),
+ TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode,
+ classContext);
+ }
+
+ if (usePySideExtensions()) {
+ if (avoidProtectedHack() && classContext.useWrapper())
+ s << classContext.wrapperName() << "::pysideInitQtMetaTypes();\n";
+ else
+ writeInitQtMetaTypeFunctionBody(s, classContext);
+ }
+
+ if (usePySideExtensions() && isQObject(metaClass)) {
+ s << "Shiboken::ObjectType::setSubTypeInitHook(pyType, &PySide::initQObjectSubType);\n"
+ << "PySide::initDynamicMetaObject(pyType, &" << m_gsp
+ << metaClass->qualifiedCppName() << "::staticMetaObject, sizeof("
+ << (shouldGenerateCppWrapper(metaClass)
+ ? wrapperName(metaClass) : getFullTypeName(metaClass))
+ << "));\n";
+ }
+
+ s << "\nreturn pyType;\n" << outdent << "}\n";
+}
+
+void CppGenerator::writeStaticFieldInitialization(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass)
+{
+ // cpythonTypeName == "Sbk_QRhiShaderResourceBinding_Data_TypeF"
+ QString name = cpythonTypeName(metaClass);
+ const auto parts = QStringView{name}.split(u'_', Qt::SkipEmptyParts);
+ if (parts.size() < 4) {
+ s << "\nPyTypeObject *" << getSimpleClassStaticFieldsInitFunctionName(metaClass)
+ << "(PyObject *module)\n{\n" << indent
+ << "auto *obType = PyObject_GetAttrString(module, \"" << metaClass->name() << "\");\n"
+ << "auto *type = reinterpret_cast<PyTypeObject *>(obType);\n"
+ << "Shiboken::AutoDecRef dict(PepType_GetDict(type));\n";
+ } else {
+ s << "\nPyTypeObject *" << getSimpleClassStaticFieldsInitFunctionName(metaClass)
+ << "(PyObject *module)\n{\n" << indent
+ << "auto *obContainerType = PyObject_GetAttrString(module, \""
+ << parts.at(1) << "\");\n"
+ << "auto *obType = PyObject_GetAttrString(obContainerType, \""
+ << parts.at(2) << "\");\n"
+ << "auto *type = reinterpret_cast<PyTypeObject *>(obType);\n"
+ << "Shiboken::AutoDecRef dict(PepType_GetDict(type));\n";
+ }
+ for (const AbstractMetaField &field : metaClass->fields()) {
+ if (field.isStatic()) {
+ s << "PyDict_SetItemString(dict, \"" << field.name()
+ << "\",\n ";
+ writeToPythonConversion(s, field.type(), metaClass, field.qualifiedCppName());
+ s << ");\n";
+ }
+ }
+ s << "return type;\n" << outdent << "}\n";
+}
+
+enum class QtRegisterMetaType
+{
+ None, Pointer, Value
+};
+
+static bool hasQtMetaTypeRegistrationSpec(const AbstractMetaClassCPtr &c)
+{
+ return c->typeEntry()->qtMetaTypeRegistration() !=
+ TypeSystem::QtMetaTypeRegistration::Unspecified;
+}
+
+// Returns if and how to register the Qt meta type. By default, "pointer" for
+// non-QObject object types and "value" for non-abstract, default-constructible
+// value types.
+QtRegisterMetaType qtMetaTypeRegistration(const AbstractMetaClassCPtr &c)
+{
+ if (c->isNamespace())
+ return QtRegisterMetaType::None;
+
+ // Specified in type system?
+ const bool isObject = c->isObjectType();
+ switch (c->typeEntry()->qtMetaTypeRegistration()) {
+ case TypeSystem::QtMetaTypeRegistration::Disabled:
+ return QtRegisterMetaType::None;
+ case TypeSystem::QtMetaTypeRegistration::Enabled:
+ case TypeSystem::QtMetaTypeRegistration::BaseEnabled:
+ return isObject ? QtRegisterMetaType::Pointer : QtRegisterMetaType::Value;
+ case TypeSystem::QtMetaTypeRegistration::Unspecified:
+ break;
+ }
+
+ // Is there a "base" specification in some base class, meaning only the
+ // base class is to be registered?
+ if (auto base = recurseClassHierarchy(c, hasQtMetaTypeRegistrationSpec)) {
+ const auto baseSpec = base->typeEntry()->qtMetaTypeRegistration();
+ if (baseSpec == TypeSystem::QtMetaTypeRegistration::BaseEnabled)
+ return QtRegisterMetaType::None;
+ }
+
+ // Default.
+ if (isObject)
+ return isQObject(c) ? QtRegisterMetaType::None : QtRegisterMetaType::Pointer;
+
+ return !c->isAbstract() && c->isDefaultConstructible()
+ ? QtRegisterMetaType::Value : QtRegisterMetaType::None;
+}
+
+void CppGenerator::writeInitQtMetaTypeFunctionBody(TextStream &s, const GeneratorContext &context)
+{
+ const auto metaClass = context.metaClass();
+ // Gets all class name variants used on different possible scopes
+ QStringList nameVariants;
+ if (!context.forSmartPointer())
+ nameVariants << metaClass->name();
+ else
+ nameVariants << context.preciseType().cppSignature();
+
+ AbstractMetaClassCPtr enclosingClass = metaClass->enclosingClass();
+ while (enclosingClass) {
+ if (enclosingClass->typeEntry()->generateCode())
+ nameVariants << (enclosingClass->name() + u"::"_s + nameVariants.constLast());
+ enclosingClass = enclosingClass->enclosingClass();
+ }
+
+ QString className;
+ if (!context.forSmartPointer())
+ className = metaClass->qualifiedCppName();
+ else
+ className = context.preciseType().cppSignature();
+
+ // Register meta types for signal/slot connections to work
+ // Qt metatypes are registered only on their first use, so we do this now.
+ switch (qtMetaTypeRegistration(metaClass)) {
+ case QtRegisterMetaType::None:
+ break;
+ case QtRegisterMetaType::Pointer:
+ s << "qRegisterMetaType< " << m_gsp << className << " *>();\n";
+ break;
+ case QtRegisterMetaType::Value:
+ for (const QString &name : std::as_const(nameVariants))
+ s << "qRegisterMetaType< " << m_gsp << className << " >(\"" << name << "\");\n";
+ break;
+ }
+
+ for (const AbstractMetaEnum &metaEnum : metaClass->enums()) {
+ if (!metaEnum.isPrivate() && !metaEnum.isAnonymous()) {
+ for (const QString &name : std::as_const(nameVariants)) {
+ s << "qRegisterMetaType< " << m_gsp
+ << metaEnum.typeEntry()->qualifiedCppName() << " >(\""
+ << name << "::" << metaEnum.name() << "\");\n";
+ }
+ }
+ }
+}
+
+void CppGenerator::replacePolymorphicIdPlaceHolders(const AbstractMetaClassCPtr &metaClass,
+ QString *id)
+{
+ if (id->contains("%1"_L1)) {
+ QString replacement = " reinterpret_cast< "_L1 + m_gsp + metaClass->qualifiedCppName()
+ + " *>(cptr)"_L1;
+ id->replace("%1"_L1, replacement);
+ }
+ if (id->contains("%B"_L1)) {
+ auto baseClass = metaClass;
+ while (!baseClass->typeEntry()->isPolymorphicBase() && baseClass->baseClass())
+ baseClass = baseClass->baseClass();
+ QString replacement = " reinterpret_cast< "_L1 + m_gsp + baseClass->qualifiedCppName()
+ + " *>(cptr)"_L1;
+ id->replace("%B"_L1, replacement);
+ }
+}
+
+void CppGenerator::writeTypeDiscoveryFunction(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass)
+{
+ QString polymorphicExpr = metaClass->typeEntry()->polymorphicIdValue();
+
+ s << "static void *" << cpythonBaseName(metaClass)
+ << "_typeDiscovery(void *cptr, PyTypeObject *instanceType)\n{\n" << indent
+ << sbkUnusedVariableCast("cptr")
+ << sbkUnusedVariableCast("instanceType");
+
+ if (!polymorphicExpr.isEmpty()) {
+ replacePolymorphicIdPlaceHolders(metaClass, &polymorphicExpr);
+ s << "if (" << polymorphicExpr << ")\n" << indent
+ << "return cptr;\n" << outdent;
+ } else if (metaClass->isPolymorphic()) {
+ const auto &ancestors = metaClass->allTypeSystemAncestors();
+ for (const auto &ancestor : ancestors) {
+ if (ancestor->baseClass() && !ancestor->typeEntry()->isPolymorphicBase())
+ continue;
+ if (ancestor->isPolymorphic()) {
+ s << "if (instanceType == Shiboken::SbkType< " << m_gsp
+ << ancestor->qualifiedCppName() << " >())\n" << indent
+ << "return dynamic_cast< " << getFullTypeName(metaClass)
+ << " *>(reinterpret_cast< "<< getFullTypeName(ancestor)
+ << " *>(cptr));\n" << outdent;
+ } else {
+ qCWarning(lcShiboken).noquote().nospace()
+ << metaClass->qualifiedCppName() << " inherits from a non polymorphic type ("
+ << ancestor->qualifiedCppName() << "), type discovery based on RTTI is "
+ "impossible, write a polymorphic-id-expression for this type.";
+ }
+
+ }
+ }
+ s << "return {};\n" << outdent << "}\n\n";
+}
+
+void CppGenerator::writeSetattroDefinition(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass)
+{
+ s << "static int " << ShibokenGenerator::cpythonSetattroFunctionName(metaClass)
+ << "(PyObject *self, PyObject *name, PyObject *value)\n{\n" << indent;
+ if (wrapperDiagnostics()) {
+ s << R"(std::cerr << __FUNCTION__ << ' ' << Shiboken::debugPyObject(name)
+ << ' ' << Shiboken::debugPyObject(value) << '\n';)" << '\n';
+ }
+}
+
+void CppGenerator::writeSetattroDefaultReturn(TextStream &s)
+{
+ s << "return PyObject_GenericSetAttr(self, name, value);\n"
+ << outdent << "}\n\n";
+}
+
+void CppGenerator::writeSetattroFunction(TextStream &s, AttroCheck attroCheck,
+ const GeneratorContext &context) const
+{
+ Q_ASSERT(!context.forSmartPointer());
+ const auto metaClass = context.metaClass();
+ writeSetattroDefinition(s, metaClass);
+
+ // PYSIDE-1019: Switch tp_dict before doing tp_setattro.
+ if (usePySideExtensions())
+ s << "PySide::Feature::Select(self);\n";
+
+ // PYSIDE-803: Detect duck-punching; clear cache if a method is set.
+ if (attroCheck.testFlag(AttroCheckFlag::SetattroMethodOverride)
+ && context.useWrapper()) {
+ s << "if (value != nullptr && PyCallable_Check(value) != 0) {\n" << indent
+ << "auto plain_inst = " << cpythonWrapperCPtr(metaClass, PYTHON_SELF_VAR) << ";\n"
+ << "auto *inst = dynamic_cast<" << context.wrapperName() << " *>(plain_inst);\n"
+ << "if (inst != nullptr)\n" << indent
+ << "inst->resetPyMethodCache();\n" << outdent << outdent
+ << "}\n";
+ }
+ if (attroCheck.testFlag(AttroCheckFlag::SetattroQObject)) {
+ s << "Shiboken::AutoDecRef pp(reinterpret_cast<PyObject *>(PySide::Property::getObject(self, name)));\n"
+ << "if (!pp.isNull())\n" << indent
+ << "return PySide::Property::setValue(reinterpret_cast<PySideProperty *>(pp.object()), self, value);\n"
+ << outdent;
+ }
+
+ if (attroCheck.testFlag(AttroCheckFlag::SetattroUser)) {
+ auto func = AbstractMetaClass::queryFirstFunction(metaClass->functions(),
+ FunctionQueryOption::SetAttroFunction);
+ Q_ASSERT(func);
+ s << "{\n" << indent
+ << "auto " << CPP_SELF_VAR << " = "
+ << cpythonWrapperCPtr(metaClass, PYTHON_SELF_VAR) << ";\n";
+ writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny,
+ TypeSystem::TargetLangCode, context);
+ s << outdent << "}\n";
+ }
+
+ writeSetattroDefaultReturn(s);
+}
+
+void CppGenerator::writeGetattroDefinition(TextStream &s, const AbstractMetaClassCPtr &metaClass)
+{
+ s << "static PyObject *" << cpythonGetattroFunctionName(metaClass)
+ << "(PyObject *self, PyObject *name)\n{\n" << indent;
+}
+
+QString CppGenerator::qObjectGetAttroFunction() const
+{
+ static QString result;
+ if (result.isEmpty()) {
+ auto qobjectClass = AbstractMetaClass::findClass(api().classes(), qObjectT);
+ Q_ASSERT(qobjectClass);
+ result = u"PySide::getHiddenDataFromQObject("_s
+ + cpythonWrapperCPtr(qobjectClass, PYTHON_SELF_VAR)
+ + u", self, name)"_s;
+ }
+ return result;
+}
+
+void CppGenerator::writeGetattroFunction(TextStream &s, AttroCheck attroCheck,
+ const GeneratorContext &context) const
+{
+ Q_ASSERT(!context.forSmartPointer());
+ const auto metaClass = context.metaClass();
+ writeGetattroDefinition(s, metaClass);
+
+ // PYSIDE-1019: Switch tp_dict before doing tp_getattro.
+ if (usePySideExtensions())
+ s << "PySide::Feature::Select(self);\n";
+
+ const QString getattrFunc = usePySideExtensions() && isQObject(metaClass)
+ ? qObjectGetAttroFunction() : u"PyObject_GenericGetAttr(self, name)"_s;
+
+ if (attroCheck.testFlag(AttroCheckFlag::GetattroOverloads)) {
+ s << "// Search the method in the instance dict\n"
+ << "auto *ob_dict = SbkObject_GetDict_NoRef(self);\n";
+ s << "if (auto *meth = PyDict_GetItem(ob_dict, name)) {\n" << indent
+ << "Py_INCREF(meth);\nreturn meth;\n" << outdent << "}\n"
+ << "// Search the method in the type dict\n"
+ << "if (Shiboken::Object::isUserType(self)) {\n" << indent;
+ // PYSIDE-772: Perform optimized name mangling.
+ s << "Shiboken::AutoDecRef tmp(_Pep_PrivateMangle(self, name));\n"
+ << "Shiboken::AutoDecRef tpDict(PepType_GetDict(Py_TYPE(self)));\n"
+ << "if (auto *meth = PyDict_GetItem(tpDict.object(), tmp)) {\n" << indent;
+ // PYSIDE-1523: PyFunction_Check is not accepting compiled functions.
+ s << "if (std::strcmp(Py_TYPE(meth)->tp_name, \"compiled_function\") == 0) {\n" << indent
+ << "auto descrGetFunc = "
+ << pyTypeGetSlot("descrgetfunc", "Py_TYPE(meth)", "Py_tp_descr_get") << ";\n"
+ << "return descrGetFunc(meth, self, nullptr);\n" << outdent
+ << "}\nreturn PyFunction_Check(meth) ? PyMethod_New(meth, self)\n"
+ << " : " << getattrFunc << ";\n" << outdent
+ << "}\n" << outdent << "}\n";
+
+ const auto &funcs = getMethodsWithBothStaticAndNonStaticMethods(metaClass);
+ for (const auto &func : funcs) {
+ QString defName = cpythonMethodDefinitionName(func);
+ s << "static PyMethodDef non_static_" << defName << " = {\n" << indent
+ << defName << ".ml_name,\n"
+ << defName << ".ml_meth,\n"
+ << defName << ".ml_flags & (~METH_STATIC),\n"
+ << defName << ".ml_doc,\n" << outdent
+ << "};\n"
+ << "if (Shiboken::String::compare(name, \""
+ << func->definitionNames().constFirst() << "\") == 0)\n" << indent
+ << "return PyCFunction_NewEx(&non_static_" << defName << ", self, 0);\n"
+ << outdent;
+ }
+ }
+
+ if (attroCheck.testFlag(AttroCheckFlag::GetattroUser)) {
+ auto func = AbstractMetaClass::queryFirstFunction(metaClass->functions(),
+ FunctionQueryOption::GetAttroFunction);
+ Q_ASSERT(func);
+ s << "{\n" << indent
+ << "auto " << CPP_SELF_VAR << " = "
+ << cpythonWrapperCPtr(metaClass, PYTHON_SELF_VAR) << ";\n";
+ writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny,
+ TypeSystem::TargetLangCode, context);
+ s << outdent << "}\n";
+ }
+
+ s << "return " << getattrFunc << ";\n" << outdent << "}\n\n";
+}
+
+void CppGenerator::writeNbBoolExpression(TextStream &s, const BoolCastFunction &f,
+ bool invert)
+{
+ if (f.function->isOperatorBool()) {
+ if (invert)
+ s << '!';
+ s << '*' << CPP_SELF_VAR;
+ return;
+ }
+ if (invert != f.invert)
+ s << '!';
+ s << CPP_SELF_VAR << "->" << f.function->name() << "()";
+}
+
+void CppGenerator::writeNbBoolFunction(const GeneratorContext &context,
+ const BoolCastFunction &f,
+ TextStream &s)
+{
+ s << "static int " << cpythonBaseName(context.metaClass()) << "___nb_bool(PyObject *self)\n"
+ << "{\n" << indent;
+ writeCppSelfDefinition(s, context, ErrorReturn::MinusOne);
+
+ const bool allowThread = f.function->allowThread();
+ if (allowThread)
+ s << "int result;\n" << BEGIN_ALLOW_THREADS << "\nresult = ";
+ else
+ s << "return ";
+
+ writeNbBoolExpression(s, f);
+ s << " ? 1 : 0;\n";
+
+ if (allowThread)
+ s << END_ALLOW_THREADS << "\nreturn result;\n";
+ s << outdent << "}\n\n";
+}
+
+// Write declaration and invocation of the init function for the module init
+// function.
+static void writeInitFuncDecl(TextStream &declStr,
+ const QString &functionName)
+{
+ declStr << "PyTypeObject *" << functionName << "(PyObject *enclosing);\n";
+}
+
+// Write declaration and invocation of the init function for the module init
+// function.
+void CppGenerator::writeInitFuncCall(TextStream &callStr,
+ const QString &functionName,
+ const TypeEntryCPtr &enclosingEntry,
+ const QString &pythonName, bool lazy)
+{
+ const bool hasParent = enclosingEntry && enclosingEntry->type() != TypeEntry::TypeSystemType;
+ if (!lazy) {
+ const QString enclosing = hasParent
+ ? "reinterpret_cast<PyObject *>("_L1 + cpythonTypeNameExt(enclosingEntry) + u')'
+ : "module"_L1;
+ callStr << functionName << '(' << enclosing << ");\n";
+ } 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, \"" << parts[0] << "\", "
+ << functionName << ", \"" << namePathPrefix << '.' << pythonName << "\");\n";
+ } else {
+ callStr << "Shiboken::Module::AddTypeCreationFunction("
+ << "module, \"" << pythonName << "\", "
+ << functionName << ");\n";
+ }
+}
+
+static void writeSubModuleHandling(TextStream &s, const QString &moduleName,
+ const QString &subModuleOf)
+{
+ s << "{\n" << indent
+ << "Shiboken::AutoDecRef parentModule(Shiboken::Module::import(\""
+ << subModuleOf << "\"));\n"
+ << "if (parentModule.isNull())\n" << indent
+ << "return nullptr;\n" << outdent
+ << "if (PyModule_AddObject(parentModule.object(), \"" << moduleName
+ << "\", module) < 0)\n"
+ << indent << "return nullptr;\n" << outdent << outdent << "}\n";
+}
+
+bool CppGenerator::finishGeneration()
+{
+ //Generate CPython wrapper file
+ StringStream s_classInitDecl(TextStream::Language::Cpp);
+ StringStream s_classPythonDefines(TextStream::Language::Cpp);
+
+ std::set<Include> includes;
+ StringStream s_globalFunctionImpl(TextStream::Language::Cpp);
+ StringStream s_globalFunctionDef(TextStream::Language::Cpp);
+ StringStream signatureStream(TextStream::Language::Cpp);
+
+ const auto functionGroups = getGlobalFunctionGroups();
+ for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) {
+ const AbstractMetaFunctionCList &overloads = it.value();
+ for (const auto &func : overloads) {
+ if (auto te = func->typeEntry())
+ includes.insert(te->include());
+ }
+
+ if (overloads.isEmpty())
+ continue;
+
+ // Dummy context to satisfy the API.
+ GeneratorContext classContext;
+ OverloadData overloadData(overloads, api());
+
+ writeMethodWrapper(s_globalFunctionImpl, overloadData, classContext);
+ writeSignatureInfo(signatureStream, overloadData);
+ s_globalFunctionDef << methodDefinitionEntries(overloadData);
+ }
+
+ AbstractMetaClassCList classesWithStaticFields;
+ for (const auto &cls : api().classes()){
+ auto te = cls->typeEntry();
+ if (shouldGenerate(te)) {
+ const bool hasConfigCondition = te->hasConfigCondition();
+ if (hasConfigCondition) {
+ s_classInitDecl << te->configCondition() << '\n';
+ s_classPythonDefines << te->configCondition() << '\n';
+ }
+ const QString initFunc = initFuncPrefix + getSimpleClassInitFunctionName(cls);
+ writeInitFuncDecl(s_classInitDecl, initFunc);
+ writeInitFuncCall(s_classPythonDefines, initFunc,
+ targetLangEnclosingEntry(te), cls->name());
+ if (cls->hasStaticFields()) {
+ s_classInitDecl << "PyTypeObject *"
+ << getSimpleClassStaticFieldsInitFunctionName(cls) << "(PyObject *module);\n";
+ classesWithStaticFields.append(cls);
+ }
+ if (hasConfigCondition) {
+ s_classInitDecl << "#endif\n";
+ s_classPythonDefines << "#endif\n";
+ }
+ }
+ }
+
+ // Initialize smart pointer types.
+ for (const auto &smp : api().instantiatedSmartPointers()) {
+ GeneratorContext context = contextForSmartPointer(smp.specialized, smp.type);
+ const auto enclosingClass = context.metaClass()->enclosingClass();
+ auto enclosingTypeEntry = targetLangEnclosingEntry(smp.specialized->typeEntry());
+
+ const QString initFunc = initFuncPrefix + getInitFunctionName(context);
+ writeInitFuncDecl(s_classInitDecl, initFunc);
+ writeInitFuncCall(s_classPythonDefines,
+ initFunc, enclosingTypeEntry, smp.specialized->name());
+ includes.insert(smp.type.instantiations().constFirst().typeEntry()->include());
+ }
+
+ for (auto &instantiatedContainer : api().instantiatedContainers()) {
+ includes.insert(instantiatedContainer.typeEntry()->include());
+ for (const auto &inst : instantiatedContainer.instantiations())
+ includes.insert(inst.typeEntry()->include());
+ }
+
+ const ExtendedConverterData extendedConverters = getExtendedConverters();
+ for (auto it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) {
+ TypeEntryCPtr te = it.key();
+ includes.insert(te->include());
+ for (const auto &metaClass : it.value())
+ includes.insert(metaClass->typeEntry()->include());
+ }
+
+ const QList<CustomConversionPtr> &typeConversions = getPrimitiveCustomConversions();
+ for (const auto &c : typeConversions) {
+ if (auto te = c->ownerType())
+ includes.insert(te->include());
+ }
+
+ QString moduleFileName(outputDirectory() + u'/' + subDirectoryForPackage(packageName()));
+ moduleFileName += u'/' + moduleName().toLower() + u"_module_wrapper.cpp"_s;
+
+ FileOut file(moduleFileName);
+
+ TextStream &s = file.stream;
+ s.setLanguage(TextStream::Language::Cpp);
+
+ // write license comment
+ s << licenseComment() << R"(
+#include <sbkpython.h>
+#include <shiboken.h>
+#include <algorithm>
+#include <signature.h>
+)";
+
+ if (!api().instantiatedContainers().isEmpty())
+ s << "#include <sbkcontainer.h>\n#include <sbkstaticstrings.h>\n";
+
+ if (usePySideExtensions()) {
+ s << includeQDebug;
+ s << R"(#include <pysidecleanup.h>
+#include <pysideqenum.h>
+#include <feature_select.h>
+#include <pysidestaticstrings.h>
+)";
+ }
+
+ s << "#include \"" << getModuleHeaderFileName() << '"' << "\n\n";
+ for (const Include &include : includes)
+ s << include;
+ s << '\n';
+
+ // Global enums
+ AbstractMetaEnumList globalEnums = api().globalEnums();
+ for (const auto &nsp : invisibleTopNamespaces()) {
+ const auto oldSize = globalEnums.size();
+ nsp->getEnumsToBeGenerated(&globalEnums);
+ if (globalEnums.size() > oldSize)
+ s << nsp->typeEntry()->include();
+ }
+
+ TypeDatabase *typeDb = TypeDatabase::instance();
+ TypeSystemTypeEntryCPtr moduleEntry = typeDb->defaultTypeSystemType();
+ Q_ASSERT(moduleEntry);
+
+ s << '\n';
+ // Extra includes
+ QList<Include> extraIncludes = moduleEntry->extraIncludes();
+ for (const AbstractMetaEnum &cppEnum : std::as_const(globalEnums))
+ extraIncludes.append(cppEnum.typeEntry()->extraIncludes());
+ if (!extraIncludes.isEmpty()) {
+ s << "// Extra includes\n";
+ std::sort(extraIncludes.begin(), extraIncludes.end());
+ for (const Include &inc : std::as_const(extraIncludes))
+ s << inc;
+ s << '\n';
+ }
+
+ // FIXME PYSIDE-7: Remove backwards compatible structure
+ s << "// Current module's type array.\n"
+ << "Shiboken::Module::TypeInitStruct *" << cppApiVariableName() << " = nullptr;\n"
+ << "// Backwards compatible structure with identical indexing.\n"
+ << "PyTypeObject **" << cppApiVariableNameOld() << " = nullptr;\n"
+ << "// Current module's PyObject pointer.\n"
+ << "PyObject *" << pythonModuleObjectName() << " = nullptr;\n"
+ << "// Current module's converter array.\n"
+ << "SbkConverter **" << convertersVariableName() << " = nullptr;\n\n";
+
+ const CodeSnipList snips = moduleEntry->codeSnips();
+
+ // module inject-code native/beginning
+ writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode);
+
+ // cleanup staticMetaObject attribute
+ if (usePySideExtensions()) {
+ QString iType = cppApiVariableName() + "[i].type"_L1;
+ QString iName = cppApiVariableName() + "[i].fullName"_L1;
+
+ s << "void cleanTypesAttributes() {\n" << indent
+ << "static PyObject *attrName = Shiboken::PyName::qtStaticMetaObject();\n"
+ << "const int imax = SBK_" << moduleName() << "_IDX_COUNT;\n"
+ << "for (int i = 0; i < imax && " << iName << " != nullptr; ++i) {\n" << indent
+ << "auto *pyType = reinterpret_cast<PyObject *>(" << iType << ");\n"
+ << "if (pyType != nullptr && PyObject_HasAttr(pyType, attrName))\n" << indent
+ << "PyObject_SetAttr(pyType, attrName, Py_None);\n" << outdent
+ << outdent << "}\n" << outdent << "}\n\n";
+ }
+
+ s << "// Global functions "
+ << "------------------------------------------------------------\n"
+ << s_globalFunctionImpl.toString() << '\n'
+ << "static PyMethodDef " << moduleName() << "_methods[] = {\n" << indent
+ << s_globalFunctionDef.toString()
+ << METHOD_DEF_SENTINEL << outdent << "};\n\n"
+ << "// Classes initialization functions "
+ << "------------------------------------------------------------\n"
+ << s_classInitDecl.toString() << '\n';
+
+ if (!globalEnums.isEmpty()) {
+ StringStream convImpl(TextStream::Language::Cpp);
+
+ s << "// Enum definitions "
+ << "------------------------------------------------------------\n";
+ for (const AbstractMetaEnum &cppEnum : std::as_const(globalEnums))
+ writeEnumConverterFunctions(s, cppEnum);
+
+ if (convImpl.size() > 0) {
+ s << "// Enum converters "
+ << "------------------------------------------------------------\n"
+ << "namespace Shiboken\n{\n"
+ << convImpl.toString() << '\n'
+ << "} // namespace Shiboken\n\n";
+ }
+
+ s << '\n';
+ }
+
+ const QStringList &requiredModules = typeDb->requiredTargetImports();
+ if (!requiredModules.isEmpty())
+ s << "// Required modules' type and converter arrays.\n";
+ for (const QString &requiredModule : requiredModules) {
+ s << "Shiboken::Module::TypeInitStruct *" << cppApiVariableName(requiredModule) << ";\n"
+ << "SbkConverter **" << convertersVariableName(requiredModule) << ";\n";
+ }
+
+ s << "\n// Module initialization "
+ << "------------------------------------------------------------\n";
+ if (!extendedConverters.isEmpty()) {
+ s << '\n' << "// Extended Converters.\n\n";
+ for (ExtendedConverterData::const_iterator it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) {
+ TypeEntryCPtr externalType = it.key();
+ s << "// Extended implicit conversions for "
+ << externalType->qualifiedTargetLangName() << '.' << '\n';
+ for (const auto &sourceClass : it.value()) {
+ AbstractMetaType sourceType = AbstractMetaType::fromAbstractMetaClass(sourceClass);
+ AbstractMetaType targetType = AbstractMetaType::fromTypeEntry(externalType);
+ writePythonToCppConversionFunctions(s, sourceType, targetType);
+ }
+ }
+ }
+
+ if (!typeConversions.isEmpty()) {
+ s << "\n// Primitive Type converters.\n\n";
+ for (const auto &conversion : typeConversions) {
+ s << "// C++ to Python conversion for primitive type '" << conversion->ownerType()->qualifiedCppName() << "'.\n";
+ writeCppToPythonFunction(s, conversion);
+ writeCustomConverterFunctions(s, conversion);
+ }
+ s << '\n';
+ }
+
+ QHash<AbstractMetaType, OpaqueContainerData> opaqueContainers;
+ const auto &containers = api().instantiatedContainers();
+ QSet<AbstractMetaType> valueConverters;
+ if (!containers.isEmpty()) {
+ s << "// Container Type converters.\n\n";
+ for (const AbstractMetaType &container : containers) {
+ s << "// C++ to Python conversion for container type '"
+ << container.cppSignature() << "'.\n";
+ writeContainerConverterFunctions(s, container);
+ if (container.generateOpaqueContainer()) {
+ auto data = writeOpaqueContainerConverterFunctions(s, container,
+ &valueConverters);
+ opaqueContainers.insert(container, data);
+ }
+ }
+ s << '\n';
+ }
+
+ s << "static struct PyModuleDef moduledef = {\n"
+ << " /* m_base */ PyModuleDef_HEAD_INIT,\n"
+ << " /* m_name */ \"" << moduleName() << "\",\n"
+ << " /* m_doc */ nullptr,\n"
+ << " /* m_size */ -1,\n"
+ << " /* m_methods */ " << moduleName() << "_methods,\n"
+ << " /* m_reload */ nullptr,\n"
+ << " /* m_traverse */ nullptr,\n"
+ << " /* m_clear */ nullptr,\n"
+ << " /* m_free */ nullptr\n};\n\n";
+
+ // 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_"
+ << moduleName() << "()\n{\n" << indent;
+ // Guard against repeated invocation
+ s << "if (" << globalModuleVar << " != nullptr)\n"
+ << indent << "return " << globalModuleVar << ";\n" << outdent;
+
+ // module inject-code target/beginning
+ writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning,
+ TypeSystem::TargetLangCode);
+
+ for (const QString &requiredModule : requiredModules) {
+ s << "{\n" << indent
+ << "Shiboken::AutoDecRef requiredModule(Shiboken::Module::import(\"" << requiredModule << "\"));\n"
+ << "if (requiredModule.isNull())\n" << indent
+ << "return nullptr;\n" << outdent
+ << cppApiVariableName(requiredModule)
+ << " = Shiboken::Module::getTypes(requiredModule);\n"
+ << convertersVariableName(requiredModule)
+ << " = Shiboken::Module::getTypeConverters(requiredModule);\n" << outdent
+ << "}\n\n";
+ }
+
+ int maxTypeIndex = getMaxTypeIndex() + api().instantiatedSmartPointers().size();
+ if (maxTypeIndex) {
+ s << "// Create an array of wrapper types/names for the current module.\n"
+ << "static Shiboken::Module::TypeInitStruct cppApi[] = {\n" << indent;
+
+ // Windows did not like an array of QString.
+ QStringList typeNames;
+ for (int idx = 0; idx < maxTypeIndex; ++idx)
+ typeNames.append("+++ unknown entry #"_L1 + QString::number(idx)
+ + " in "_L1 + moduleName());
+
+ collectFullTypeNamesArray(typeNames);
+
+ for (auto typeName : typeNames)
+ s << "{nullptr, \"" << typeName << "\"},\n";
+
+ s << "{nullptr, nullptr}\n" << outdent << "};\n"
+ << "// The new global structure consisting of (type, name) pairs.\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"
+ << "static SbkConverter *sbkConverters[SBK_" << moduleName()
+ << "_CONVERTERS_IDX_COUNT" << "];\n"
+ << convertersVariableName() << " = sbkConverters;\n\n"
+ << "PyObject *module = Shiboken::Module::create(\"" << moduleName()
+ << "\", &moduledef);\n\n"
+ << "// Make module available from global scope\n"
+ << globalModuleVar << " = module;\n\n";
+
+ const QString subModuleOf = typeDb->defaultTypeSystemType()->subModuleOf();
+ if (!subModuleOf.isEmpty())
+ writeSubModuleHandling(s, moduleName(), subModuleOf);
+
+ s << "// Initialize classes in the type system\n"
+ << s_classPythonDefines.toString();
+
+ if (!typeConversions.isEmpty()) {
+ s << '\n';
+ for (const auto &conversion : typeConversions) {
+ writePrimitiveConverterInitialization(s, conversion);
+ s << '\n';
+ }
+ }
+
+ if (!containers.isEmpty()) {
+ s << '\n';
+ for (const AbstractMetaType &container : containers) {
+ const QString converterObj = writeContainerConverterInitialization(s, container, api());
+ const auto it = opaqueContainers.constFind(container);
+ if (it != opaqueContainers.constEnd()) {
+ writeSetPythonToCppPointerConversion(s, converterObj,
+ it.value().pythonToConverterFunctionName,
+ it.value().converterCheckFunctionName);
+ }
+ s << '\n';
+ }
+ }
+
+ if (!opaqueContainers.isEmpty()) {
+ s << "\n// Opaque container type registration\n"
+ << "PyObject *ob_type{};\n";
+ for (const auto &d : opaqueContainers)
+ s << d.registrationCode;
+ s << '\n';
+ }
+
+ if (!extendedConverters.isEmpty()) {
+ s << '\n';
+ for (ExtendedConverterData::const_iterator it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) {
+ writeExtendedConverterInitialization(s, it.key(), it.value());
+ s << '\n';
+ }
+ }
+
+ writeEnumsInitialization(s, globalEnums);
+
+ s << "// Register primitive types converters.\n";
+ const PrimitiveTypeEntryCList &primitiveTypeList = primitiveTypes();
+ for (const auto &pte : primitiveTypeList) {
+ if (!pte->generateCode() || !isCppPrimitive(pte))
+ continue;
+ if (!pte->referencesType())
+ continue;
+ TypeEntryCPtr referencedType = basicReferencedTypeEntry(pte);
+ registerConverterInScopes(s, pte->qualifiedCppName(), converterObject(referencedType));
+ }
+
+ s << '\n';
+ if (maxTypeIndex)
+ s << "Shiboken::Module::registerTypes(module, " << cppApiVariableName() << ");\n";
+ s << "Shiboken::Module::registerTypeConverters(module, " << convertersVariableName() << ");\n";
+
+ // Static fields are registered last since they may use converter functions
+ // of the previously registered types (PYSIDE-1529).
+ if (!classesWithStaticFields.isEmpty()) {
+ s << "\n// Static field initialization\n";
+ for (const auto &cls : std::as_const(classesWithStaticFields)) {
+ ConfigurableScope configScope(s, cls->typeEntry());
+ s << getSimpleClassStaticFieldsInitFunctionName(cls) << "(module);\n";
+ }
+ }
+
+ s << '\n' << initInheritanceFunction << "();\n"
+ << "\nif (" << shibokenErrorsOccurred << ") {\n" << indent
+ << "PyErr_Print();\n"
+ << "Py_FatalError(\"can't initialize module " << moduleName() << "\");\n"
+ << outdent << "}\n";
+
+ // module inject-code target/end
+ writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode);
+
+ // module inject-code native/end
+ writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode);
+
+ if (usePySideExtensions()) {
+ for (const AbstractMetaEnum &metaEnum : std::as_const(globalEnums))
+ if (!metaEnum.isAnonymous()) {
+ ConfigurableScope configScope(s, metaEnum.typeEntry());
+ s << "qRegisterMetaType< " << getFullTypeName(metaEnum.typeEntry())
+ << " >(\"" << metaEnum.name() << "\");\n";
+ }
+
+ // cleanup staticMetaObject attribute
+ s << "PySide::registerCleanupFunction(cleanTypesAttributes);\n\n";
+ }
+
+ // finish the rest of get_signature() initialization.
+ s << "FinishSignatureInitialization(module, " << moduleName()
+ << "_SignatureStrings);\n"
+ << "\nreturn module;\n" << outdent << "}\n";
+
+ file.done();
+ return true;
+}
+
+static ArgumentOwner getArgumentOwner(const AbstractMetaFunctionCPtr &func, int argIndex)
+{
+ ArgumentOwner argOwner = func->argumentOwner(func->ownerClass(), argIndex);
+ if (argOwner.index == ArgumentOwner::InvalidIndex)
+ argOwner = func->argumentOwner(func->declaringClass(), argIndex);
+ return argOwner;
+}
+
+// Whether to enable parent ownership heuristic for a function and its argument.
+// Both must belong to the same class hierarchy and have the same
+// type entry enabling parent management.
+static bool useParentHeuristics(const ApiExtractorResult &api,
+ const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaType &argType)
+{
+ if (!ComplexTypeEntry::isParentManagementEnabled()) // FIXME PYSIDE 7: Remove this
+ return true;
+ const auto owner = func->ownerClass();
+ if (!owner)
+ return false;
+ auto ownerEntry = parentManagementEntry(owner);
+ if (!ownerEntry)
+ return false;
+ auto argTypeEntry = argType.typeEntry();
+ if (!argTypeEntry->isComplex())
+ return false;
+ const auto argClass = AbstractMetaClass::findClass(api.classes(), argTypeEntry);
+ return argClass && parentManagementEntry(argClass) == ownerEntry;
+}
+
+bool CppGenerator::writeParentChildManagement(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ int argIndex,
+ bool usePyArgs, bool useHeuristicPolicy) const
+{
+ const int numArgs = func->arguments().size();
+ bool ctorHeuristicEnabled = func->isConstructor() && useCtorHeuristic() && useHeuristicPolicy;
+ bool heuristicTriggered = false;
+
+ ArgumentOwner argOwner = getArgumentOwner(func, argIndex);
+ ArgumentOwner::Action action = argOwner.action;
+ int parentIndex = argOwner.index;
+ int childIndex = argIndex;
+ if (ctorHeuristicEnabled && argIndex > 0 && argIndex <= numArgs) {
+ const AbstractMetaArgument &arg = func->arguments().at(argIndex-1);
+ if (arg.name() == u"parent" && arg.type().isObjectType()
+ && useParentHeuristics(api(), func, arg.type())) {
+ action = ArgumentOwner::Add;
+ parentIndex = argIndex;
+ childIndex = -1;
+ heuristicTriggered = true;
+ }
+ }
+
+ QString parentVariable;
+ QString childVariable;
+ if (action != ArgumentOwner::Invalid) {
+ if (!usePyArgs && argIndex > 1)
+ qCWarning(lcShiboken).noquote().nospace()
+ << "Argument index for parent tag out of bounds: " << func->signature();
+
+ if (action == ArgumentOwner::Remove) {
+ parentVariable = u"Py_None"_s;
+ } else {
+ if (parentIndex == 0) {
+ parentVariable = PYTHON_RETURN_VAR;
+ } else if (parentIndex == -1) {
+ parentVariable = PYTHON_SELF_VAR;
+ } else {
+ parentVariable = usePyArgs
+ ? pythonArgsAt(parentIndex - 1) : PYTHON_ARG;
+ }
+ }
+
+ if (childIndex == 0) {
+ childVariable = PYTHON_RETURN_VAR;
+ } else if (childIndex == -1) {
+ childVariable = PYTHON_SELF_VAR;
+ } else {
+ childVariable = usePyArgs
+ ? pythonArgsAt(childIndex - 1) : PYTHON_ARG;
+ }
+
+ s << "// Ownership transferences";
+ if (heuristicTriggered)
+ s << " (constructor heuristics)";
+ s << ".\nShiboken::Object::setParent(" << parentVariable << ", "
+ << childVariable << ");\n";
+ return true;
+ }
+
+ return false;
+}
+
+void CppGenerator::writeParentChildManagement(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ bool usesPyArgs,
+ bool useHeuristicForReturn) const
+{
+ const int numArgs = func->arguments().size();
+
+ // -1 = return value
+ // 0 = self
+ // 1..n = func. args.
+ for (int i = -1; i <= numArgs; ++i)
+ writeParentChildManagement(s, func, i, usesPyArgs, useHeuristicForReturn);
+
+ if (useHeuristicForReturn)
+ writeReturnValueHeuristics(s, func);
+}
+
+void CppGenerator::writeReturnValueHeuristics(TextStream &s, const AbstractMetaFunctionCPtr &func) const
+{
+ const AbstractMetaType &type = func->type();
+ if (!useReturnValueHeuristic()
+ || !func->ownerClass()
+ || type.isVoid()
+ || func->isStatic()
+ || func->isConstructor()
+ || func->isTypeModified()
+ || !useParentHeuristics(api(), func, type)
+ // Something like parent(), parentWidget(): No child relationship here.
+ || (func->maybeAccessor() && func->name().startsWith(u"parent"))) {
+ return;
+ }
+
+ ArgumentOwner argOwner = getArgumentOwner(func, ArgumentOwner::ReturnIndex);
+ if (argOwner.action == ArgumentOwner::Invalid || argOwner.index != ArgumentOwner::ThisIndex) {
+ if (type.isPointerToWrapperType()) {
+ s << "// Ownership transferences (return value heuristics).\n"
+ << "Shiboken::Object::setParent(self, " << PYTHON_RETURN_VAR << ");\n";
+ }
+ }
+}
+
+void CppGenerator::writeHashFunction(TextStream &s, const GeneratorContext &context)
+{
+ const auto metaClass = context.metaClass();
+ const char hashType[] = "Py_hash_t";
+ s << "static " << hashType << ' ' << cpythonBaseName(metaClass)
+ << "_HashFunc(PyObject *self)\n{\n" << indent;
+ writeCppSelfDefinition(s, context);
+
+ bool deref = true;
+ QString name = metaClass->typeEntry()->hashFunction();
+ if (name.isEmpty())
+ name = metaClass->hashFunction();
+ else
+ deref = !metaClass->isObjectType();
+ Q_ASSERT(!name.isEmpty());
+
+ s << "return " << hashType << '(' << name << '(';
+ if (deref)
+ s << '*';
+ s << CPP_SELF_VAR << "));\n"
+ << outdent << "}\n\n";
+}
+
+void CppGenerator::writeDefaultSequenceMethods(TextStream &s,
+ const GeneratorContext &context) const
+{
+ const auto metaClass = context.metaClass();
+ ErrorReturn errorReturn = ErrorReturn::Zero;
+
+ // __len__
+ const QString namePrefix = cpythonBaseName(metaClass->typeEntry());
+ s << "Py_ssize_t " << namePrefix
+ << "__len__(PyObject *self)\n{\n" << indent;
+ writeCppSelfDefinition(s, context, errorReturn);
+ s << "return " << CPP_SELF_VAR << "->size();\n"
+ << outdent << "}\n";
+
+ // __getitem__
+ s << "PyObject *" << namePrefix
+ << "__getitem__(PyObject *self, Py_ssize_t _i)\n{\n" << indent;
+ writeCppSelfDefinition(s, context, errorReturn);
+ writeIndexError(s, u"index out of bounds"_s, errorReturn);
+
+ s << metaClass->qualifiedCppName() << "::const_iterator _item = "
+ << CPP_SELF_VAR << "->begin();\n"
+ << "std::advance(_item, _i);\n";
+
+ const AbstractMetaTypeList &instantiations = metaClass->templateBaseClassInstantiations();
+ if (instantiations.isEmpty()) {
+ QString m;
+ QTextStream(&m) << "shiboken: " << __FUNCTION__
+ << ": Internal error, no instantiations of \"" << metaClass->qualifiedCppName()
+ << "\" were found.";
+ throw Exception(m);
+ }
+ const AbstractMetaType &itemType = instantiations.constFirst();
+
+ s << "return ";
+ writeToPythonConversion(s, itemType, metaClass, u"*_item"_s);
+ s << ";\n" << outdent << "}\n";
+
+ // __setitem__
+ s << "int " << namePrefix
+ << "__setitem__(PyObject *self, Py_ssize_t _i, PyObject *pyArg)\n{\n"
+ << indent;
+ errorReturn = ErrorReturn::MinusOne;
+ writeCppSelfDefinition(s, context, errorReturn);
+ writeIndexError(s, u"list assignment index out of range"_s, errorReturn);
+
+ s << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR << ";\n"
+ << "if (!";
+ writeTypeCheck(s, itemType, u"pyArg"_s, isNumber(itemType.typeEntry()));
+ s << ") {\n" << indent
+ << "Shiboken::Errors::setSequenceTypeError(\"" << itemType.name() << "\");\n"
+ << "return -1;\n" << outdent << "}\n";
+ writeArgumentConversion(s, itemType, u"cppValue"_s,
+ u"pyArg"_s, errorReturn, metaClass);
+
+ s << metaClass->qualifiedCppName() << "::iterator _item = "
+ << CPP_SELF_VAR << "->begin();\n"
+ << "std::advance(_item, _i);\n"
+ << "*_item = cppValue;\n";
+
+ s << "return {};\n" << outdent << "}\n";
+}
+void CppGenerator::writeIndexError(TextStream &s, const QString &errorMsg,
+ ErrorReturn errorReturn)
+{
+ s << "if (_i < 0 || _i >= (Py_ssize_t) " << CPP_SELF_VAR << "->size()) {\n"
+ << indent << "PyErr_SetString(PyExc_IndexError, \"" << errorMsg << "\");\n"
+ << errorReturn << outdent << "}\n";
+}
+
+QString CppGenerator::writeReprFunctionHeader(TextStream &s, const GeneratorContext &context)
+{
+ QString funcName = cpythonBaseName(context.metaClass()) + REPR_FUNCTION;
+ s << "extern \"C\"\n{\n"
+ << "static PyObject *" << funcName << "(PyObject *self)\n{\n" << indent;
+ return funcName;
+}
+
+QString CppGenerator::writeReprFunction(TextStream &s,
+ const GeneratorContext &context,
+ uint indirections)
+{
+ const auto metaClass = context.metaClass();
+ QString funcName = writeReprFunctionHeader(s, context);
+ writeCppSelfDefinition(s, context);
+ s << R"(QBuffer buffer;
+buffer.open(QBuffer::ReadWrite);
+QDebug dbg(&buffer);
+dbg << )";
+ if (metaClass->typeEntry()->isValue() || indirections == 0)
+ s << '*';
+ s << CPP_SELF_VAR << R"(;
+buffer.close();
+QByteArray str = buffer.data();
+const auto idx = str.indexOf('(');
+auto *typeName = Py_TYPE(self)->tp_name;
+if (idx >= 0)
+)" << indent << "str.replace(0, idx, typeName);\n" << outdent
+ << "str = str.trimmed();\n"
+ << "Shiboken::AutoDecRef tpDict(PepType_GetDict(Py_TYPE(self)));\n"
+ << "PyObject *mod = PyDict_GetItem(tpDict.object(), Shiboken::PyMagicName::module());\n";
+ // PYSIDE-595: The introduction of heap types has the side effect that the module name
+ // is always prepended to the type name. Therefore the strchr check:
+ s << "if (mod != nullptr && std::strchr(typeName, '.') == nullptr)\n" << indent
+ << "return Shiboken::String::fromFormat(\"<%s.%s at %p>\","
+ " Shiboken::String::toCString(mod), str.constData(), self);\n"
+ << outdent
+ << "return Shiboken::String::fromFormat(\"<%s at %p>\", str.constData(), self);\n";
+ writeReprFunctionFooter(s);
+ return funcName;
+}
+
+void CppGenerator::writeReprFunctionFooter(TextStream &s)
+{
+ s << outdent << "}\n} // extern C\n\n";
+}
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.h b/sources/shiboken6/generator/shiboken/cppgenerator.h
new file mode 100644
index 000000000..5920c9a3a
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/cppgenerator.h
@@ -0,0 +1,565 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CPPGENERATOR_H
+#define CPPGENERATOR_H
+
+#include "shibokengenerator.h"
+#include "include.h"
+#include "modifications_typedefs.h"
+
+#include <QtCore/QFlags>
+#include <QtCore/QSet>
+#include <QtCore/QHash>
+
+#include <memory>
+#include <utility>
+
+class OverloadDataNode;
+class OverloadDataRootNode;
+struct PyMethodDefEntry;
+
+/**
+ * The CppGenerator generate the implementations of C++ bindings classes.
+ */
+class CppGenerator : public ShibokenGenerator
+{
+public:
+ enum class ErrorReturn {
+ Default, // "{}"
+ Zero,
+ MinusOne,
+ Void
+ };
+
+ enum CppSelfDefinitionFlag {
+ HasStaticOverload = 0x1,
+ HasClassMethodOverload = 0x2,
+ CppSelfAsReference = 0x4
+ };
+ Q_DECLARE_FLAGS(CppSelfDefinitionFlags, CppSelfDefinitionFlag)
+
+ CppGenerator();
+
+ const char *name() const override { return "Source generator"; }
+
+protected:
+ QString fileNameForContext(const GeneratorContext &context) const override;
+ void generateClass(TextStream &s, const GeneratorContext &classContext) override;
+ bool finishGeneration() override;
+
+private:
+ struct VirtualMethodReturn
+ {
+ QString statement;
+ bool needsReference = false;
+ };
+
+
+ void generateSmartPointerClass(TextStream &s, const GeneratorContext &classContext);
+ void generateIncludes(TextStream &s, const GeneratorContext &classContext,
+ const IncludeGroupList &includes = {},
+ const AbstractMetaClassCList &innerClasses = {}) const;
+ static void writeInitFuncCall(TextStream &callStr,
+ const QString &functionName,
+ const TypeEntryCPtr &enclosingEntry,
+ const QString &pythonName, bool lazy = true);
+ static void writeCacheResetNative(TextStream &s, const GeneratorContext &classContext);
+ void writeConstructorNative(TextStream &s, const GeneratorContext &classContext,
+ const AbstractMetaFunctionCPtr &func) const;
+ static void writeDestructorNative(TextStream &s, const GeneratorContext &classContext);
+
+ QString getVirtualFunctionReturnTypeName(const AbstractMetaFunctionCPtr &func) const;
+ static std::pair<QString, QChar> virtualMethodNativeArg(const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaArgument &arg);
+ static void writeVirtualMethodNativeVectorCallArgs(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaArgumentList &arguments,
+ const QList<int> &invalidateArgs);
+ static void writeVirtualMethodNativeArgs(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaArgumentList &arguments,
+ const QList<int> &invalidateArgs);
+ void writeVirtualMethodNative(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ int cacheIndex) const;
+ void writeVirtualMethodPythonOverride(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ const CodeSnipList &snips,
+ const VirtualMethodReturn &returnStatement) const;
+ void writeUserAddedPythonOverride(TextStream &s,
+ const AbstractMetaFunctionCPtr &func) const;
+ void writeVirtualMethodCppCall(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ const QString &funcName, const QList<CodeSnip> &snips,
+ const AbstractMetaArgument *lastArg, const TypeEntryCPtr &retType,
+ const QString &returnStatement, bool hasGil) const;
+
+ static VirtualMethodReturn virtualMethodReturn(const ApiExtractorResult &api,
+ const AbstractMetaFunctionCPtr &func,
+ const FunctionModificationList &functionModifications);
+ void writeMetaObjectMethod(TextStream &s, const GeneratorContext &classContext) const;
+ static void writeMetaCast(TextStream &s, const GeneratorContext &classContext);
+
+ void writeEnumConverterFunctions(TextStream &s, const AbstractMetaEnum &metaEnum) const;
+ void writeConverterFunctions(TextStream &s, const AbstractMetaClassCPtr &metaClass,
+ const GeneratorContext &classContext) const;
+ void writeCustomConverterFunctions(TextStream &s,
+ const CustomConversionPtr &customConversion) const;
+ void writeConverterRegister(TextStream &s, const AbstractMetaClassCPtr &metaClass,
+ const GeneratorContext &classContext) const;
+ static void writeCustomConverterRegister(TextStream &s,
+ const CustomConversionPtr &customConversion,
+ const QString &converterVar);
+
+ void writeContainerConverterFunctions(TextStream &s,
+ const AbstractMetaType &containerType) const;
+
+ struct OpaqueContainerData
+ {
+ QString name;
+ QString checkFunctionName;
+ QString converterCheckFunctionName;
+ QString pythonToConverterFunctionName;
+ QString registrationCode;
+ };
+
+ OpaqueContainerData
+ writeOpaqueContainerConverterFunctions(TextStream &s,
+ const AbstractMetaType &containerType,
+ QSet<AbstractMetaType> *valueTypes) const;
+ void writeOpaqueContainerValueConverter(TextStream &s,
+ const AbstractMetaType &valueType) const;
+
+ void writeSmartPointerConverterFunctions(TextStream &s,
+ const AbstractMetaType &smartPointerType) const;
+
+ static bool needsArgumentErrorHandling(const OverloadData &overloadData);
+ static void writeMethodWrapperPreamble(TextStream &s,
+ const OverloadData &overloadData,
+ const GeneratorContext &context,
+ ErrorReturn errorReturn = ErrorReturn::Default);
+ void writeConstructorWrapper(TextStream &s,
+ const OverloadData &overloadData,
+ const GeneratorContext &classContext) const;
+ void writeMethodWrapper(TextStream &s, const OverloadData &overloadData,
+ const GeneratorContext &classContext) const;
+ void writeMethodWrapper(TextStream &s, TextStream &definitionStream,
+ TextStream &signatureStream,
+ const AbstractMetaFunctionCList &overloads,
+ const GeneratorContext &classContext) const;
+ static void writeArgumentsInitializer(TextStream &s, const OverloadData &overloadData,
+ ErrorReturn errorReturn = ErrorReturn::Default);
+ static void writeCppSelfConversion(TextStream &s,
+ const GeneratorContext &context,
+ const QString &className,
+ bool useWrapperClass);
+ static void writeSmartPointerCppSelfConversion(TextStream &s,
+ const GeneratorContext &context);
+
+ static void writeCppSelfVarDef(TextStream &s, CppSelfDefinitionFlags flags = {});
+ static void writeSmartPointerCppSelfDefinition(TextStream &s,
+ const GeneratorContext &,
+ ErrorReturn errorReturn = ErrorReturn::Default,
+ CppSelfDefinitionFlags flags = {});
+ static void writeCppSelfDefinition(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ const GeneratorContext &context,
+ ErrorReturn errorReturn = ErrorReturn::Default,
+ CppSelfDefinitionFlags flags = {});
+ static void writeCppSelfDefinition(TextStream &s,
+ const GeneratorContext &context,
+ ErrorReturn errorReturn = ErrorReturn::Default,
+ CppSelfDefinitionFlags flags = {});
+
+ static void writeErrorSection(TextStream &s,
+ const OverloadData &overloadData,
+ ErrorReturn errorReturn);
+
+ static QString returnErrorWrongArguments(const OverloadData &overloadData,
+ ErrorReturn errorReturn);
+
+ static void writeFunctionReturnErrorCheckSection(TextStream &s,
+ ErrorReturn errorReturn,
+ bool hasReturnValue = true);
+
+ /// Writes the check section for the validity of wrapped C++ objects.
+ static void writeInvalidPyObjectCheck(TextStream &s, const QString &pyObj,
+ ErrorReturn errorReturn);
+
+ static void writeTypeCheck(TextStream &s, const AbstractMetaType &argType,
+ const QString &argumentName,
+ bool isNumber = false, bool rejectNull = false);
+ static void writeTypeCheck(TextStream &s, const QString &customType,
+ const QString &argumentName);
+ static void writeTypeCheck(TextStream& s, const std::shared_ptr<OverloadDataNode> &overloadData,
+ const QString &argumentName);
+
+ static void replacePolymorphicIdPlaceHolders(const AbstractMetaClassCPtr &metaClass,
+ QString *id);
+ static void writeTypeDiscoveryFunction(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass);
+
+ static void writeSetattroDefinition(TextStream &s, const AbstractMetaClassCPtr &metaClass);
+ static void writeSetattroDefaultReturn(TextStream &s);
+ static void writeSmartPointerSetattroFunction(TextStream &s,
+ const GeneratorContext &context);
+ void writeSetattroFunction(TextStream &s,
+ AttroCheck attroCheck,
+ const GeneratorContext &context) const;
+ static void writeGetattroDefinition(TextStream &s, const AbstractMetaClassCPtr &metaClass);
+ static void writeSmartPointerGetattroFunction(TextStream &s,
+ const GeneratorContext &context,
+ const BoolCastFunctionOptional &boolCast);
+ void writeGetattroFunction(TextStream &s, AttroCheck attroCheck,
+ const GeneratorContext &context) const;
+ QString qObjectGetAttroFunction() const;
+
+ static void writeNbBoolFunction(const GeneratorContext &context,
+ const BoolCastFunction &f,
+ TextStream &s);
+ static void writeNbBoolExpression(TextStream &s, const BoolCastFunction &f, bool invert = false);
+
+ /**
+ * Writes Python to C++ conversions for arguments on Python wrappers.
+ * If implicit conversions, and thus new object allocation, are needed,
+ * code to deallocate a possible new instance is also generated.
+ * \param s text stream to write
+ * \param argType a pointer to the argument type to be converted
+ * \param argName C++ argument name
+ * \param pyArgName Python argument name
+ * \param context the current meta class
+ * \param defaultValue an optional default value to be used instead of the conversion result
+ * \param castArgumentAsUnused if true the converted argument is cast as unused to avoid compiler warnings
+ */
+ qsizetype writeArgumentConversion(TextStream &s, const AbstractMetaType &argType,
+ const QString &argName, const QString &pyArgName,
+ ErrorReturn errorReturn,
+ const AbstractMetaClassCPtr &context = {},
+ const QString &defaultValue = QString(),
+ bool castArgumentAsUnused = false) const;
+
+ /**
+ * Returns the AbstractMetaType for a function argument.
+ * If the argument type was modified in the type system, this method will
+ * try to build a new type based on the type name defined in the type system.
+ * \param func The function which owns the argument.
+ * \param index Argument index in the function signature.
+ * \return The type of the argument indicated by \p index.
+ */
+ static AbstractMetaType
+ getArgumentType(const AbstractMetaFunctionCPtr &func, int index);
+
+ /// Writes the Python to C++ Conversion for function arguments and return
+ /// values of virtual methods for wrappers.
+ /// \return The number of indirections in case of return types
+ qsizetype writePythonToCppTypeConversion(TextStream &s,
+ const AbstractMetaType &type,
+ const QString &pyIn,
+ const QString &cppOut,
+ const AbstractMetaClassCPtr &context = {},
+ const QString &defaultValue = {}) const;
+
+ /// Writes the conversion rule for arguments of regular and virtual methods.
+ void writeConversionRule(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ TypeSystem::Language language, bool usesPyArgs) const;
+ /// Writes the conversion rule for the return value of a method.
+ void writeConversionRule(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ TypeSystem::Language language, const QString &outputVar) const;
+
+ /**
+ * Set the Python method wrapper return value variable to Py_None if
+ * there are return types different from void in any of the other overloads
+ * for the function passed as parameter.
+ * \param s text stream to write
+ * \param func a pointer to the function that will possibly return Py_None
+ * \param thereIsReturnValue indicates if the return type of any of the other overloads
+ * for this function is different from 'void'
+ */
+ static void writeNoneReturn(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ bool thereIsReturnValue);
+
+ /**
+ * Writes the Python function wrapper overload decisor that selects which C++
+ * method/function to call with the received Python arguments.
+ * \param s text stream to write
+ * \param overloadData the overload data describing all the possible overloads for the function/method
+ */
+ void writeOverloadedFunctionDecisor(TextStream &s, const OverloadData &overloadData,
+ ErrorReturn errorReturn) const;
+ /// Recursive auxiliar method to the other writeOverloadedFunctionDecisor.
+ void writeOverloadedFunctionDecisorEngine(TextStream &s,
+ const OverloadData &overloadData,
+ const OverloadDataRootNode *node) const;
+
+ /// Writes calls to all the possible method/function overloads.
+ void writeFunctionCalls(TextStream &s,
+ const OverloadData &overloadData,
+ const GeneratorContext &context,
+ ErrorReturn errorReturn) const;
+
+ /// Writes the call to a single function usually from a collection of overloads.
+ void writeSingleFunctionCall(TextStream &s,
+ const OverloadData &overloadData,
+ const AbstractMetaFunctionCPtr &func,
+ const GeneratorContext &context,
+ ErrorReturn errorReturn) const;
+
+ /// Returns the name of a C++ to Python conversion function.
+ static QString cppToPythonFunctionName(const QString &sourceTypeName, QString targetTypeName = QString());
+
+ /// Returns the name of a Python to C++ conversion function.
+ static QString pythonToCppFunctionName(const QString &sourceTypeName, const QString &targetTypeName);
+ static QString pythonToCppFunctionName(const AbstractMetaType &sourceType, const AbstractMetaType &targetType);
+ static QString pythonToCppFunctionName(const TargetToNativeConversion &toNative,
+ const TypeEntryCPtr &targetType);
+
+ /// Returns the name of a Python to C++ convertible check function.
+ static QString convertibleToCppFunctionName(const QString &sourceTypeName, const QString &targetTypeName);
+ static QString convertibleToCppFunctionName(const AbstractMetaType &sourceType, const AbstractMetaType &targetType);
+ static QString convertibleToCppFunctionName(const TargetToNativeConversion &toNative,
+ const TypeEntryCPtr &targetType);
+
+ /// Writes a C++ to Python conversion function.
+ void writeCppToPythonFunction(TextStream &s, const QString &code, const QString &sourceTypeName,
+ QString targetTypeName = QString()) const;
+ void writeCppToPythonFunction(TextStream &s, const CustomConversionPtr &customConversion) const;
+ void writeCppToPythonFunction(TextStream &s, const AbstractMetaType &containerType) const;
+ /// Main target type name of a container (for naming the functions).
+ static QString containerNativeToTargetTypeName(const ContainerTypeEntryCPtr &type);
+
+ /// Writes a Python to C++ conversion function.
+ void writePythonToCppFunction(TextStream &s, const QString &code, const QString &sourceTypeName,
+ const QString &targetTypeName) const;
+
+ /// Writes a Python to C++ convertible check function.
+ static void writeIsPythonConvertibleToCppFunction(TextStream &s,
+ const QString &sourceTypeName,
+ const QString &targetTypeName,
+ const QString &condition,
+ QString pythonToCppFuncName = QString(),
+ bool acceptNoneAsCppNull = false);
+
+ /// Writes a pair of Python to C++ conversion and check functions.
+ void writePythonToCppConversionFunctions(TextStream &s,
+ const AbstractMetaType &sourceType,
+ const AbstractMetaType &targetType,
+ QString typeCheck = QString(),
+ QString conversion = QString(),
+ const QString &preConversion = QString()) const;
+ /// Writes a pair of Python to C++ conversion and check functions for implicit conversions.
+ void writePythonToCppConversionFunctions(TextStream &s,
+ const TargetToNativeConversion &toNative,
+ const TypeEntryCPtr &targetType) const;
+
+ /// Writes a pair of Python to C++ conversion and check functions for instantiated container types.
+ void writePythonToCppConversionFunctions(TextStream &s,
+ const AbstractMetaType &containerType) const;
+
+ void writePythonToCppConversionFunction(TextStream &s,
+ const AbstractMetaType &containerType,
+ const TargetToNativeConversion &conv) const;
+
+ static void writeAddPythonToCppConversion(TextStream &s, const QString &converterVar,
+ const QString &pythonToCppFunc,
+ const QString &isConvertibleFunc);
+
+ static void writeSetPythonToCppPointerConversion(TextStream &s, const QString &converterVar,
+ const QString &pythonToCppFunc,
+ const QString &isConvertibleFunc);
+
+ static void writeNamedArgumentResolution(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ bool usePyArgs,
+ const OverloadData &overloadData,
+ ErrorReturn errorReturn);
+
+ /// Returns a string containing the name of an argument for the given function and argument index.
+ static QString argumentNameFromIndex(const ApiExtractorResult &api,
+ const AbstractMetaFunctionCPtr &func, int argIndex);
+ /// Returns the class for an ownership modification of the argument.
+ /// Throws if the argument is not a class or cannot be found.
+ static AbstractMetaClassCPtr
+ argumentClassFromIndex(const ApiExtractorResult &api,
+ const AbstractMetaFunctionCPtr &func, int argIndex);
+
+ void writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ const GeneratorContext &context, bool usesPyArgs,
+ int maxArgs, const QList<qsizetype> &argumentIndirections,
+ ErrorReturn errorReturn) const;
+
+ static QString getInitFunctionName(const GeneratorContext &context) ;
+ static QString getSimpleClassInitFunctionName(const AbstractMetaClassCPtr &metaClass);
+ static QString
+ getSimpleClassStaticFieldsInitFunctionName(const AbstractMetaClassCPtr &metaClass);
+
+ 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,
+ const QString &signatures) const;
+ static QStringList pyBaseTypes(const AbstractMetaClassCPtr &metaClass);
+ static QString destructorClassName(const AbstractMetaClassCPtr &metaClass,
+ const GeneratorContext &classContext);
+ static void writeStaticFieldInitialization(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass);
+ void writeClassDefinition(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass,
+ const GeneratorContext &classContext);
+ QByteArrayList methodDefinitionParameters(const OverloadData &overloadData) const;
+ QList<PyMethodDefEntry> methodDefinitionEntries(const OverloadData &overloadData) const;
+
+ void writeSignatureInfo(TextStream &s, const OverloadData &overloads) const;
+ QString signatureParameter(const AbstractMetaArgument &arg) const;
+ QString pythonSignature(const AbstractMetaType &type) const;
+ /// Writes the implementation of all methods part of python sequence protocol
+ void writeSequenceMethods(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass,
+ const GeneratorContext &context) const;
+ static void writeTypeAsSequenceDefinition(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass);
+
+ /// Writes the PyMappingMethods structure for types that supports the python mapping protocol.
+ static void writeTypeAsMappingDefinition(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass);
+ void writeMappingMethods(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass,
+ const GeneratorContext &context) const;
+
+ void writeTypeAsNumberDefinition(TextStream &s, const AbstractMetaClassCPtr &metaClass) const;
+
+ static void writeTpTraverseFunction(TextStream &s, const AbstractMetaClassCPtr &metaClass);
+ static void writeTpClearFunction(TextStream &s, const AbstractMetaClassCPtr &metaClass);
+
+ static QString writeCopyFunction(TextStream &s, TextStream &definitionStream,
+ TextStream &signatureStream, const GeneratorContext &context);
+
+ static QString cppFieldAccess(const AbstractMetaField &metaField,
+ const GeneratorContext &context);
+ static void writeGetterFunction(TextStream &s,
+ const AbstractMetaField &metaField,
+ const GeneratorContext &context);
+ static void writeGetterFunction(TextStream &s,
+ const QPropertySpec &property,
+ const GeneratorContext &context);
+ static void writeSetterFunctionPreamble(TextStream &s,
+ const QString &name,
+ const QString &funcName,
+ const AbstractMetaType &type,
+ const GeneratorContext &context);
+ static void writeSetterFunction(TextStream &s,
+ const AbstractMetaField &metaField,
+ const GeneratorContext &context);
+ static void writeSetterFunction(TextStream &s,
+ const QPropertySpec &property,
+ const GeneratorContext &context);
+
+ static void writeRichCompareFunctionHeader(TextStream &s,
+ const QString &baseName,
+ const GeneratorContext &context);
+ void writeRichCompareFunction(TextStream &s, const GeneratorContext &context) const;
+ void writeSmartPointerRichCompareFunction(TextStream &s, const GeneratorContext &context) const;
+
+ static void writeEnumsInitialization(TextStream &s, AbstractMetaEnumList &enums);
+ static bool writeEnumInitialization(TextStream &s, const AbstractMetaEnum &metaEnum);
+
+ static void writeSignalInitialization(TextStream &s, const AbstractMetaClassCPtr &metaClass);
+
+ /// Writes the function that registers the multiple inheritance information
+ /// for the classes that need it.
+ static void writeMultipleInheritanceInitializerFunction(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass);
+ /// Writes the implementation of special cast functions, used when we need
+ /// to cast a class with multiple inheritance.
+ static void writeSpecialCastFunction(TextStream &s, const AbstractMetaClassCPtr &metaClass);
+
+ static void writePrimitiveConverterInitialization(TextStream &s,
+ const CustomConversionPtr &customConversion);
+ static void writeEnumConverterInitialization(TextStream &s, const AbstractMetaEnum &metaEnum);
+ static QString writeContainerConverterInitialization(TextStream &s,
+ 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);
+
+ void writeParentChildManagement(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ bool usesPyArgs,
+ bool userHeuristicForReturn) const;
+ bool writeParentChildManagement(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ int argIndex,
+ bool usePyArgs,
+ bool userHeuristicPolicy) const;
+ void writeReturnValueHeuristics(TextStream &s, const AbstractMetaFunctionCPtr &func) const;
+ static void writeInitQtMetaTypeFunctionBody(TextStream &s, const GeneratorContext &context);
+
+ /**
+ * Returns the multiple inheritance initializer function for the given class.
+ * \param metaClass the class for whom the function name must be generated.
+ * \return name of the multiple inheritance information initializer function or
+ * an empty string if there is no multiple inheritance in its ancestry.
+ */
+ static QString multipleInheritanceInitializerFunctionName(const AbstractMetaClassCPtr &metaClass);
+
+ /// Returns a list of all classes to which the given class could be cast.
+ static QStringList getAncestorMultipleInheritance(const AbstractMetaClassCPtr &metaClass);
+
+ /// Returns true if the given class supports the python number protocol
+ static bool supportsNumberProtocol(const AbstractMetaClassCPtr &metaClass);
+
+ /// Returns true if the given class supports the python sequence protocol
+ static bool supportsSequenceProtocol(const AbstractMetaClassCPtr &metaClass) ;
+
+ /// Returns true if the given class supports the python mapping protocol
+ static bool supportsMappingProtocol(const AbstractMetaClassCPtr &metaClass) ;
+
+ /// Returns true if generator should produce getters and setters for the given class.
+ static bool shouldGenerateGetSetList(const AbstractMetaClassCPtr &metaClass);
+
+ static bool hasHashFunction(const AbstractMetaClassCPtr &c);
+ static void writeHashFunction(TextStream &s, const GeneratorContext &context);
+
+ /// Write default implementations for sequence protocol
+ void writeDefaultSequenceMethods(TextStream &s, const GeneratorContext &context) const;
+ /// Helper function for writeStdListWrapperMethods.
+ static void writeIndexError(TextStream &s, const QString &errorMsg,
+ ErrorReturn errorReturn);
+
+ static QString writeReprFunctionHeader(TextStream &s, const GeneratorContext &context);
+ static QString writeReprFunction(TextStream &s,
+ const GeneratorContext &context,
+ uint indirections);
+ static QString writeSmartPointerReprFunction(TextStream &s,
+ const GeneratorContext &context);
+ static QString writeSmartPointerDirFunction(TextStream &s,
+ TextStream &definitionStream,
+ TextStream &signatureStream,
+ const GeneratorContext &context);
+ static void writeReprFunctionFooter(TextStream &s);
+ static void writePyMethodDefs(TextStream &s, const QString &className,
+ const QString &methodsDefinitions);
+
+ void writeModuleCodeSnips(TextStream &s, const CodeSnipList &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language) const;
+
+ static bool hasBoolCast(const AbstractMetaClassCPtr &metaClass)
+ { return boolCast(metaClass).has_value(); }
+
+ void clearTpFuncs();
+ static QString chopType(QString s);
+
+ QHash<QString, QString> m_tpFuncs;
+ QHash<QString, QString> m_nbFuncs;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(CppGenerator::CppSelfDefinitionFlags)
+
+TextStream &operator<<(TextStream &s, CppGenerator::ErrorReturn r);
+
+#endif // CPPGENERATOR_H
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator_container.cpp b/sources/shiboken6/generator/shiboken/cppgenerator_container.cpp
new file mode 100644
index 000000000..00e0cabea
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/cppgenerator_container.cpp
@@ -0,0 +1,272 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "cppgenerator.h"
+#include "generatorstrings.h"
+#include <abstractmetalang.h>
+#include "apiextractorresult.h"
+#include "ctypenames.h"
+#include "containertypeentry.h"
+#include "textstream.h"
+#include "typedatabase.h"
+
+#include <QtCore/QDebug>
+
+#include <algorithm>
+
+using namespace Qt::StringLiterals;
+
+// Write a PyMethodDef entry, allowing for registering C++ functions
+// under different names for Python.
+static void writeMethod(TextStream &s, const QString &privateObjType,
+ const char *cppName, const char *pythonName,
+ const char *flags)
+{
+ if (pythonName == nullptr)
+ pythonName = cppName;
+ s << "{\"" << pythonName << "\", reinterpret_cast<PyCFunction>("
+ << privateObjType << "::" << cppName << "), "<< flags
+ << ", \"" << /* doc */ pythonName << "\"},\n";
+}
+
+static inline void writeMethod(TextStream &s, const QString &privateObjType,
+ const char *cppName, const char *pythonName = nullptr)
+{
+ writeMethod(s, privateObjType, cppName, pythonName, "METH_O");
+}
+
+static inline void writeNoArgsMethod(TextStream &s, const QString &privateObjType,
+ const char *cppName, const char *pythonName = nullptr)
+{
+ writeMethod(s, privateObjType, cppName, pythonName, "METH_NOARGS");
+}
+
+static void writeSlot(TextStream &s, const char *tpName, const char *value)
+{
+ s << '{' << tpName << ", reinterpret_cast<void *>(" << value << ")},\n";
+}
+
+static void writeSlot(TextStream &s, const QString &privateObjType,
+ const char *tpName, const char *methodName)
+{
+ s << '{' << tpName << ", reinterpret_cast<void *>(" << privateObjType
+ << "::" << methodName << ")},\n";
+}
+
+// Write creation function from C++ reference, used by field accessors
+// and getters which are within extern "C"
+
+enum ContainerCreationFlag
+{
+ None = 0,
+ Const = 0x1,
+ Allocate = 0x2
+};
+
+Q_DECLARE_FLAGS(ContainerCreationFlags, ContainerCreationFlag)
+Q_DECLARE_OPERATORS_FOR_FLAGS(ContainerCreationFlags)
+
+static void writeContainerCreationFunc(TextStream &s,
+ const QString &funcName,
+ const QString &typeFName,
+ const QString &containerSignature,
+ ContainerCreationFlags flags = {})
+{
+
+ // creation function from C++ reference, used by field accessors
+ // which are within extern "C"
+ s << "extern \"C\" PyObject *" << funcName << '(';
+ if (flags.testFlag(ContainerCreationFlag::Const))
+ s << "const ";
+ s << containerSignature << "* ct)\n{\n" << indent
+ << "auto *container = PyObject_New(ShibokenContainer, " << typeFName << "());\n"
+ << "auto *d = new ShibokenSequenceContainerPrivate<"
+ << containerSignature << ">();\n";
+ if (flags.testFlag(ContainerCreationFlag::Allocate)) {
+ s << "d->m_list = new " << containerSignature << "(*ct);\n"
+ << "d->m_ownsList = true;\n";
+ } else if (flags.testFlag(ContainerCreationFlag::Const)) {
+ s << "d->m_list = const_cast<" << containerSignature << " *>(ct);\n"
+ << "d->m_const = true;\n";
+ } else {
+ s << "d->m_list = ct;\n";
+ }
+ s << "container->d = d;\n";
+ s << "return reinterpret_cast<PyObject *>(container);\n" << outdent
+ << "}\n\n";
+}
+
+// Generate template specialization of value converter helper
+void CppGenerator::writeOpaqueContainerValueConverter(TextStream &s,
+ const AbstractMetaType &valueType) const
+{
+ // Generate template specialization of value converter helper unless it is already there
+ const QString valueTypeName = valueType.cppSignature();
+ const QString checkFunction = cpythonCheckFunction(valueType);
+
+ s << "template <>\nstruct ShibokenContainerValueConverter<"
+ << valueTypeName << ">\n{\n";
+ // Type check
+ s << indent << "static bool checkValue(PyObject *" << PYTHON_ARG << ")\n{\n"
+ << indent << "return " << checkFunction;
+ if (!checkFunction.contains(u'('))
+ s << '(';
+ s << PYTHON_ARG << ");\n"
+ << outdent << "}\n\n";
+
+ // C++ to Python
+ const bool passByConstRef = valueType.indirectionsV().isEmpty()
+ && !valueType.isCppPrimitive();
+ s << "static PyObject *convertValueToPython(";
+ if (passByConstRef)
+ s << "const ";
+ s << valueTypeName << ' ';
+ if (passByConstRef)
+ s << '&';
+ s << CPP_ARG << ")\n{\n" << indent << "return ";
+ writeToPythonConversion(s, valueType, nullptr, CPP_ARG);
+ s << ";\n" << outdent << "}\n\n";
+
+ // Python to C++
+ s << "static std::optional<" << valueTypeName << "> convertValueToCpp(PyObject *"
+ << PYTHON_ARG << ")\n{\n" << indent;
+ s << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR << ";\n"
+ << "if (!(";
+ writeTypeCheck(s, valueType, PYTHON_ARG), isNumber(valueType.typeEntry());
+ s << ")) {\n" << indent
+ << "Shiboken::Errors::setWrongContainerType();\n"
+ << "return {};\n" << outdent << "}\n";
+ writePythonToCppTypeConversion(s, valueType, PYTHON_ARG, CPP_ARG, nullptr, {});
+ s << "return " << CPP_ARG << ";\n" << outdent << "}\n" << outdent << "};\n\n";
+}
+
+// Generate code for a type wrapping a C++ container instantiation
+CppGenerator::OpaqueContainerData
+ CppGenerator::writeOpaqueContainerConverterFunctions(TextStream &s,
+ const AbstractMetaType &containerType,
+ QSet<AbstractMetaType> *valueTypes) const
+{
+ OpaqueContainerData result;
+ const auto &valueType = containerType.instantiations().constFirst();
+ const auto containerTypeEntry = std::static_pointer_cast<const ContainerTypeEntry>(containerType.typeEntry());
+ result.name =
+ containerTypeEntry->opaqueContainerName(containerType.instantiationCppSignatures());
+
+ const auto cppSignature = containerType.cppSignature();
+ s << "\n// Binding for " << cppSignature << "\n\n";
+
+ if (!valueTypes->contains(valueType)) {
+ valueTypes->insert(valueType);
+ writeOpaqueContainerValueConverter(s, valueType);
+ }
+
+ const QString privateObjType = u"ShibokenSequenceContainerPrivate<"_s
+ + cppSignature + u'>';
+
+ // methods
+ const QString &containerName = containerType.name();
+ const bool isStdVector = containerName == u"std::vector";
+ const auto kind = containerTypeEntry->containerKind();
+ const bool isFixed = kind == ContainerTypeEntry::SpanContainer || containerName == u"std::array";
+ const QString methods = result.name + u"_methods"_s;
+ s << "static PyMethodDef " << methods << "[] = {\n" << indent;
+ if (!isFixed) {
+ writeMethod(s, privateObjType, "push_back");
+ writeMethod(s, privateObjType, "push_back", "append"); // Qt convention
+ writeNoArgsMethod(s, privateObjType, "clear");
+ writeNoArgsMethod(s, privateObjType, "pop_back");
+ writeNoArgsMethod(s, privateObjType, "pop_back", "removeLast"); // Qt convention
+ if (!isStdVector) {
+ writeMethod(s, privateObjType, "push_front");
+ writeMethod(s, privateObjType, "push_front", "prepend"); // Qt convention
+ writeNoArgsMethod(s, privateObjType, "pop_front");
+ writeMethod(s, privateObjType, "pop_front", "removeFirst"); // Qt convention
+ }
+ writeMethod(s, privateObjType, "reserve"); // SFINAE'd out for list
+ writeNoArgsMethod(s, privateObjType, "capacity");
+ }
+ writeNoArgsMethod(s, privateObjType, "data");
+ writeNoArgsMethod(s, privateObjType, "constData");
+ s << "{nullptr, nullptr, 0, nullptr} // Sentinel\n"
+ << outdent << "};\n\n";
+
+ // slots
+ const QString slotsList = result.name + u"_slots"_s;
+ s << "static PyType_Slot " << slotsList << "[] = {\n" << indent;
+ writeSlot(s, privateObjType, "Py_tp_init", "tpInit");
+ const auto *tpNew = containerTypeEntry->viewOn() == nullptr ? "tpNew" : "tpNewInvalid";
+ writeSlot(s, privateObjType, "Py_tp_new", tpNew);
+ writeSlot(s, privateObjType, "Py_tp_free", "tpFree");
+ writeSlot(s, "Py_tp_dealloc", "Sbk_object_dealloc"); // FIXME?
+ writeSlot(s, "Py_tp_methods", methods.toUtf8().constData());
+ writeSlot(s, privateObjType, "Py_sq_ass_item", "sqSetItem");
+ writeSlot(s, privateObjType, "Py_sq_length", "sqLen");
+ writeSlot(s, privateObjType, "Py_sq_item", "sqGetItem");
+ s << "{0, nullptr}\n" << outdent << "};\n\n";
+
+ // spec
+ const QString specName = result.name + u"_spec"_s;
+ const QString name = TypeDatabase::instance()->defaultPackageName()
+ + u'.' + result.name;
+ s << "static PyType_Spec " << specName << " = {\n" << indent
+ << "\"" << name.count(u'.') << ':' << name << "\",\n"
+ << "sizeof(ShibokenContainer),\n0,\nPy_TPFLAGS_DEFAULT,\n"
+ << slotsList << outdent << "\n};\n\n";
+
+ // type creation function that sets a key in the type dict.
+ const QString typeCreationFName = u"create"_s + result.name + u"Type"_s;
+ s << "static inline PyTypeObject *" << typeCreationFName << "()\n{\n" << indent
+ << "auto *result = reinterpret_cast<PyTypeObject *>(SbkType_FromSpec(&"
+ << specName << "));\nPy_INCREF(Py_True);\n"
+ << "Shiboken::AutoDecRef tpDict(PepType_GetDict(result));\n"
+ << "PyDict_SetItem(tpDict.object(), "
+ "Shiboken::PyMagicName::opaque_container(), Py_True);\n"
+ << "return result;\n" << outdent << "}\n\n";
+
+ // _TypeF() function
+ const QString typeFName = result.name + u"_TypeF"_s;
+ s << "static PyTypeObject *" << typeFName << "()\n{\n" << indent
+ << "static PyTypeObject *type = " << typeCreationFName
+ << "();\nreturn type;\n" << outdent << "}\n\n";
+
+ // creation functions from C++ references
+ ContainerCreationFlags flags;
+ if (kind == ContainerTypeEntry::SpanContainer)
+ flags.setFlag(ContainerCreationFlag::Allocate);
+
+ writeContainerCreationFunc(s, u"create"_s + result.name, typeFName,
+ containerType.cppSignature(), flags);
+ flags.setFlag(ContainerCreationFlag::Const);
+ writeContainerCreationFunc(s, u"createConst"_s + result.name, typeFName,
+ containerType.cppSignature(), flags);
+
+ // Check function
+ result.checkFunctionName = result.name + u"_Check"_s;
+ s << "extern \"C\" int " << result.checkFunctionName << "(PyObject *" << PYTHON_ARG
+ << ")\n{\n" << indent << "return " << PYTHON_ARG << " != nullptr && "
+ << PYTHON_ARG << " != Py_None && " << PYTHON_ARG << "->ob_type == "
+ << typeFName << "();\n" << outdent << "}\n\n";
+
+ // SBK converter Python to C++
+ result.pythonToConverterFunctionName = u"PythonToCpp"_s + result.name;
+ s << "extern \"C\" void " << result.pythonToConverterFunctionName
+ << "(PyObject *" << PYTHON_ARG << ", void *cppOut)\n{\n" << indent
+ << "auto *d = ShibokenSequenceContainerPrivate<" << cppSignature
+ << ">::get(" << PYTHON_ARG << ");\n"
+ << "*reinterpret_cast<" << cppSignature << "**>(cppOut) = d->m_list;\n"
+ << outdent << "}\n\n";
+
+ // SBK check function for converting Python to C++ that returns the converter
+ result.converterCheckFunctionName = u"is"_s + result.name + u"PythonToCppConvertible"_s;
+ s << "extern \"C\" PythonToCppFunc " << result.converterCheckFunctionName
+ << "(PyObject *" << PYTHON_ARG << ")\n{\n" << indent << "if ("
+ << result.checkFunctionName << '(' << PYTHON_ARG << "))\n" << indent
+ << "return " << result.pythonToConverterFunctionName << ";\n"
+ << outdent << "return {};\n" << outdent << "}\n\n";
+
+ QTextStream(&result.registrationCode) << "ob_type = reinterpret_cast<PyObject *>("
+ << typeFName
+ << "());\nPy_XINCREF(ob_type);\nPyModule_AddObject(module, \""
+ << result.name << "\", ob_type);\n";
+ return result;
+}
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp b/sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp
new file mode 100644
index 000000000..44b76f181
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp
@@ -0,0 +1,476 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "cppgenerator.h"
+#include "generatorstrings.h"
+#include "generatorcontext.h"
+#include <apiextractorresult.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <codesnip.h>
+#include <exception.h>
+#include <messages.h>
+#include <textstream.h>
+#include <overloaddata.h>
+#include <smartpointertypeentry.h>
+
+#include <QtCore/QDebug>
+
+using namespace Qt::StringLiterals;
+
+static const char smartPtrComment[] =
+ "// Try to find the 'name' attribute, by retrieving the PyObject for "
+ "the corresponding C++ object held by the smart pointer.\n";
+
+static QString smartPointerGetter(const GeneratorContext &context)
+{
+ const auto te = context.metaClass()->typeEntry();
+ Q_ASSERT(te->isSmartPointer());
+ return std::static_pointer_cast<const SmartPointerTypeEntry>(te)->getter();
+}
+
+struct callGetter
+{
+ explicit callGetter(const GeneratorContext &context) : m_context(context) {}
+
+ const GeneratorContext &m_context;
+};
+
+TextStream &operator<<(TextStream &str, const callGetter &c)
+{
+ str << "PyObject_CallMethod(self, \"" << smartPointerGetter(c.m_context) << "\", 0)";
+ return str;
+}
+
+// Helpers to collect all smart pointer pointee base classes
+static AbstractMetaClassCList
+ findSmartPointeeBaseClasses(const ApiExtractorResult &api,
+ const AbstractMetaType &smartPointerType)
+{
+ AbstractMetaClassCList result;
+ auto instantiationsTe = smartPointerType.instantiations().at(0).typeEntry();
+ auto targetClass = AbstractMetaClass::findClass(api.classes(), instantiationsTe);
+ if (targetClass != nullptr)
+ result = targetClass->allTypeSystemAncestors();
+ return result;
+}
+
+using ComparisonOperatorList = QList<AbstractMetaFunction::ComparisonOperatorType>;
+
+// Return the available comparison operators for smart pointers
+static ComparisonOperatorList smartPointeeComparisons(const GeneratorContext &context)
+{
+ Q_ASSERT(context.forSmartPointer());
+ auto te = context.preciseType().instantiations().constFirst().typeEntry();
+ if (isExtendedCppPrimitive(te)) { // Primitive pointee types have all
+ return {AbstractMetaFunction::OperatorEqual,
+ AbstractMetaFunction::OperatorNotEqual,
+ AbstractMetaFunction::OperatorLess,
+ AbstractMetaFunction::OperatorLessEqual,
+ AbstractMetaFunction::OperatorGreater,
+ AbstractMetaFunction::OperatorGreaterEqual};
+ }
+
+ const auto pointeeClass = context.pointeeClass();
+ if (!pointeeClass)
+ return {};
+
+ ComparisonOperatorList result;
+ const auto &comparisons =
+ pointeeClass->operatorOverloads(OperatorQueryOption::SymmetricalComparisonOp);
+ for (const auto &f : comparisons) {
+ const auto ct = f->comparisonOperatorType().value();
+ if (!result.contains(ct))
+ result.append(ct);
+ }
+ return result;
+}
+
+static bool hasParameterPredicate(const AbstractMetaFunctionCPtr &f)
+{
+ return !f->arguments().isEmpty();
+}
+
+void CppGenerator::generateSmartPointerClass(TextStream &s, const GeneratorContext &classContext)
+{
+ s.setLanguage(TextStream::Language::Cpp);
+ AbstractMetaClassCPtr metaClass = classContext.metaClass();
+ const auto typeEntry = std::static_pointer_cast<const SmartPointerTypeEntry>(metaClass->typeEntry());
+ const bool hasPointeeClass = classContext.pointeeClass() != nullptr;
+ const auto smartPointerType = typeEntry->smartPointerType();
+ const bool isValueHandle = smartPointerType ==TypeSystem::SmartPointerType::ValueHandle;
+
+ IncludeGroup includes{u"Extra includes"_s, typeEntry->extraIncludes()};
+ if (hasPointeeClass)
+ includes.append(classContext.pointeeClass()->typeEntry()->include());
+ includes.includes.append({Include::IncludePath, u"sbksmartpointer.h"_s});
+ generateIncludes(s, classContext, {includes});
+
+ s << '\n';
+
+ // class inject-code native/beginning
+ if (!typeEntry->codeSnips().isEmpty()) {
+ writeClassCodeSnips(s, typeEntry->codeSnips(),
+ TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode,
+ classContext);
+ s << '\n';
+ }
+
+ StringStream smd(TextStream::Language::Cpp);
+ StringStream md(TextStream::Language::Cpp);
+ StringStream signatureStream(TextStream::Language::Cpp);
+
+ s << openTargetExternC;
+
+ const auto &functionGroups = getFunctionGroups(metaClass);
+
+ // Skip all public methods of the smart pointer except for the special
+ // methods declared in the type entry.
+
+ auto ctors = metaClass->queryFunctions(FunctionQueryOption::Constructors);
+ if (!hasPointeeClass && !isValueHandle) { // Cannot generate "int*"
+ auto end = std::remove_if(ctors.begin(), ctors.end(), hasParameterPredicate);
+ ctors.erase(end, ctors.end());
+ }
+
+ if (!ctors.isEmpty()) {
+ OverloadData overloadData(ctors, api());
+ writeConstructorWrapper(s, overloadData, classContext);
+ writeSignatureInfo(signatureStream, overloadData);
+ }
+
+ if (!typeEntry->resetMethod().isEmpty()) {
+ auto it = functionGroups.constFind(typeEntry->resetMethod());
+ if (it == functionGroups.cend())
+ throw Exception(msgCannotFindSmartPointerMethod(typeEntry, typeEntry->resetMethod()));
+ AbstractMetaFunctionCList resets = it.value();
+ if (!hasPointeeClass && !isValueHandle) { // Cannot generate "int*"
+ auto end = std::remove_if(resets.begin(), resets.end(), hasParameterPredicate);
+ resets.erase(end, resets.end());
+ }
+ if (!resets.isEmpty())
+ writeMethodWrapper(s, md, signatureStream, resets, classContext);
+ }
+
+ auto it = functionGroups.constFind(typeEntry->getter());
+ if (it == functionGroups.cend() || it.value().size() != 1)
+ throw Exception(msgCannotFindSmartPointerGetter(typeEntry));
+
+ writeMethodWrapper(s, md, signatureStream, it.value(), classContext);
+
+ QStringList optionalMethods;
+ if (!typeEntry->refCountMethodName().isEmpty())
+ optionalMethods.append(typeEntry->refCountMethodName());
+ const QString valueCheckMethod = typeEntry->valueCheckMethod();
+ if (!valueCheckMethod.isEmpty() && !valueCheckMethod.startsWith(u"operator"))
+ optionalMethods.append(valueCheckMethod);
+ if (!typeEntry->nullCheckMethod().isEmpty())
+ optionalMethods.append(typeEntry->nullCheckMethod());
+
+ for (const QString &optionalMethod : optionalMethods) {
+ auto it = functionGroups.constFind(optionalMethod);
+ if (it == functionGroups.cend() || it.value().size() != 1)
+ throw Exception(msgCannotFindSmartPointerMethod(typeEntry, optionalMethod));
+ writeMethodWrapper(s, md, signatureStream, it.value(), classContext);
+ }
+
+ writeCopyFunction(s, md, signatureStream, classContext);
+ writeSmartPointerDirFunction(s, md, signatureStream, classContext);
+
+ const QString methodsDefinitions = md.toString();
+ const QString singleMethodDefinitions = smd.toString();
+
+ const QString className = chopType(cpythonTypeName(typeEntry));
+
+ // Write single method definitions
+ s << singleMethodDefinitions;
+
+ // Write methods definition
+ writePyMethodDefs(s, className, methodsDefinitions);
+
+ // Write tp_s/getattro function
+ const auto boolCastOpt = boolCast(metaClass);
+ writeSmartPointerGetattroFunction(s, classContext, boolCastOpt);
+ writeSmartPointerSetattroFunction(s, classContext);
+
+ if (boolCastOpt.has_value())
+ writeNbBoolFunction(classContext, boolCastOpt.value(), s);
+
+ if (smartPointerType == TypeSystem::SmartPointerType::Shared)
+ writeSmartPointerRichCompareFunction(s, classContext);
+
+ s << closeExternC;
+
+ if (hasHashFunction(metaClass))
+ writeHashFunction(s, classContext);
+
+ // Write tp_traverse and tp_clear functions.
+ writeTpTraverseFunction(s, metaClass);
+ writeTpClearFunction(s, metaClass);
+
+ writeClassDefinition(s, metaClass, classContext);
+
+ s << '\n';
+
+ writeConverterFunctions(s, metaClass, classContext);
+ // Implicit smart pointers conversions
+ writeSmartPointerConverterFunctions(s, classContext.preciseType());
+ writeClassRegister(s, metaClass, classContext, signatureStream);
+
+ // class inject-code native/end
+ if (!typeEntry->codeSnips().isEmpty()) {
+ writeClassCodeSnips(s, typeEntry->codeSnips(),
+ TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode,
+ classContext);
+ s << '\n';
+ }
+}
+
+void CppGenerator::writeSmartPointerConverterFunctions(TextStream &s,
+ const AbstractMetaType &smartPointerType) const
+{
+ const auto baseClasses = findSmartPointeeBaseClasses(api(), smartPointerType);
+ if (baseClasses.isEmpty())
+ return;
+
+ auto smartPointerTypeEntry =
+ std::static_pointer_cast<const SmartPointerTypeEntry>(smartPointerType.typeEntry());
+
+ // TODO: Missing conversion to smart pointer pointer type:
+
+ s << "// Register smartpointer conversion for all derived classes\n";
+ for (const auto &base : baseClasses) {
+ auto baseTe = base->typeEntry();
+ if (smartPointerTypeEntry->matchesInstantiation(baseTe)) {
+ if (auto opt = api().findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) {
+ const auto &smartTargetType = opt.value().type;
+ s << "// SmartPointer derived class: "
+ << smartTargetType.cppSignature() << "\n";
+ writePythonToCppConversionFunctions(s, smartPointerType,
+ smartTargetType, {}, {}, {});
+ }
+ }
+ }
+}
+
+void CppGenerator::writeSmartPointerCppSelfConversion(TextStream &s,
+ const GeneratorContext &context)
+{
+ Q_ASSERT(context.forSmartPointer());
+ s << cpythonWrapperCPtr(context.preciseType(), u"self"_s);
+}
+
+void CppGenerator::writeSmartPointerCppSelfDefinition(TextStream &s,
+ const GeneratorContext &context,
+ ErrorReturn errorReturn,
+ CppSelfDefinitionFlags flags)
+{
+ Q_ASSERT(context.forSmartPointer());
+ writeInvalidPyObjectCheck(s, u"self"_s, errorReturn);
+ writeCppSelfVarDef(s, flags);
+ writeSmartPointerCppSelfConversion(s, context);
+ s << ";\n";
+}
+
+void CppGenerator::writeSmartPointerConverterInitialization(TextStream &s,
+ const AbstractMetaType &type) const
+{
+ const QByteArray cppSignature = type.cppSignature().toUtf8();
+ auto writeConversionRegister = [&s](const AbstractMetaType &sourceType,
+ const QString &targetTypeName,
+ const QString &targetConverter)
+ {
+ const QString sourceTypeName = fixedCppTypeName(sourceType);
+ const QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName);
+ const QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName);
+
+ writeAddPythonToCppConversion(s, targetConverter, toCpp, isConv);
+ };
+
+ const auto classes = findSmartPointeeBaseClasses(api(), type);
+ if (classes.isEmpty())
+ return;
+
+ auto smartPointerTypeEntry = std::static_pointer_cast<const SmartPointerTypeEntry>(type.typeEntry());
+
+ s << "// Register SmartPointer converter for type '" << cppSignature << "'." << '\n'
+ << "///////////////////////////////////////////////////////////////////////////////////////\n\n";
+
+ for (const auto &base : classes) {
+ auto baseTe = base->typeEntry();
+ if (auto opt = api().findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) {
+ const auto &smartTargetType = opt.value().type;
+ s << "// Convert to SmartPointer derived class: ["
+ << smartTargetType.cppSignature() << "]\n";
+ const QString converter = u"Shiboken::Conversions::getConverter(\""_s
+ + smartTargetType.cppSignature() + u"\")"_s;
+ writeConversionRegister(type, fixedCppTypeName(smartTargetType), converter);
+ } else {
+ s << "// Class not found:" << type.instantiations().at(0).cppSignature();
+ }
+ }
+
+ s << "///////////////////////////////////////////////////////////////////////////////////////" << '\n' << '\n';
+}
+
+void CppGenerator::writeSmartPointerRichCompareFunction(TextStream &s,
+ const GeneratorContext &context) const
+{
+ static const char selfPointeeVar[] = "cppSelfPointee";
+ static const char cppArg0PointeeVar[] = "cppArg0Pointee";
+
+ const auto metaClass = context.metaClass();
+ QString baseName = cpythonBaseName(metaClass);
+ writeRichCompareFunctionHeader(s, baseName, context);
+
+ s << "if (";
+ writeTypeCheck(s, context.preciseType(), PYTHON_ARG);
+ s << ") {\n" << indent;
+ writeArgumentConversion(s, context.preciseType(), CPP_ARG0,
+ PYTHON_ARG, ErrorReturn::Default, metaClass);
+
+ const auto te = context.preciseType().typeEntry();
+ Q_ASSERT(te->isSmartPointer());
+ const auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(te);
+
+ s << "const auto *" << selfPointeeVar << " = " << CPP_SELF_VAR
+ << '.' << ste->getter() << "();\n";
+ s << "const auto *" << cppArg0PointeeVar << " = " << CPP_ARG0
+ << '.' << ste->getter() << "();\n";
+
+ // If we have an object without any comparisons, only generate a simple
+ // equality check by pointee address
+ auto availableOps = smartPointeeComparisons(context);
+ const bool comparePointeeAddressOnly = availableOps.isEmpty();
+ if (comparePointeeAddressOnly) {
+ availableOps << AbstractMetaFunction::OperatorEqual
+ << AbstractMetaFunction::OperatorNotEqual;
+ } else {
+ // For value types with operators, we complain about nullptr
+ s << "if (" << selfPointeeVar << " == nullptr || " << cppArg0PointeeVar
+ << " == nullptr) {\n" << indent
+ << "PyErr_SetString(PyExc_NotImplementedError, \"nullptr passed to comparison.\");\n"
+ << ErrorReturn::Default << '\n' << outdent << "}\n";
+ }
+
+ s << "bool " << CPP_RETURN_VAR << "= false;\n"
+ << "switch (op) {\n";
+ for (auto op : availableOps) {
+ s << "case " << AbstractMetaFunction::pythonRichCompareOpCode(op) << ":\n"
+ << indent << CPP_RETURN_VAR << " = ";
+ if (comparePointeeAddressOnly) {
+ s << selfPointeeVar << ' ' << AbstractMetaFunction::cppComparisonOperator(op)
+ << ' ' << cppArg0PointeeVar << ";\n";
+ } else {
+ // Shortcut for equality: Check pointee address
+ if (op == AbstractMetaFunction::OperatorEqual
+ || op == AbstractMetaFunction::OperatorLessEqual
+ || op == AbstractMetaFunction::OperatorGreaterEqual) {
+ s << selfPointeeVar << " == " << cppArg0PointeeVar << " || ";
+ }
+ // Generate object's comparison
+ s << "*" << selfPointeeVar << ' '
+ << AbstractMetaFunction::cppComparisonOperator(op) << " *"
+ << cppArg0PointeeVar << ";\n";
+ }
+ s << "break;\n" << outdent;
+
+ }
+ if (availableOps.size() < 6) {
+ s << "default:\n" << indent
+ << richCompareComment
+ << "return FallbackRichCompare(self, " << PYTHON_ARG << ", op);\n" << outdent;
+ }
+ s << "}\n" << PYTHON_RETURN_VAR << " = " << CPP_RETURN_VAR
+ << " ? Py_True : Py_False;\n"
+ << "Py_INCREF(" << PYTHON_RETURN_VAR << ");\n"
+ << outdent << "}\n"
+ << "return Shiboken::returnFromRichCompare(" << PYTHON_RETURN_VAR << ");\n"
+ << outdent << "}\n\n";
+}
+
+void CppGenerator::writeSmartPointerSetattroFunction(TextStream &s,
+ const GeneratorContext &context)
+{
+ Q_ASSERT(context.forSmartPointer());
+ writeSetattroDefinition(s, context.metaClass());
+ s << smartPtrComment
+ << "if (auto *rawObj = " << callGetter(context) << ") {\n" << indent
+ << "if (PyObject_HasAttr(rawObj, name) != 0)\n" << indent
+ << "return PyObject_GenericSetAttr(rawObj, name, value);\n" << outdent
+ << "Py_DECREF(rawObj);\n" << outdent
+ << "}\n";
+ writeSetattroDefaultReturn(s);
+}
+
+void CppGenerator::writeSmartPointerGetattroFunction(TextStream &s,
+ const GeneratorContext &context,
+ const BoolCastFunctionOptional &boolCast)
+{
+ Q_ASSERT(context.forSmartPointer());
+ const auto metaClass = context.metaClass();
+ writeGetattroDefinition(s, metaClass);
+ s << "PyObject *tmp = PyObject_GenericGetAttr(self, name);\n"
+ << "if (tmp)\n" << indent << "return tmp;\n" << outdent
+ << "if (PyErr_ExceptionMatches(PyExc_AttributeError) == 0)\n"
+ << indent << "return nullptr;\n" << outdent
+ << "PyErr_Clear();\n";
+
+ if (boolCast.has_value()) {
+ writeSmartPointerCppSelfDefinition(s, context);
+ s << "if (";
+ writeNbBoolExpression(s, boolCast.value(), true /* invert */);
+ s << ") {\n" << indent
+ << R"(PyTypeObject *tp = Py_TYPE(self);
+PyErr_Format(PyExc_AttributeError, "Attempt to retrieve '%s' from null object '%s'.",
+ Shiboken::String::toCString(name), tp->tp_name);
+return nullptr;
+)" << outdent << "}\n";
+ }
+
+ // This generates the code which dispatches access to member functions
+ // and fields from the smart pointer to its pointee.
+ s << smartPtrComment
+ << "if (auto *rawObj = " << callGetter(context) << ") {\n" << indent
+ << "if (auto *attribute = PyObject_GetAttr(rawObj, name))\n"
+ << indent << "tmp = attribute;\n" << outdent
+ << "Py_DECREF(rawObj);\n" << outdent
+ << "}\n"
+ << "if (!tmp) {\n" << indent
+ << R"(PyTypeObject *tp = Py_TYPE(self);
+PyErr_Format(PyExc_AttributeError,
+ "'%.50s' object has no attribute '%.400s'",
+ tp->tp_name, Shiboken::String::toCString(name));
+)" << outdent
+ << "}\n"
+ << "return tmp;\n" << outdent << "}\n\n";
+}
+
+QString CppGenerator::writeSmartPointerReprFunction(TextStream &s,
+ const GeneratorContext &context)
+{
+ const auto metaClass = context.metaClass();
+ QString funcName = writeReprFunctionHeader(s, context);
+ s << "Shiboken::AutoDecRef pointee(" << callGetter(context) << ");\n"
+ << "return Shiboken::SmartPointer::repr(self, pointee);\n";
+ writeReprFunctionFooter(s);
+ return funcName;
+}
+
+QString CppGenerator::writeSmartPointerDirFunction(TextStream &s, TextStream &definitionStream,
+ TextStream &signatureStream,
+ const GeneratorContext &context)
+{
+ QString funcName = cpythonBaseName(context.metaClass()) + u"__dir__"_s;
+
+ signatureStream << fullPythonClassName(context.metaClass()) << ".__dir__()\n";
+ definitionStream << PyMethodDefEntry{u"__dir__"_s, funcName, {"METH_NOARGS"_ba}, {}}
+ << ",\n";
+
+ s << "extern \"C\"\n{\n"
+ << "static PyObject *" << funcName << "(PyObject *self)\n{\n" << indent
+ << "Shiboken::AutoDecRef pointee(" << callGetter(context) << ");\n"
+ << "return Shiboken::SmartPointer::dir(self, pointee);\n"
+ << outdent << "}\n} // extern C\n\n";
+ return funcName;
+}
diff --git a/sources/shiboken6/generator/shiboken/ctypenames.h b/sources/shiboken6/generator/shiboken/ctypenames.h
new file mode 100644
index 000000000..f665b30ff
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/ctypenames.h
@@ -0,0 +1,31 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CTYPENAMES_H
+#define CTYPENAMES_H
+
+#include <QtCore/QString>
+
+constexpr auto boolT = QLatin1StringView("bool");
+constexpr auto intT = QLatin1StringView("int");
+constexpr auto unsignedT = QLatin1StringView("unsigned");
+constexpr auto unsignedIntT = QLatin1StringView("unsigned int");
+constexpr auto longT = QLatin1StringView("long");
+constexpr auto unsignedLongT = QLatin1StringView("unsigned long");
+constexpr auto shortT = QLatin1StringView("short");
+constexpr auto unsignedShortT = QLatin1StringView("unsigned short");
+constexpr auto unsignedCharT = QLatin1StringView("unsigned char");
+constexpr auto longLongT = QLatin1StringView("long long");
+constexpr auto unsignedLongLongT = QLatin1StringView("unsigned long long");
+constexpr auto charT = QLatin1StringView("char");
+constexpr auto floatT = QLatin1StringView("float");
+constexpr auto doubleT = QLatin1StringView("double");
+constexpr auto constCharPtrT = QLatin1StringView("const char*");
+
+constexpr auto qByteArrayT = QLatin1StringView("QByteArray");
+constexpr auto qMetaObjectT = QLatin1StringView("QMetaObject");
+constexpr auto qObjectT = QLatin1StringView("QObject");
+constexpr auto qStringT = QLatin1StringView("QString");
+constexpr auto qVariantT = QLatin1StringView("QVariant");
+
+#endif // CTYPENAMES_H
diff --git a/sources/shiboken6/generator/shiboken/generatorargument.cpp b/sources/shiboken6/generator/shiboken/generatorargument.cpp
new file mode 100644
index 000000000..e81ad0797
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/generatorargument.cpp
@@ -0,0 +1,110 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "generatorargument.h"
+#include <abstractmetatype.h>
+#include <messages.h>
+#include <typesystem.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QSet>
+
+static bool isCppPrimitiveString(const AbstractMetaType &type)
+{
+ return type.referenceType() == NoReference && type.indirections() == 1
+ && AbstractMetaType::cppSignedCharTypes().contains(type.name());
+}
+
+GeneratorArgument GeneratorArgument::fromMetaType(const AbstractMetaType &type)
+{
+ GeneratorArgument result;
+
+ const auto typeEntry = type.typeEntry();
+ if (typeEntry->isCustom() || typeEntry->isVarargs())
+ return result;
+
+ result.indirections = -type.indirectionsV().size();
+ if (isCppPrimitiveString(type)
+ || type.isVoidPointer()
+ || type.typeUsagePattern() == AbstractMetaType::NativePointerAsArrayPattern) {
+ result.indirections += 1;
+ }
+
+ if (typeEntry->isEnum()) {
+ result.type = Type::Enum;
+ } else if (typeEntry->isFlags()) {
+ result.type = Type::Flags;
+ } else if (typeEntry->isContainer()) {
+ result.type = Type::Container;
+ } else {
+ if (typeEntry->isPrimitive())
+ result.type = Type::Primitive;
+
+ const AbstractMetaTypeList &nestedArrayTypes = type.nestedArrayTypes();
+ if (!nestedArrayTypes.isEmpty()) {
+ if (nestedArrayTypes.constLast().isCppPrimitive()) {
+ result.type = Type::CppPrimitiveArray;
+ } else {
+ static QSet<QString> warnedTypes;
+ const QString &signature = type.cppSignature();
+ if (!warnedTypes.contains(signature)) {
+ warnedTypes.insert(signature);
+ qWarning("%s", qPrintable(msgUnknownArrayPointerConversion(signature)));
+ }
+ result.indirections -= 1;
+ }
+ }
+ }
+
+ if (result.type == Type::Other || result.type == Type::Primitive) {
+ if (type.valueTypeWithCopyConstructorOnlyPassed()) {
+ result.flags.setFlag(Flag::TreatAsPointer);
+ } else if ((type.isObjectType() || type.isPointer())
+ && !type.isUserPrimitive() && !type.isExtendedCppPrimitive()) {
+ result.flags.setFlag(Flag::PointerOrObjectType);
+ } else if (type.referenceType() == LValueReference
+ && !type.isUserPrimitive()
+ && !type.isExtendedCppPrimitive()) {
+ result.flags.setFlag(Flag::MayHaveImplicitConversion);
+ }
+ }
+
+ // For implicit conversions or containers, either value or pointer conversion
+ // may occur. An implicit conversion uses value conversion whereas the object
+ // itself uses pointer conversion. For containers, the PyList/container
+ // conversion is by value whereas opaque containers use pointer conversion.
+ // For a container passed by pointer, a local variable is also needed.
+ if (result.flags.testFlag(Flag::MayHaveImplicitConversion)
+ || type.generateOpaqueContainer()
+ || (result.type == Type::Container && result.indirections != 0)) {
+ result.flags.setFlag(Flag::ValueOrPointer);
+ }
+
+ if (result.type == Type::CppPrimitiveArray) {
+ result.conversion = Conversion::CppPrimitiveArray;
+ } else if (result.flags.testFlag(Flag::ValueOrPointer)) {
+ result.conversion = Conversion::ValueOrPointer;
+ ++result.indirections;
+ } else if (result.flags.testAnyFlags(Flag::TreatAsPointer | Flag::PointerOrObjectType)) {
+ result.conversion = Conversion::Pointer;
+ ++result.indirections;
+ }
+
+ return result;
+}
+
+QDebug operator<<(QDebug debug, const GeneratorArgument &a)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "GeneratorArgument(" << a.type;
+ if (a.conversion != GeneratorArgument::Conversion::Default)
+ debug << ", conversion=" << a.conversion;
+ if (a.flags)
+ debug << ", flags(" << a.flags;
+ if (a.indirections != 0)
+ debug << ", indirections=" << a.indirections;
+ debug << ')';
+ return debug;
+}
diff --git a/sources/shiboken6/generator/shiboken/generatorargument.h b/sources/shiboken6/generator/shiboken/generatorargument.h
new file mode 100644
index 000000000..385ad0f63
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/generatorargument.h
@@ -0,0 +1,60 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef GENERATORARGUMENT_H
+#define GENERATORARGUMENT_H
+
+#include <QtCore/QFlags>
+#include <QtCore/qobjectdefs.h>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+class AbstractMetaType;
+
+/// A struct containing information on how the generator processes a function argument.
+struct GeneratorArgument
+{
+ Q_GADGET
+
+public:
+ enum class Type {
+ Other,
+ Primitive,
+ Enum,
+ Flags,
+ Container,
+ CppPrimitiveArray
+ };
+ Q_ENUM(Type)
+
+ enum class Conversion {
+ Default,
+ CppPrimitiveArray, // Similar to Default except default values
+ Pointer,
+ ValueOrPointer
+ };
+ Q_ENUM(Conversion)
+
+ enum class Flag {
+ TreatAsPointer = 0x1,
+ PointerOrObjectType = 0x2,
+ MayHaveImplicitConversion = 0x4,
+ ValueOrPointer = 0x8,
+ };
+ Q_ENUM(Flag)
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ static GeneratorArgument fromMetaType(const AbstractMetaType &type);
+
+ Flags flags;
+ /// Indirections from generated "cppArg<n>" variable to function argument.
+ qsizetype indirections = 0;
+ Type type = Type::Other;
+ Conversion conversion = Conversion::Default;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(GeneratorArgument::Flags)
+
+QDebug operator<<(QDebug debug, const GeneratorArgument &a);
+
+#endif // GENERATORARGUMENT_H
diff --git a/sources/shiboken6/generator/shiboken/generatorstrings.h b/sources/shiboken6/generator/shiboken/generatorstrings.h
new file mode 100644
index 000000000..9ce91e599
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/generatorstrings.h
@@ -0,0 +1,39 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef GENERATORSTRINGS_H
+#define GENERATORSTRINGS_H
+
+#include <QtCore/QString>
+
+QString CPP_ARG_N(int i);
+QString CPP_ARG_REMOVED(int i);
+
+constexpr auto CPP_RETURN_VAR = QLatin1StringView("cppResult");
+constexpr auto CPP_SELF_VAR = QLatin1StringView("cppSelf");
+constexpr auto CPP_ARG = QLatin1StringView("cppArg");
+constexpr auto NULL_PTR = QLatin1StringView("nullptr");
+constexpr auto PYTHON_ARG = QLatin1StringView("pyArg");
+constexpr auto PYTHON_ARGS = QLatin1StringView("pyArgs");
+constexpr auto PYTHON_OVERRIDE_VAR = QLatin1StringView("pyOverride");
+constexpr auto PYTHON_RETURN_VAR = QLatin1StringView("pyResult");
+constexpr auto PYTHON_SELF_VAR = QLatin1StringView("self");
+constexpr auto PYTHON_TO_CPP_VAR = QLatin1StringView("pythonToCpp");
+
+constexpr auto CONV_RULE_OUT_VAR_SUFFIX = QLatin1StringView("_out");
+constexpr auto BEGIN_ALLOW_THREADS
+ = QLatin1StringView("PyThreadState *_save = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS");
+constexpr auto END_ALLOW_THREADS
+ = QLatin1StringView("PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS");
+
+constexpr auto REPR_FUNCTION = QLatin1StringView("__repr__");
+
+constexpr auto CPP_ARG0 = QLatin1StringView("cppArg0");
+
+extern const char *const METHOD_DEF_SENTINEL;
+extern const char *const PYTHON_TO_CPPCONVERSION_STRUCT;
+extern const char *const openTargetExternC;
+extern const char *const closeExternC;
+extern const char *const richCompareComment;
+
+#endif // GENERATORSTRINGS_H
diff --git a/sources/shiboken6/generator/shiboken/headergenerator.cpp b/sources/shiboken6/generator/shiboken/headergenerator.cpp
new file mode 100644
index 000000000..7cec9c38e
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/headergenerator.cpp
@@ -0,0 +1,961 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "headergenerator.h"
+#include "configurablescope.h"
+#include "generatorcontext.h"
+#include <apiextractorresult.h>
+#include <abstractmetaargument.h>
+#include <abstractmetaenum.h>
+#include <abstractmetafield.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <abstractmetalang_helpers.h>
+#include <codesnip.h>
+#include <clangparser/compilersupport.h>
+#include <exception.h>
+#include <typedatabase.h>
+#include <reporthandler.h>
+#include <textstream.h>
+#include <fileout.h>
+#include "containertypeentry.h"
+#include "enumtypeentry.h"
+#include "flagstypeentry.h"
+#include <messages.h>
+#include "namespacetypeentry.h"
+#include "primitivetypeentry.h"
+#include "typedefentry.h"
+#include "typesystemtypeentry.h"
+
+#include "qtcompat.h"
+
+#include <algorithm>
+#include <set>
+
+#include <QtCore/QDir>
+#include <QtCore/QTextStream>
+#include <QtCore/QVariant>
+#include <QtCore/QDebug>
+
+using namespace Qt::StringLiterals;
+
+struct IndexValue
+{
+ QString name; // "SBK_..."
+ int value;
+ QString comment;
+};
+
+TextStream &operator<<(TextStream &s, const IndexValue &iv)
+{
+ s << " " << AlignedField(iv.name, 56) << " = " << iv.value << ',';
+ if (!iv.comment.isEmpty())
+ s << " // " << iv.comment;
+ s << '\n';
+ return s;
+}
+
+// PYSIDE-504: Handling the "protected hack"
+// The problem: Creating wrappers when the class has private destructors.
+// You can see an example on Windows in qclipboard_wrapper.h and others.
+// Simply search for the text "// C++11: need to declare (unimplemented) destructor".
+// The protected hack is the definition "#define protected public".
+// For most compilers, this "hack" is enabled, because the problem of private
+// destructors simply vanishes.
+//
+// If one does not want to use this hack, then a new problem arises:
+// C++11 requires that a destructor is declared in a wrapper class when it is
+// private in the base class. There is no implementation allowed!
+//
+// Unfortunately, MSVC in recent versions supports C++11, and due to restrictive
+// rules, it is impossible to use the hack with this compiler.
+// More unfortunate: Clang, when C++11 is enabled, also enforces a declaration
+// of a private destructor, but it falsely then creates a linker error!
+//
+// Originally, we wanted to remove the protected hack. But due to the Clang
+// problem, we gave up on removal of the protected hack and use it always
+// when we can. This might change again when the Clang problem is solved.
+
+static bool alwaysGenerateDestructorDeclaration()
+{
+ return clang::compiler() == Compiler::Msvc;
+}
+
+const char *HeaderGenerator::protectedHackDefine = R"(// Workaround to access protected functions
+#ifndef protected
+# define protected public
+#endif
+
+)";
+
+QString HeaderGenerator::fileNameForContext(const GeneratorContext &context) const
+{
+ return headerFileNameForContext(context);
+}
+
+void HeaderGenerator::writeCopyCtor(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass)
+{
+ s << wrapperName(metaClass) << "(const " << metaClass->qualifiedCppName()
+ << "& self) : " << metaClass->qualifiedCppName() << "(self)\n{\n}\n\n";
+}
+
+static void writeProtectedEnums(TextStream &s, const AbstractMetaClassCPtr &metaClass)
+{
+ const QString name = metaClass->qualifiedCppName();
+ for (const auto &e : metaClass->enums()) {
+ if (e.isProtected())
+ s << "using " << name << "::" << e.name() << ";\n";
+ }
+}
+
+void HeaderGenerator::generateClass(TextStream &s, const GeneratorContext &classContext)
+{
+ const AbstractMetaClassCPtr metaClass = classContext.metaClass();
+
+ // write license comment
+ s << licenseComment();
+
+ QString wrapperName = classContext.effectiveClassName();
+ QString outerHeaderGuard = getFilteredCppSignatureString(wrapperName);
+
+ // Header
+ s << "#ifndef SBK_" << outerHeaderGuard << "_H\n";
+ s << "#define SBK_" << outerHeaderGuard << "_H\n\n";
+
+ if (!avoidProtectedHack())
+ s << protectedHackDefine;
+
+ // Includes
+ s << metaClass->typeEntry()->include() << '\n';
+ for (auto &inst : metaClass->templateBaseClassInstantiations())
+ s << inst.typeEntry()->include();
+
+ if (classContext.useWrapper())
+ writeWrapperClass(s, wrapperName, classContext);
+
+ s << "#endif // SBK_" << outerHeaderGuard << "_H\n\n";
+}
+
+void HeaderGenerator::writeWrapperClass(TextStream &s,
+ const QString &wrapperName,
+ const GeneratorContext &classContext) const
+{
+ const auto metaClass = classContext.metaClass();
+
+ if (avoidProtectedHack()) {
+ const auto includeGroups = classIncludes(metaClass);
+ for( const auto &includeGroup : includeGroups)
+ s << includeGroup;
+ }
+
+ if (usePySideExtensions() && isQObject(metaClass))
+ s << "namespace PySide { class DynamicQMetaObject; }\n\n";
+
+ writeWrapperClassDeclaration(s, wrapperName, classContext);
+
+ // PYSIDE-500: Use also includes for inherited wrapper classes other
+ // modules, because without the protected hack, we sometimes need to
+ // cast inherited wrappers. CppGenerator generates include statements for
+ // the classes of the current module. For other modules, we insert the
+ // declarations as recursive headers, since wrapper headers are not
+ // installed. This keeps the file structure as simple as before the
+ // enhanced inheritance.
+ if (avoidProtectedHack()) {
+ const auto &baseClasses = allBaseClasses(classContext.metaClass());
+ for (const auto &baseClass : baseClasses) {
+ const auto gen = baseClass->typeEntry()->codeGeneration();
+ if (gen == TypeEntry::GenerateForSubclass) { // other module
+ const auto baseContext = contextForClass(baseClass);
+ if (baseContext.useWrapper())
+ writeInheritedWrapperClassDeclaration(s, baseContext);
+ }
+ }
+ }
+}
+
+void HeaderGenerator::writeInheritedWrapperClassDeclaration(TextStream &s,
+ const GeneratorContext &classContext) const
+{
+ const QString wrapperName = classContext.effectiveClassName();
+ const QString innerHeaderGuard =
+ getFilteredCppSignatureString(wrapperName).toUpper();
+
+ s << "# ifndef SBK_" << innerHeaderGuard << "_H\n"
+ << "# define SBK_" << innerHeaderGuard << "_H\n\n"
+ << "// Inherited base class:\n";
+
+ writeWrapperClassDeclaration(s, wrapperName, classContext);
+
+ s << "# endif // SBK_" << innerHeaderGuard << "_H\n\n";
+}
+
+void HeaderGenerator::writeWrapperClassDeclaration(TextStream &s,
+ const QString &wrapperName,
+ const GeneratorContext &classContext) const
+{
+ const AbstractMetaClassCPtr metaClass = classContext.metaClass();
+ const auto typeEntry = metaClass->typeEntry();
+ InheritedOverloadSet inheritedOverloads;
+
+ // Class
+ s << "class " << wrapperName
+ << " : public " << metaClass->qualifiedCppName()
+ << "\n{\npublic:\n" << indent
+ << wrapperName << "(const " << wrapperName << " &) = delete;\n"
+ << wrapperName << "& operator=(const " << wrapperName << " &) = delete;\n"
+ << wrapperName << '(' << wrapperName << " &&) = delete;\n"
+ << wrapperName << "& operator=(" << wrapperName << " &&) = delete;\n\n";
+
+ // Make protected enums accessible
+ if (avoidProtectedHack()) {
+ recurseClassHierarchy(metaClass, [&s] (const AbstractMetaClassCPtr &metaClass) {
+ writeProtectedEnums(s, metaClass);
+ return false;
+ });
+ }
+
+ if (avoidProtectedHack() && metaClass->hasProtectedFields()) {
+ s << "\n// Make protected fields accessible\n";
+ const QString name = metaClass->qualifiedCppName();
+ for (const auto &f : metaClass->fields()) {
+ if (f.isProtected())
+ s << "using " << name << "::" << f.originalName() << ";\n";
+ }
+ s << '\n';
+ }
+
+ int maxOverrides = 0;
+ for (const auto &func : metaClass->functions()) {
+ const auto generation = functionGeneration(func);
+ writeFunction(s, func, &inheritedOverloads, generation);
+ // PYSIDE-803: Build a boolean cache for unused overrides.
+ if (generation.testFlag(FunctionGenerationFlag::VirtualMethod))
+ maxOverrides++;
+ }
+ if (!maxOverrides)
+ maxOverrides = 1;
+
+ //destructor
+ // PYSIDE-504: When C++ 11 is used, then the destructor must always be declared.
+ if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor()
+ || alwaysGenerateDestructorDeclaration()) {
+ if (avoidProtectedHack() && metaClass->hasPrivateDestructor())
+ s << "// C++11: need to declare (unimplemented) destructor because "
+ "the base class destructor is private.\n";
+ s << '~' << wrapperName << "()";
+ if (metaClass->hasVirtualDestructor())
+ s << " override";
+ s << ";\n";
+ }
+
+ writeClassCodeSnips(s, typeEntry->codeSnips(),
+ TypeSystem::CodeSnipPositionDeclaration, TypeSystem::NativeCode,
+ classContext);
+
+ if (shouldGenerateMetaObjectFunctions(metaClass)) {
+ s << R"(
+const ::QMetaObject * metaObject() const override;
+int qt_metacall(QMetaObject::Call call, int id, void **args) override;
+void *qt_metacast(const char *_clname) override;
+)";
+ }
+
+ if (!inheritedOverloads.isEmpty()) {
+ s << "// Inherited overloads, because the using keyword sux\n";
+ for (const auto &func : std::as_const(inheritedOverloads))
+ writeMemberFunctionWrapper(s, func);
+ }
+
+ if (usePySideExtensions())
+ s << "static void pysideInitQtMetaTypes();\n";
+
+ s << "void resetPyMethodCache();\n"
+ << outdent << "private:\n" << indent;
+
+ if (!metaClass->userAddedPythonOverrides().isEmpty()) {
+ for (const auto &f : metaClass->userAddedPythonOverrides())
+ s << functionSignature(f, {}, {}, Generator::OriginalTypeDescription) << ";\n";
+ s << '\n';
+ }
+
+ s << "mutable bool m_PyMethodCache[" << maxOverrides << "];\n"
+ << outdent << "};\n\n";
+}
+
+// Write an inline wrapper around a function
+void HeaderGenerator::writeMemberFunctionWrapper(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ const QString &postfix) const
+{
+ Q_ASSERT(!func->isConstructor() && !func->isOperatorOverload());
+ s << "inline ";
+ s << functionSignature(func, {}, postfix, Generator::OriginalTypeDescription)
+ << " { ";
+ if (!func->isVoid())
+ s << "return ";
+ if (!func->isAbstract()) {
+ // Use implementingClass() in case of multiple inheritance (for example
+ // function setProperty() being inherited from QObject and
+ // QDesignerPropertySheetExtension).
+ auto klass = func->implementingClass();
+ if (klass == nullptr)
+ klass = func->ownerClass();
+ s << klass->qualifiedCppName() << "::";
+ }
+ s << func->originalName() << '(';
+ const AbstractMetaArgumentList &arguments = func->arguments();
+ for (qsizetype i = 0, size = arguments.size(); i < size; ++i) {
+ if (i > 0)
+ s << ", ";
+ const AbstractMetaArgument &arg = arguments.at(i);
+ const auto &type = arg.type();
+ TypeEntryCPtr enumTypeEntry;
+ if (type.isFlags())
+ enumTypeEntry = std::static_pointer_cast<const FlagsTypeEntry>(type.typeEntry())->originator();
+ else if (type.isEnum())
+ enumTypeEntry = type.typeEntry();
+ if (enumTypeEntry) {
+ s << type.cppSignature() << '(' << arg.name() << ')';
+ } else if (type.passByValue() && type.isUniquePointer()) {
+ s << stdMove(arg.name());
+ } else {
+ s << arg.name();
+ }
+ }
+ s << "); }\n";
+}
+
+void HeaderGenerator::writeFunction(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ InheritedOverloadSet *inheritedOverloads,
+ FunctionGeneration generation) const
+{
+
+ // do not write copy ctors here.
+ if (generation.testFlag(FunctionGenerationFlag::WrapperSpecialCopyConstructor)) {
+ writeCopyCtor(s, func->ownerClass());
+ return;
+ }
+
+ if (generation.testFlag(FunctionGenerationFlag::ProtectedWrapper))
+ writeMemberFunctionWrapper(s, func, u"_protected"_s);
+
+ if (generation.testFlag(FunctionGenerationFlag::WrapperConstructor)) {
+ Options option = func->hasSignatureModifications()
+ ? Generator::OriginalTypeDescription : Generator::NoOption;
+ s << functionSignature(func, {}, {}, option) << ";\n";
+ return;
+ }
+
+ const bool isVirtual = generation.testFlag(FunctionGenerationFlag::VirtualMethod);
+ if (isVirtual) {
+ s << functionSignature(func, {}, {}, Generator::OriginalTypeDescription)
+ << " override;\n";
+ }
+
+ // Check if this method hide other methods in base classes
+ if (isVirtual) {
+ for (const auto &f : func->ownerClass()->functions()) {
+ if (f != func
+ && !f->isConstructor()
+ && !f->isPrivate()
+ && !f->isVirtual()
+ && !f->isAbstract()
+ && !f->isStatic()
+ && f->name() == func->name()) {
+ inheritedOverloads->insert(f);
+ }
+ }
+
+ // TODO: when modified an abstract method ceases to be virtual but stays abstract
+ //if (func->isModifiedRemoved() && func->isAbstract()) {
+ //}
+ }
+}
+
+// Find equivalent typedefs "using Foo=QList<int>", "using Bar=QList<int>"
+static AbstractMetaClassCPtr
+ findEquivalentTemplateTypedef(const AbstractMetaClassCList &haystack,
+ const AbstractMetaClassCPtr &needle)
+{
+ auto templateBaseClass = needle->templateBaseClass();
+ const auto &instantiations = needle->templateBaseClassInstantiations();
+ for (const auto &candidate : haystack) {
+ if (candidate->isTypeDef()
+ && candidate->templateBaseClass() == templateBaseClass
+ && candidate->templateBaseClassInstantiations() == instantiations) {
+ return candidate;
+ }
+ }
+ return nullptr;
+}
+
+void HeaderGenerator::collectTypeEntryTypeIndexes(const ApiExtractorResult &api,
+ const TypeEntryCPtr &typeEntry,
+ IndexValues *indexValues)
+{
+ if (!typeEntry || !typeEntry->generateCode())
+ return;
+ const int typeIndex = typeEntry->sbkIndex();
+ indexValues->append({getTypeIndexVariableName(typeEntry), typeIndex, {}});
+
+ if (typeEntry->isComplex()) {
+ // For a typedef "using Foo=QList<int>", write a type index
+ // SBK_QLIST_INT besides SBK_FOO which is then matched by function
+ // argument. Check against duplicate typedefs for the same types.
+ const auto cType = std::static_pointer_cast<const ComplexTypeEntry>(typeEntry);
+ if (cType->baseContainerType()) {
+ auto metaClass = AbstractMetaClass::findClass(api.classes(), cType);
+ Q_ASSERT(metaClass != nullptr);
+ if (metaClass->isTypeDef()
+ && metaClass->templateBaseClass() != nullptr
+ && findEquivalentTemplateTypedef(m_alternateTemplateIndexes,
+ metaClass) == nullptr) {
+ const QString indexVariable =
+ getTypeAlternateTemplateIndexVariableName(metaClass);
+ indexValues->append({indexVariable, typeIndex, {}});
+ m_alternateTemplateIndexes.append(m_alternateTemplateIndexes);
+ }
+ }
+ }
+ if (typeEntry->isEnum()) {
+ auto ete = std::static_pointer_cast<const EnumTypeEntry>(typeEntry);
+ if (ete->flags())
+ collectTypeEntryTypeIndexes(api, ete->flags(), indexValues);
+ }
+}
+
+void HeaderGenerator::collectClassTypeIndexes(const ApiExtractorResult &api,
+ const AbstractMetaClassCPtr &metaClass,
+ IndexValues *indexValues)
+{
+ auto typeEntry = metaClass->typeEntry();
+ if (!typeEntry->generateCode())
+ return;
+ // enum indices are required for invisible namespaces as well.
+ for (const AbstractMetaEnum &metaEnum : metaClass->enums()) {
+ if (!metaEnum.isPrivate())
+ collectTypeEntryTypeIndexes(api, metaEnum.typeEntry(), indexValues);
+ }
+ if (NamespaceTypeEntry::isVisibleScope(typeEntry))
+ collectTypeEntryTypeIndexes(api, typeEntry, indexValues);
+}
+
+// Format the typedefs for the typedef entries to be generated
+static void formatTypeDefEntries(TextStream &s)
+{
+ QList<TypedefEntryCPtr> entries;
+ const auto typeDbEntries = TypeDatabase::instance()->typedefEntries();
+ for (auto it = typeDbEntries.cbegin(), end = typeDbEntries.cend(); it != end; ++it) {
+ if (it.value()->generateCode() != 0)
+ entries.append(it.value());
+ }
+ if (entries.isEmpty())
+ return;
+ s << "\n// typedef entries\n";
+ for (const auto &e : entries) {
+ const QString name = e->qualifiedCppName();
+ // Fixme: simplify by using nested namespaces in C++ 17.
+ const auto components = QStringView{name}.split(u"::");
+ const auto nameSpaceCount = components.size() - 1;
+ for (qsizetype n = 0; n < nameSpaceCount; ++n)
+ s << "namespace " << components.at(n) << " {\n";
+ s << "using " << components.constLast() << " = " << e->sourceType() << ";\n";
+ for (qsizetype n = 0; n < nameSpaceCount; ++n)
+ s << "}\n";
+ }
+ s << '\n';
+}
+
+// Helpers for forward-declaring classes in the module header for the
+// specialization of the SbkType template functions. This is possible if the
+// class does not have inner types or enums which need to be known.
+static bool canForwardDeclare(const AbstractMetaClassCPtr &c)
+{
+ if (c->isNamespace() || !c->enums().isEmpty()
+ || !c->innerClasses().isEmpty() || c->isTypeDef()) {
+ return false;
+ }
+ if (auto encl = c->enclosingClass())
+ return encl->isNamespace();
+ return true;
+}
+
+static void writeForwardDeclaration(TextStream &s, const AbstractMetaClassCPtr &c)
+{
+ Q_ASSERT(!c->isNamespace());
+ const bool isStruct = c->attributes().testFlag(AbstractMetaClass::Struct);
+ s << (isStruct ? "struct " : "class ");
+ // Do not use name as this can be modified/renamed for target lang.
+ const QString qualifiedCppName = c->qualifiedCppName();
+ const auto lastQualifier = qualifiedCppName.lastIndexOf(u':');
+ if (lastQualifier != -1)
+ s << QStringView{qualifiedCppName}.mid(lastQualifier + 1);
+ else
+ s << qualifiedCppName;
+ s << ";\n";
+}
+
+// Helpers for writing out namespaces hierarchically when writing class
+// forward declarations to the module header. Ensure inline namespaces
+// are marked as such (else clang complains) and namespaces are ordered.
+struct NameSpace {
+ AbstractMetaClassCPtr nameSpace;
+ AbstractMetaClassCList classes;
+};
+
+static bool operator<(const NameSpace &n1, const NameSpace &n2)
+{
+ return n1.nameSpace->name() < n2.nameSpace->name();
+}
+
+using NameSpaces = QList<NameSpace>;
+
+static qsizetype indexOf(const NameSpaces &nsps, const AbstractMetaClassCPtr &needle)
+{
+ for (qsizetype i = 0, count = nsps.size(); i < count; ++i) {
+ if (nsps.at(i).nameSpace == needle)
+ return i;
+ }
+ return -1;
+}
+
+static void writeNamespaceForwardDeclarationRecursion(TextStream &s, qsizetype idx,
+ const NameSpaces &nameSpaces)
+{
+ auto &root = nameSpaces.at(idx);
+ s << '\n';
+ if (root.nameSpace->isInlineNamespace())
+ s << "inline ";
+ s << "namespace " << root.nameSpace->name() << " {\n" << indent;
+ for (const auto &c : root.classes)
+ writeForwardDeclaration(s, c);
+
+ for (qsizetype i = 0, count = nameSpaces.size(); i < count; ++i) {
+ if (i != idx && nameSpaces.at(i).nameSpace->enclosingClass() == root.nameSpace)
+ writeNamespaceForwardDeclarationRecursion(s, i, nameSpaces);
+ }
+ s << outdent << "}\n";
+}
+
+static void writeForwardDeclarations(TextStream &s,
+ const AbstractMetaClassCList &classList)
+{
+ NameSpaces nameSpaces;
+
+ s << '\n';
+ auto typeSystemEntry = TypeDatabase::instance()->defaultTypeSystemType();
+ if (!typeSystemEntry->namespaceBegin().isEmpty())
+ s << typeSystemEntry->namespaceBegin() << '\n';
+
+ for (const auto &c : classList) {
+ if (auto encl = c->enclosingClass()) {
+ Q_ASSERT(encl->isNamespace());
+ auto idx = indexOf(nameSpaces, encl);
+ if (idx != -1) {
+ nameSpaces[idx].classes.append(c);
+ } else {
+ nameSpaces.append(NameSpace{encl, {c}});
+ for (auto enclNsp = encl->enclosingClass(); enclNsp;
+ enclNsp = enclNsp->enclosingClass()) {
+ idx = indexOf(nameSpaces, enclNsp);
+ if (idx == -1)
+ nameSpaces.append(NameSpace{enclNsp, {}});
+ }
+ }
+ } else {
+ writeForwardDeclaration(s, c);
+ }
+ }
+
+ std::sort(nameSpaces.begin(), nameSpaces.end());
+
+ // Recursively write out namespaces starting at the root elements.
+ for (qsizetype i = 0, count = nameSpaces.size(); i < count; ++i) {
+ const auto &nsp = nameSpaces.at(i);
+ if (nsp.nameSpace->enclosingClass() == nullptr)
+ writeNamespaceForwardDeclarationRecursion(s, i, nameSpaces);
+ }
+
+ if (!typeSystemEntry->namespaceEnd().isEmpty())
+ s << typeSystemEntry->namespaceEnd() << '\n';
+}
+
+// Include parameters required for the module/private module header
+
+using ConditionalIncludeMap = QMap<QString, IncludeGroup>;
+
+static TextStream &operator<<(TextStream &s, const ConditionalIncludeMap &m)
+{
+ for (auto it = m.cbegin(), end = m.cend(); it != end; ++it)
+ s << it.key() << '\n' << it.value() << "#endif\n";
+ return s;
+}
+
+struct ModuleHeaderParameters
+{
+ AbstractMetaClassCList forwardDeclarations;
+ std::set<Include> includes;
+ ConditionalIncludeMap conditionalIncludes;
+ QString typeFunctions;
+};
+
+HeaderGenerator::IndexValues
+ HeaderGenerator::collectTypeIndexes(const AbstractMetaClassCList &classList)
+{
+ IndexValues result;
+
+ for (const auto &metaClass : classList)
+ collectClassTypeIndexes(api(), metaClass, &result);
+
+ for (const AbstractMetaEnum &metaEnum : api().globalEnums())
+ collectTypeEntryTypeIndexes(api(), metaEnum.typeEntry(), &result);
+
+ // Write the smart pointer define indexes.
+ int smartPointerCountIndex = getMaxTypeIndex();
+ int smartPointerCount = 0;
+ for (const auto &smp : api().instantiatedSmartPointers()) {
+ QString indexName = getTypeIndexVariableName(smp.type);
+ result.append({indexName, smartPointerCountIndex, smp.type.cppSignature()});
+ // Add a the same value for const pointees (shared_ptr<const Foo>).
+ const auto ptrName = smp.type.typeEntry()->entryName();
+ const auto pos = indexName.indexOf(ptrName, 0, Qt::CaseInsensitive);
+ if (pos >= 0) {
+ indexName.insert(pos + ptrName.size() + 1, u"const"_s);
+ result.append({indexName, smartPointerCountIndex, "(const)"_L1});
+ }
+ ++smartPointerCountIndex;
+ ++smartPointerCount;
+ }
+ result.append({"SBK_"_L1 + moduleName() + "_IDX_COUNT"_L1,
+ getMaxTypeIndex() + smartPointerCount, {}});
+ return result;
+}
+
+HeaderGenerator::IndexValues HeaderGenerator::collectConverterIndexes() const
+{
+ IndexValues result;
+ const auto &primitives = primitiveTypes();
+ int pCount = 0;
+ for (const auto &ptype : primitives) {
+ // Note: do not generate indices for typedef'd primitive types as
+ // they'll use the primitive type converters instead, so we
+ // don't need to create any other.
+ if (ptype->generateCode() && ptype->customConversion() != nullptr)
+ result.append({getTypeIndexVariableName(ptype), pCount++, {}});
+ }
+
+ for (const AbstractMetaType &container : api().instantiatedContainers()) {
+ result.append({getTypeIndexVariableName(container),
+ pCount++, container.cppSignature()});
+ }
+
+ // Because on win32 the compiler will not accept a zero length array.
+ if (pCount == 0)
+ pCount++;
+ result.append({"SBK_"_L1 + moduleName() + "_CONVERTERS_IDX_COUNT"_L1,
+ pCount, {}});
+ return result;
+}
+
+// PYSIDE-2404: Write the enums in unchanged case for reuse in type imports.
+// For conpatibility, we create them in uppercase, too and with
+// doubled index for emulating the former type-only case.
+//
+// FIXME: Remove in PySide 7. (See the note in `parser.py`)
+//
+static IndexValue typeIndexUpper(struct IndexValue const &ti)
+{
+ QString modi = ti.name.toUpper();
+ if (modi == ti.name)
+ modi = u"// "_s + modi;
+ return {modi, ti.value * 2, ti.comment};
+}
+
+bool HeaderGenerator::finishGeneration()
+{
+ // Generate the main header for this module. This header should be included
+ // by binding modules extending on top of this one.
+ ModuleHeaderParameters parameters;
+ ModuleHeaderParameters privateParameters;
+ StringStream macrosStream(TextStream::Language::Cpp);
+
+ const auto snips = TypeDatabase::instance()->defaultTypeSystemType()->codeSnips();
+ writeModuleCodeSnips(macrosStream, snips, TypeSystem::CodeSnipPositionDeclaration,
+ TypeSystem::TargetLangCode);
+
+ auto classList = api().classes();
+
+ std::sort(classList.begin(), classList.end(),
+ [](const AbstractMetaClassCPtr &a, const AbstractMetaClassCPtr &b) {
+ return a->typeEntry()->sbkIndex() < b->typeEntry()->sbkIndex();
+ });
+
+ const auto typeIndexes = collectTypeIndexes(classList);
+
+ macrosStream << "\n// Type indices\nenum [[deprecated]] : int {\n";
+ for (const auto &ti : typeIndexes)
+ macrosStream << typeIndexUpper(ti);
+ macrosStream << "};\n";
+
+ macrosStream << "\n// Type indices\nenum : int {\n";
+ for (const auto &ti : typeIndexes)
+ 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";
+ macrosStream << "extern SbkConverter **" << convertersVariableName() << ";\n\n";
+
+ // TODO-CONVERTER ------------------------------------------------------------------------------
+ // Using a counter would not do, a fix must be made to APIExtractor's getTypeIndex().
+ const auto converterIndexes = collectConverterIndexes();
+ macrosStream << "// Converter indices\nenum [[deprecated]] : int {\n";
+ for (const auto &ci : converterIndexes)
+ macrosStream << typeIndexUpper(ci);
+ macrosStream << "};\n\n";
+
+ macrosStream << "// Converter indices\nenum : int {\n";
+ for (const auto &ci : converterIndexes)
+ macrosStream << ci;
+ macrosStream << "};\n";
+
+ formatTypeDefEntries(macrosStream);
+
+ // TODO-CONVERTER ------------------------------------------------------------------------------
+
+ macrosStream << "// Macros for type check\n";
+
+ TextStream typeFunctions(&parameters.typeFunctions, TextStream::Language::Cpp);
+ TextStream privateTypeFunctions(&privateParameters.typeFunctions, TextStream::Language::Cpp);
+
+ for (const AbstractMetaEnum &cppEnum : api().globalEnums()) {
+ if (!cppEnum.isAnonymous()) {
+ const auto te = cppEnum.typeEntry();
+ if (te->hasConfigCondition())
+ parameters.conditionalIncludes[te->configCondition()].append(te->include());
+ else
+ parameters.includes.insert(cppEnum.typeEntry()->include());
+ writeSbkTypeFunction(typeFunctions, cppEnum);
+ }
+ }
+
+ StringStream protEnumsSurrogates(TextStream::Language::Cpp);
+ for (const auto &metaClass : classList) {
+ const auto classType = metaClass->typeEntry();
+ if (!shouldGenerate(classType))
+ continue;
+
+ // Includes
+ const bool isPrivate = classType->isPrivate();
+ auto &par = isPrivate ? privateParameters : parameters;
+ const auto classInclude = classType->include();
+ const bool hasConfigCondition = classType->hasConfigCondition();
+ if (leanHeaders() && canForwardDeclare(metaClass))
+ par.forwardDeclarations.append(metaClass);
+ else if (hasConfigCondition)
+ par.conditionalIncludes[classType->configCondition()].append(classInclude);
+ else
+ par.includes.insert(classInclude);
+
+ auto &typeFunctionsStr = isPrivate ? privateTypeFunctions : typeFunctions;
+
+ ConfigurableScope configScope(typeFunctionsStr, classType);
+ for (const AbstractMetaEnum &cppEnum : metaClass->enums()) {
+ if (cppEnum.isAnonymous() || cppEnum.isPrivate())
+ continue;
+ if (const auto inc = cppEnum.typeEntry()->include(); inc != classInclude)
+ par.includes.insert(inc);
+ writeProtectedEnumSurrogate(protEnumsSurrogates, cppEnum);
+ writeSbkTypeFunction(typeFunctionsStr, cppEnum);
+ }
+
+ if (!metaClass->isNamespace())
+ writeSbkTypeFunction(typeFunctionsStr, metaClass);
+ }
+
+ for (const auto &smp : api().instantiatedSmartPointers()) {
+ parameters.includes.insert(smp.type.typeEntry()->include());
+ writeSbkTypeFunction(typeFunctions, smp.type);
+ }
+
+ const QString moduleHeaderDir = outputDirectory() + u'/'
+ + subDirectoryForPackage(packageName()) + u'/';
+ const QString moduleHeaderFileName(moduleHeaderDir + getModuleHeaderFileName());
+
+ QString includeShield(u"SBK_"_s + moduleName().toUpper() + u"_PYTHON_H"_s);
+
+ FileOut file(moduleHeaderFileName);
+ TextStream &s = file.stream;
+ s.setLanguage(TextStream::Language::Cpp);
+
+ // write license comment
+ s << licenseComment()<< "\n\n";
+
+ s << "#ifndef " << includeShield<< '\n';
+ s << "#define " << includeShield<< "\n\n";
+ if (!avoidProtectedHack()) {
+ s << "//workaround to access protected functions\n";
+ s << "#define protected public\n\n";
+ }
+
+ s << "#include <sbkpython.h>\n";
+ s << "#include <sbkmodule.h>\n";
+ s << "#include <sbkconverter.h>\n";
+
+ QStringList requiredTargetImports = TypeDatabase::instance()->requiredTargetImports();
+ if (!requiredTargetImports.isEmpty()) {
+ s << "// Module Includes\n";
+ for (const QString &requiredModule : std::as_const(requiredTargetImports))
+ s << "#include <" << getModuleHeaderFileName(requiredModule) << ">\n";
+ s<< '\n';
+ }
+
+ s << "// Bound library includes\n";
+ for (const Include &include : parameters.includes)
+ s << include;
+ s << parameters.conditionalIncludes;
+
+ if (leanHeaders()) {
+ writeForwardDeclarations(s, parameters.forwardDeclarations);
+ } else {
+ if (!primitiveTypes().isEmpty()) {
+ s << "// Conversion Includes - Primitive Types\n";
+ const auto &primitiveTypeList = primitiveTypes();
+ for (const auto &ptype : primitiveTypeList)
+ s << ptype->include();
+ s<< '\n';
+ }
+
+ if (!containerTypes().isEmpty()) {
+ s << "// Conversion Includes - Container Types\n";
+ const ContainerTypeEntryCList &containerTypeList = containerTypes();
+ for (const auto &ctype : containerTypeList)
+ s << ctype->include();
+ s<< '\n';
+ }
+ }
+
+ s << macrosStream.toString() << '\n';
+
+ if (protEnumsSurrogates.size() > 0) {
+ s << "// Protected enum surrogates\n"
+ << protEnumsSurrogates.toString() << '\n';
+ }
+
+ writeTypeFunctions(s, parameters.typeFunctions);
+
+ s << "#endif // " << includeShield << "\n\n";
+
+ file.done();
+
+ if (hasPrivateClasses())
+ writePrivateHeader(moduleHeaderDir, includeShield, privateParameters);
+
+ return true;
+}
+
+void HeaderGenerator::writePrivateHeader(const QString &moduleHeaderDir,
+ const QString &publicIncludeShield,
+ const ModuleHeaderParameters &parameters)
+{
+ // Write includes and type functions of private classes
+
+ FileOut privateFile(moduleHeaderDir + getPrivateModuleHeaderFileName());
+ TextStream &ps = privateFile.stream;
+ ps.setLanguage(TextStream::Language::Cpp);
+ QString privateIncludeShield =
+ publicIncludeShield.left(publicIncludeShield.size() - 2) + "_P_H"_L1;
+
+ ps << licenseComment()<< "\n\n";
+
+ ps << "#ifndef " << privateIncludeShield << '\n';
+ ps << "#define " << privateIncludeShield << "\n\n";
+
+ for (const Include &include : parameters.includes)
+ ps << include;
+ ps << parameters.conditionalIncludes;
+ ps << '\n';
+
+ if (leanHeaders())
+ writeForwardDeclarations(ps, parameters.forwardDeclarations);
+
+ writeTypeFunctions(ps, parameters.typeFunctions);
+
+ ps << "#endif\n";
+ privateFile.done();
+}
+
+void HeaderGenerator::writeTypeFunctions(TextStream &s, const QString &typeFunctions)
+{
+ if (typeFunctions.isEmpty())
+ return;
+
+ if (usePySideExtensions())
+ s << "QT_WARNING_PUSH\nQT_WARNING_DISABLE_DEPRECATED\n";
+
+ s << "namespace Shiboken\n{\n\n"
+ << "// PyType functions, to get the PyObjectType for a type T\n"
+ << typeFunctions << '\n'
+ << "} // namespace Shiboken\n\n";
+
+ if (usePySideExtensions())
+ s << "QT_WARNING_POP\n";
+}
+
+void HeaderGenerator::writeProtectedEnumSurrogate(TextStream &s, const AbstractMetaEnum &cppEnum)
+{
+ if (avoidProtectedHack() && cppEnum.isProtected())
+ s << "enum " << protectedEnumSurrogateName(cppEnum) << " {};\n";
+}
+
+void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaEnum &cppEnum)
+{
+ const QString enumName = avoidProtectedHack() && cppEnum.isProtected()
+ ? protectedEnumSurrogateName(cppEnum)
+ : cppEnum.qualifiedCppName();
+ const auto te = cppEnum.typeEntry();
+ ConfigurableScope configScope(s, te);
+ s << "template<> inline PyTypeObject *SbkType< " << m_gsp << enumName << " >() ";
+ s << "{ return " << cpythonTypeNameExt(te) << "; }\n";
+
+ const auto flag = cppEnum.typeEntry()->flags();
+ if (flag) {
+ s << "template<> inline PyTypeObject *SbkType< " << m_gsp << flag->name() << " >() "
+ << "{ return " << cpythonTypeNameExt(flag) << "; }\n";
+ }
+}
+
+void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaClassCPtr &cppClass)
+{
+ s << "template<> inline PyTypeObject *SbkType< "
+ << getFullTypeName(cppClass) << " >() "
+ << "{ return " << cpythonTypeNameExt(cppClass->typeEntry()) << "; }\n";
+}
+
+void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaType &metaType)
+{
+ s << "template<> inline PyTypeObject *SbkType< "
+ << m_gsp << metaType.cppSignature() << " >() "
+ << "{ return " << cpythonTypeNameExt(metaType) << "; }\n";
+}
+
+void HeaderGenerator::writeModuleCodeSnips(TextStream &s, const CodeSnipList &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language) const
+{
+ if (!codeSnips.isEmpty()) {
+ try {
+ writeCodeSnips(s, codeSnips, position, language);
+ } catch (const std::exception &e) {
+ throw Exception(msgSnippetError("module header of "_L1 + moduleName(), e.what()));
+ }
+ }
+}
diff --git a/sources/shiboken6/generator/shiboken/headergenerator.h b/sources/shiboken6/generator/shiboken/headergenerator.h
new file mode 100644
index 000000000..03b98e743
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/headergenerator.h
@@ -0,0 +1,76 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef HEADERGENERATOR_H
+#define HEADERGENERATOR_H
+
+#include "shibokengenerator.h"
+#include "include.h"
+#include "modifications_typedefs.h"
+
+#include <QtCore/QList>
+#include <QtCore/QSet>
+
+struct IndexValue;
+class AbstractMetaFunction;
+struct ModuleHeaderParameters;
+
+/**
+ * The HeaderGenerator generate the declarations of C++ bindings classes.
+ */
+class HeaderGenerator : public ShibokenGenerator
+{
+public:
+ const char *name() const override { return "Header generator"; }
+
+ static const char *protectedHackDefine;
+
+protected:
+ QString fileNameForContext(const GeneratorContext &context) const override;
+ void generateClass(TextStream &s, const GeneratorContext &classContext) override;
+ bool finishGeneration() override;
+
+private:
+ using InheritedOverloadSet = QSet<AbstractMetaFunctionCPtr>;
+ using IndexValues = QList<IndexValue>;
+
+ IndexValues collectTypeIndexes(const AbstractMetaClassCList &classList);
+ IndexValues collectConverterIndexes() const;
+
+ static void writeCopyCtor(TextStream &s, const AbstractMetaClassCPtr &metaClass);
+ void writeFunction(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ InheritedOverloadSet *inheritedOverloads,
+ FunctionGeneration generation) const;
+ static void writeSbkTypeFunction(TextStream &s, const AbstractMetaEnum &cppEnum);
+ static void writeSbkTypeFunction(TextStream &s, const AbstractMetaClassCPtr &cppClass);
+ static void writeSbkTypeFunction(TextStream &s, const AbstractMetaType &metaType);
+ void collectTypeEntryTypeIndexes(const ApiExtractorResult &api,
+ const TypeEntryCPtr &typeEntry,
+ IndexValues *indexValues);
+ void collectClassTypeIndexes(const ApiExtractorResult &api,
+ const AbstractMetaClassCPtr &metaClass,
+ IndexValues *indexValues);
+ static void writeProtectedEnumSurrogate(TextStream &s, const AbstractMetaEnum &cppEnum);
+ void writeMemberFunctionWrapper(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ const QString &postfix = {}) const;
+ void writePrivateHeader(const QString &moduleHeaderDir,
+ const QString &publicIncludeShield,
+ const ModuleHeaderParameters &parameters);
+ static void writeTypeFunctions(TextStream &s, const QString &typeFunctions);
+ void writeWrapperClassDeclaration(TextStream &s,
+ const QString &wrapperName,
+ const GeneratorContext &classContext) const;
+ void writeWrapperClass(TextStream &s, const QString &wrapperName, const GeneratorContext &classContext) const;
+ void writeInheritedWrapperClassDeclaration(TextStream &s,
+ const GeneratorContext &classContext) const;
+ void writeModuleCodeSnips(TextStream &s, const CodeSnipList &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language) const;
+
+ AbstractMetaClassCList m_alternateTemplateIndexes;
+};
+
+#endif // HEADERGENERATOR_H
+
diff --git a/sources/shiboken6/generator/shiboken/overloaddata.cpp b/sources/shiboken6/generator/shiboken/overloaddata.cpp
new file mode 100644
index 000000000..c28fcdc1a
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/overloaddata.cpp
@@ -0,0 +1,1011 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <abstractmetafunction.h>
+#include <apiextractorresult.h>
+#include <abstractmetalang.h>
+#include <dotview.h>
+#include <reporthandler.h>
+#include <complextypeentry.h>
+#include <containertypeentry.h>
+#include <primitivetypeentry.h>
+#include <graph.h>
+#include "overloaddata.h"
+#include "messages.h"
+#include "ctypenames.h"
+#include "pytypenames.h"
+#include "textstream.h"
+#include "exception.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QTemporaryFile>
+
+#include <algorithm>
+#include <utility>
+
+using namespace Qt::StringLiterals;
+
+static QString getTypeName(const AbstractMetaType &type)
+{
+ TypeEntryCPtr typeEntry = type.typeEntry();
+ if (typeEntry->isPrimitive())
+ typeEntry = basicReferencedTypeEntry(typeEntry);
+ QString typeName = typeEntry->name();
+ if (typeEntry->isContainer()) {
+ QStringList types;
+ for (const auto &cType : type.instantiations()) {
+ TypeEntryCPtr typeEntry = cType.typeEntry();
+ if (typeEntry->isPrimitive())
+ typeEntry = basicReferencedTypeEntry(typeEntry);
+ types << typeEntry->name();
+ }
+ typeName += u'<' + types.join(u',') + u" >"_s;
+ }
+ return typeName;
+}
+
+static bool typesAreEqual(const AbstractMetaType &typeA, const AbstractMetaType &typeB)
+{
+ if (typeA.typeEntry() == typeB.typeEntry()) {
+ if (typeA.isContainer() || typeA.isSmartPointer()) {
+ if (typeA.instantiations().size() != typeB.instantiations().size())
+ return false;
+
+ for (qsizetype i = 0; i < typeA.instantiations().size(); ++i) {
+ if (!typesAreEqual(typeA.instantiations().at(i), typeB.instantiations().at(i)))
+ return false;
+ }
+ return true;
+ }
+
+ return !(typeA.isCString() ^ typeB.isCString());
+ }
+ return false;
+}
+
+/**
+ * Helper function that returns the name of a container get from containerType argument and
+ * an instantiation taken either from an implicit conversion expressed by the function argument,
+ * or from the string argument implicitConvTypeName.
+ */
+static QString getImplicitConversionTypeName(const AbstractMetaType &containerType,
+ const AbstractMetaType &instantiation,
+ const AbstractMetaFunctionCPtr &function,
+ const QString &implicitConvTypeName = QString())
+{
+ QString impConv;
+ if (!implicitConvTypeName.isEmpty())
+ impConv = implicitConvTypeName;
+ else if (function->isConversionOperator())
+ impConv = function->ownerClass()->typeEntry()->name();
+ else
+ impConv = getTypeName(function->arguments().constFirst().type());
+
+ QStringList types;
+ for (const auto &otherType : containerType.instantiations())
+ types << (otherType == instantiation ? impConv : getTypeName(otherType));
+
+ return containerType.typeEntry()->qualifiedCppName() + u'<'
+ + types.join(u", "_s) + u" >"_s;
+}
+
+static inline int overloadNumber(const OverloadDataNodePtr &o)
+{
+ return o->referenceFunction()->overloadNumber();
+}
+
+static bool sortByOverloadNumberModification(OverloadDataList &list)
+{
+ if (std::all_of(list.cbegin(), list.cend(),
+ [](const OverloadDataNodePtr &o) { return overloadNumber(o) == TypeSystem::OverloadNumberDefault; })) {
+ return false;
+ }
+ std::stable_sort(list.begin(), list.end(),
+ [] (const OverloadDataNodePtr &o1, const OverloadDataNodePtr &o2) {
+ return overloadNumber(o1) < overloadNumber(o2);
+ });
+ return true;
+}
+
+using OverloadGraph = Graph<QString>;
+
+/**
+ * Topologically sort the overloads by implicit convertion order
+ *
+ * This avoids using an implicit conversion if there's an explicit
+ * overload for the convertible type. So, if there's an implicit convert
+ * like TargetType(ConvertibleType foo) and both are in the overload list,
+ * ConvertibleType is checked before TargetType.
+ *
+ * Side effects: Modifies m_nextOverloadData
+ */
+void OverloadDataRootNode::sortNextOverloads(const ApiExtractorResult &api)
+{
+ QHash<QString, OverloadDataList> typeToOverloads;
+ using Edge = std::pair<QString, QString>;
+
+ bool checkPyObject = false;
+ bool checkPySequence = false;
+ bool checkQString = false;
+ bool checkQVariant = false;
+ bool checkPyBuffer = false;
+
+ // Primitive types that are not int, long, short,
+ // char and their respective unsigned counterparts.
+ static const QStringList nonIntegerPrimitives{floatT, doubleT, boolT};
+
+ // Signed integer primitive types.
+ static const QStringList signedIntegerPrimitives{intT, shortT, longT, longLongT};
+
+ // sort the children overloads
+ for (const auto &ov : std::as_const(m_children))
+ ov->sortNextOverloads(api);
+
+ if (m_children.size() <= 1 || sortByOverloadNumberModification(m_children))
+ return;
+
+ // Populates the OverloadSortData object containing map and reverseMap, to map type names to ids,
+ // these ids will be used by the topological sort algorithm, because is easier and faster to work
+ // with graph sorting using integers.
+
+ OverloadGraph graph;
+ for (const auto &ov : std::as_const(m_children)) {
+ const QString typeName = getTypeName(ov->modifiedArgType());
+ auto it = typeToOverloads.find(typeName);
+ if (it == typeToOverloads.end()) {
+ typeToOverloads.insert(typeName, {ov});
+ graph.addNode(typeName);
+ } else {
+ it.value().append(ov);
+ }
+
+ if (!checkPyObject && typeName == cPyObjectT)
+ checkPyObject = true;
+ else if (!checkPySequence && typeName == cPySequenceT)
+ checkPySequence = true;
+ else if (!checkPyBuffer && typeName == cPyBufferT)
+ checkPyBuffer = true;
+ else if (!checkQVariant && typeName == qVariantT)
+ checkQVariant = true;
+ else if (!checkQString && typeName == qStringT)
+ checkQString = true;
+
+ for (const auto &instantiation : ov->argType().instantiations()) {
+ // Add dependencies for type instantiation of container.
+ graph.addNode(getTypeName(instantiation));
+
+ // Build dependency for implicit conversion types instantiations for base container.
+ // For example, considering signatures "method(list<PointF>)" and "method(list<Point>)",
+ // and being PointF implicitly convertible from Point, an list<T> instantiation with T
+ // as Point must come before the PointF instantiation, or else list<Point> will never
+ // be called. In the case of primitive types, list<double> must come before list<int>.
+ if (instantiation.isPrimitive() && (signedIntegerPrimitives.contains(instantiation.name()))) {
+ for (const QString &primitive : std::as_const(nonIntegerPrimitives))
+ graph.addNode(getImplicitConversionTypeName(ov->argType(), instantiation, nullptr, primitive));
+ } else {
+ const auto &funcs = api.implicitConversions(instantiation);
+ for (const auto &function : funcs)
+ graph.addNode(getImplicitConversionTypeName(ov->argType(), instantiation, function));
+ }
+ }
+ }
+
+
+ // Create the graph of type dependencies based on implicit conversions.
+ // All C++ primitive types, add any forgotten type AT THE END OF THIS LIST!
+ static const QStringList primitiveTypes{intT, unsignedIntT, longT, unsignedLongT,
+ shortT, unsignedShortT, boolT, unsignedCharT, charT, floatT,
+ doubleT, constCharPtrT};
+
+ QStringList foundPrimitiveTypeIds;
+ for (const auto &p : primitiveTypes) {
+ if (graph.hasNode(p))
+ foundPrimitiveTypeIds.append(p);
+ }
+
+ if (checkPySequence && checkPyObject)
+ graph.addEdge(cPySequenceT, cPyObjectT);
+
+ QStringList classesWithIntegerImplicitConversion;
+
+ AbstractMetaFunctionCList involvedConversions;
+
+ for (const auto &ov : std::as_const(m_children)) {
+ const AbstractMetaType &targetType = ov->argType();
+ const QString targetTypeEntryName = getTypeName(ov->modifiedArgType());
+
+ // Process implicit conversions
+ const auto &functions = api.implicitConversions(targetType);
+ for (const auto &function : functions) {
+ QString convertibleType;
+ if (function->isConversionOperator())
+ convertibleType = function->ownerClass()->typeEntry()->name();
+ else
+ convertibleType = getTypeName(function->arguments().constFirst().type());
+
+ if (convertibleType == intT || convertibleType == unsignedIntT)
+ classesWithIntegerImplicitConversion << targetTypeEntryName;
+
+ if (!graph.hasNode(convertibleType))
+ continue;
+
+ // If a reverse pair already exists, remove it. Probably due to the
+ // container check (This happened to QVariant and QHash)
+ graph.removeEdge(targetTypeEntryName, convertibleType);
+ graph.addEdge(convertibleType, targetTypeEntryName);
+ involvedConversions.append(function);
+ }
+
+ // Process inheritance relationships
+ if (targetType.isValue() || targetType.isObject()) {
+ const auto te = targetType.typeEntry();
+ auto metaClass = AbstractMetaClass::findClass(api.classes(), te);
+ if (!metaClass)
+ throw Exception(msgArgumentClassNotFound(m_overloads.constFirst(), te));
+ const auto &ancestors = metaClass->allTypeSystemAncestors();
+ for (const auto &ancestor : ancestors) {
+ QString ancestorTypeName = ancestor->typeEntry()->name();
+ if (!graph.hasNode(ancestorTypeName))
+ continue;
+ graph.removeEdge(ancestorTypeName, targetTypeEntryName);
+ graph.addEdge(targetTypeEntryName, ancestorTypeName);
+ }
+ }
+
+ // Process template instantiations
+ for (const auto &instantiation : targetType.instantiations()) {
+ const QString convertible = getTypeName(instantiation);
+ if (graph.hasNode(convertible)) {
+ if (!graph.containsEdge(targetTypeEntryName, convertible)) // Avoid cyclic dependency.
+ graph.addEdge(convertible, targetTypeEntryName);
+
+ if (instantiation.isPrimitive() && (signedIntegerPrimitives.contains(instantiation.name()))) {
+ for (const QString &primitive : std::as_const(nonIntegerPrimitives)) {
+ QString convertibleTypeName =
+ getImplicitConversionTypeName(ov->argType(), instantiation, nullptr, primitive);
+ // Avoid cyclic dependency.
+ if (!graph.containsEdge(targetTypeEntryName, convertibleTypeName))
+ graph.addEdge(convertibleTypeName, targetTypeEntryName);
+ }
+
+ } else {
+ const auto &funcs = api.implicitConversions(instantiation);
+ for (const auto &function : funcs) {
+ QString convertibleTypeName =
+ getImplicitConversionTypeName(ov->argType(), instantiation, function);
+ // Avoid cyclic dependency.
+ if (!graph.containsEdge(targetTypeEntryName, convertibleTypeName)) {
+ graph.addEdge(convertibleTypeName, targetTypeEntryName);
+ involvedConversions.append(function);
+ }
+ }
+ }
+ }
+ }
+
+
+ if ((checkPySequence || checkPyObject || checkPyBuffer)
+ && !targetTypeEntryName.contains(cPyObjectT)
+ && !targetTypeEntryName.contains(cPyBufferT)
+ && !targetTypeEntryName.contains(cPySequenceT)) {
+ if (checkPySequence) {
+ // PySequence will be checked after all more specific types, but before PyObject.
+ graph.addEdge(targetTypeEntryName, cPySequenceT);
+ } else if (checkPyBuffer) {
+ // PySequence will be checked after all more specific types, but before PyObject.
+ graph.addEdge(targetTypeEntryName, cPyBufferT);
+ } else {
+ // Add dependency on PyObject, so its check is the last one (too generic).
+ graph.addEdge(targetTypeEntryName, cPyObjectT);
+ }
+ } else if (checkQVariant && targetTypeEntryName != qVariantT) {
+ if (!graph.containsEdge(qVariantT, targetTypeEntryName)) // Avoid cyclic dependency.
+ graph.addEdge(targetTypeEntryName, qVariantT);
+ } else if (checkQString && ov->argType().isPointer()
+ && targetTypeEntryName != qStringT
+ && targetTypeEntryName != qByteArrayT
+ && (!checkPyObject || targetTypeEntryName != cPyObjectT)) {
+ if (!graph.containsEdge(qStringT, targetTypeEntryName)) // Avoid cyclic dependency.
+ graph.addEdge(targetTypeEntryName, qStringT);
+ }
+
+ if (targetType.isEnum()) {
+ // Enum values must precede primitive types.
+ for (const auto &id : foundPrimitiveTypeIds)
+ graph.addEdge(targetTypeEntryName, id);
+ }
+ }
+
+ // QByteArray args need to be checked after QString args
+ if (graph.hasNode(qStringT) && graph.hasNode(qByteArrayT))
+ graph.addEdge(qStringT, qByteArrayT);
+
+ static const Edge rangeOrder[] =
+ {{doubleT, floatT},
+ {longLongT, longT}, {longLongT, intT}, {intT, shortT},
+ {unsignedLongLongT, unsignedLongT}, {unsignedLongLongT, unsignedT},
+ {unsignedLongLongT, unsignedIntT}, {unsignedT, unsignedShortT}
+ };
+ for (const auto &r : rangeOrder) {
+ if (graph.hasNode(r.first) && graph.hasNode(r.second))
+ graph.addEdge(r.first, r.second);
+ }
+
+ for (const auto &ov : std::as_const(m_children)) {
+ const AbstractMetaType &targetType = ov->argType();
+ if (!targetType.isEnum())
+ continue;
+
+ QString targetTypeEntryName = getTypeName(targetType);
+ // Enum values must precede types implicitly convertible from "int" or "unsigned int".
+ for (const QString &implicitFromInt : std::as_const(classesWithIntegerImplicitConversion))
+ graph.addEdge(targetTypeEntryName, implicitFromInt);
+ }
+
+
+ // Special case for double(int i) (not tracked by m_generator->implicitConversions
+ for (const QString &signedIntegerName : std::as_const(signedIntegerPrimitives)) {
+ if (graph.hasNode(signedIntegerName)) {
+ for (const QString &nonIntegerName : std::as_const(nonIntegerPrimitives)) {
+ if (graph.hasNode(nonIntegerName))
+ graph.addEdge(nonIntegerName, signedIntegerName);
+ }
+ }
+ }
+
+ // sort the overloads topologically based on the dependency graph.
+ const auto unmappedResult = graph.topologicalSort();
+ if (!unmappedResult.isValid()) {
+ QString funcName = referenceFunction()->name();
+ if (auto owner = referenceFunction()->ownerClass())
+ funcName.prepend(owner->name() + u'.');
+
+ // Dump overload graph
+ QString graphName = QDir::tempPath() + u'/' + funcName + u".dot"_s;
+ graph.dumpDot(graphName, [] (const QString &n) { return n; });
+ AbstractMetaFunctionCList cyclic;
+ for (const auto &typeName : unmappedResult.cyclic) {
+ const auto oit = typeToOverloads.constFind(typeName);
+ if (oit != typeToOverloads.cend())
+ cyclic.append(oit.value().constFirst()->referenceFunction());
+ }
+ qCWarning(lcShiboken, "%s", qPrintable(msgCyclicDependency(funcName, graphName, cyclic, involvedConversions)));
+ }
+
+ m_children.clear();
+ for (const auto &typeName : unmappedResult.result) {
+ const auto oit = typeToOverloads.constFind(typeName);
+ if (oit != typeToOverloads.cend()) {
+ std::copy(oit.value().crbegin(), oit.value().crend(),
+ std::back_inserter(m_children));
+ }
+ }
+}
+
+// Determine the minimum (first default argument)/maximum arguments (size)
+// of a function (taking into account the removed arguments).
+static std::pair<int, int> getMinMaxArgs(const AbstractMetaFunctionCPtr &func)
+{
+ int defaultValueIndex = -1;
+ const auto &arguments = func->arguments();
+ int argIndex = 0;
+ for (const auto &arg : arguments) {
+ if (!arg.isModifiedRemoved()) {
+ if (defaultValueIndex < 0 && arg.hasDefaultValueExpression())
+ defaultValueIndex = argIndex;
+ ++argIndex;
+ }
+ }
+ const int maxArgs = argIndex;
+ const int minArgs = defaultValueIndex >= 0 ? defaultValueIndex : maxArgs;
+ return {minArgs, maxArgs};
+}
+
+const OverloadDataRootNode *OverloadDataNode::parent() const
+{
+ return m_parent;
+}
+
+/**
+ * Root constructor for OverloadData
+ *
+ * This constructor receives the list of overloads for a given function and iterates generating
+ * the graph of OverloadData instances. Each OverloadDataNode instance references an argument/type
+ * combination.
+ *
+ * Example:
+ * addStuff(double, PyObject *)
+ * addStuff(double, int)
+ *
+ * Given these two overloads, there will be the following graph:
+ *
+ * addStuff - double - PyObject *
+ * \- int
+ *
+ */
+OverloadData::OverloadData(const AbstractMetaFunctionCList &overloads,
+ const ApiExtractorResult &api) :
+ OverloadDataRootNode(overloads)
+{
+ for (const auto &func : overloads) {
+ const auto minMaxArgs = getMinMaxArgs(func);
+ if (minMaxArgs.first < m_minArgs)
+ m_minArgs = minMaxArgs.first;
+ if (minMaxArgs.second > m_maxArgs)
+ m_maxArgs = minMaxArgs.second;
+ OverloadDataRootNode *currentOverloadData = this;
+ const AbstractMetaArgumentList &arguments = func->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+ if (!arg.isModifiedRemoved())
+ currentOverloadData = currentOverloadData->addOverloadDataNode(func, arg);
+ }
+ }
+
+ // Sort the overload possibilities so that the overload decisor code goes for the most
+ // important cases first, based on the topological order of the implicit conversions
+ sortNextOverloads(api);
+}
+
+OverloadDataNode::OverloadDataNode(const AbstractMetaFunctionCPtr &func,
+ OverloadDataRootNode *parent,
+ const AbstractMetaArgument &argument,
+ int argPos,
+ const QString argTypeReplaced) :
+ m_argument(argument),
+ m_argTypeReplaced(argTypeReplaced),
+ m_parent(parent),
+ m_argPos(argPos)
+{
+ if (func)
+ this->addOverload(func);
+}
+
+void OverloadDataNode::addOverload(const AbstractMetaFunctionCPtr &func)
+{
+ m_overloads.append(func);
+}
+
+OverloadDataNode *OverloadDataRootNode::addOverloadDataNode(const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaArgument &arg)
+{
+ OverloadDataNodePtr overloadData;
+ if (!func->isOperatorOverload()) {
+ for (const auto &tmp : std::as_const(m_children)) {
+ // TODO: 'const char *', 'char *' and 'char' will have the same TypeEntry?
+
+ // If an argument have a type replacement, then we should create a new overloaddata
+ // for it, unless the next argument also have a identical type replacement.
+ if (typesAreEqual(tmp->modifiedArgType(), arg.modifiedType())) {
+ tmp->addOverload(func);
+ overloadData = tmp;
+ }
+ }
+ }
+
+ if (!overloadData) {
+ const int argpos = argPos() + 1;
+ overloadData.reset(new OverloadDataNode(func, this, arg, argpos));
+ m_children.append(overloadData);
+ }
+
+ return overloadData.get();
+}
+
+bool OverloadData::hasNonVoidReturnType() const
+{
+ for (const auto &func : m_overloads) {
+ if (func->isTypeModified()) {
+ if (func->modifiedTypeName() != u"void")
+ return true;
+ } else {
+ if (!func->argumentRemoved(0) && !func->type().isVoid())
+ return true;
+ }
+ }
+ return false;
+}
+
+bool OverloadData::hasVarargs() const
+{
+ for (const auto &func : m_overloads) {
+ AbstractMetaArgumentList args = func->arguments();
+ if (args.size() > 1 && args.constLast().type().isVarargs())
+ return true;
+ }
+ return false;
+}
+
+bool OverloadData::hasStaticFunction(const AbstractMetaFunctionCList &overloads)
+{
+ for (const auto &func : overloads) {
+ if (func->isStatic())
+ return true;
+ }
+ return false;
+}
+
+bool OverloadData::hasStaticFunction() const
+{
+ for (const auto &func : m_overloads) {
+ if (func->isStatic())
+ return true;
+ }
+ return false;
+}
+
+bool OverloadData::hasClassMethod(const AbstractMetaFunctionCList &overloads)
+{
+ for (const auto &func : overloads) {
+ if (func->isClassMethod())
+ return true;
+ }
+ return false;
+}
+
+bool OverloadData::hasClassMethod() const
+{
+ for (const auto &func : m_overloads) {
+ if (func->isClassMethod())
+ return true;
+ }
+ return false;
+}
+
+bool OverloadData::hasInstanceFunction(const AbstractMetaFunctionCList &overloads)
+{
+ for (const auto &func : overloads) {
+ if (!func->isStatic())
+ return true;
+ }
+ return false;
+}
+
+bool OverloadData::hasInstanceFunction() const
+{
+ for (const auto &func : m_overloads) {
+ if (!func->isStatic())
+ return true;
+ }
+ return false;
+}
+
+bool OverloadData::hasStaticAndInstanceFunctions(const AbstractMetaFunctionCList &overloads)
+{
+ return OverloadData::hasStaticFunction(overloads) && OverloadData::hasInstanceFunction(overloads);
+}
+
+bool OverloadData::hasStaticAndInstanceFunctions() const
+{
+ return OverloadData::hasStaticFunction() && OverloadData::hasInstanceFunction();
+}
+
+OverloadDataRootNode::OverloadDataRootNode(const AbstractMetaFunctionCList &o) :
+ m_overloads(o)
+{
+}
+
+OverloadDataRootNode::~OverloadDataRootNode() = default;
+
+AbstractMetaFunctionCPtr OverloadDataRootNode::referenceFunction() const
+{
+ return m_overloads.constFirst();
+}
+
+const AbstractMetaArgument *OverloadDataNode::overloadArgument(const AbstractMetaFunctionCPtr &func) const
+{
+ if (isRoot() || !m_overloads.contains(func))
+ return nullptr;
+
+ int argPos = 0;
+ int removed = 0;
+ for (int i = 0; argPos <= m_argPos; i++) {
+ if (func->arguments().at(i).isModifiedRemoved())
+ removed++;
+ else
+ argPos++;
+ }
+
+ return &func->arguments().at(m_argPos + removed);
+}
+
+bool OverloadDataRootNode::nextArgumentHasDefaultValue() const
+{
+ for (const auto &overloadData : m_children) {
+ if (overloadData->getFunctionWithDefaultValue())
+ return true;
+ }
+ return false;
+}
+
+static const OverloadDataRootNode *_findNextArgWithDefault(const OverloadDataRootNode *overloadData)
+{
+ if (overloadData->getFunctionWithDefaultValue())
+ return overloadData;
+
+ const OverloadDataRootNode *result = nullptr;
+ const OverloadDataList &data = overloadData->children();
+ for (const auto &odata : data) {
+ const auto *tmp = _findNextArgWithDefault(odata.get());
+ if (!result || (tmp && result->argPos() > tmp->argPos()))
+ result = tmp;
+ }
+ return result;
+}
+
+const OverloadDataRootNode *OverloadDataRootNode::findNextArgWithDefault() const
+{
+ return _findNextArgWithDefault(this);
+}
+
+bool OverloadDataRootNode::isFinalOccurrence(const AbstractMetaFunctionCPtr &func) const
+{
+ for (const auto &pd : m_children) {
+ if (pd->overloads().contains(func))
+ return false;
+ }
+ return true;
+}
+
+AbstractMetaFunctionCPtr OverloadDataRootNode::getFunctionWithDefaultValue() const
+{
+ const qsizetype argpos = argPos();
+ for (const auto &func : m_overloads) {
+ qsizetype removedArgs = 0;
+ for (qsizetype i = 0; i <= argpos + removedArgs; i++) {
+ if (func->arguments().at(i).isModifiedRemoved())
+ removedArgs++;
+ }
+ if (func->arguments().at(argpos + removedArgs).hasDefaultValueExpression())
+ return func;
+ }
+ return {};
+}
+
+QList<int> OverloadData::invalidArgumentLengths() const
+{
+ QSet<int> validArgLengths;
+
+ for (const auto &func : m_overloads) {
+ const AbstractMetaArgumentList args = func->arguments();
+ int offset = 0;
+ for (qsizetype i = 0; i < args.size(); ++i) {
+ if (func->arguments().at(i).isModifiedRemoved()) {
+ offset++;
+ } else {
+ if (args.at(i).hasDefaultValueExpression())
+ validArgLengths << i-offset;
+ }
+ }
+ validArgLengths << args.size() - offset;
+ }
+
+ QList<int> invalidArgLengths;
+ for (int i = m_minArgs + 1; i < m_maxArgs; i++) {
+ if (!validArgLengths.contains(i))
+ invalidArgLengths.append(i);
+ }
+
+ return invalidArgLengths;
+}
+
+int OverloadData::numberOfRemovedArguments(const AbstractMetaFunctionCPtr &func)
+{
+ return std::count_if(func->arguments().cbegin(), func->arguments().cend(),
+ [](const AbstractMetaArgument &a) { return a.isModifiedRemoved(); });
+}
+
+int OverloadData::numberOfRemovedArguments(const AbstractMetaFunctionCPtr &func, int finalArgPos)
+{
+ Q_ASSERT(finalArgPos >= 0);
+ int removed = 0;
+ const auto size = func->arguments().size();
+ for (qsizetype i = 0; i < qMin(size, qsizetype(finalArgPos + removed)); ++i) {
+ if (func->arguments().at(i).isModifiedRemoved())
+ ++removed;
+ }
+ return removed;
+}
+
+void OverloadData::dumpGraph(const QString &filename) const
+{
+ QFile file(filename);
+ if (file.open(QFile::WriteOnly)) {
+ QTextStream s(&file);
+ dumpRootGraph(s, m_minArgs, m_maxArgs);
+ }
+}
+
+QString OverloadData::dumpGraph() const
+{
+ QString result;
+ QTextStream s(&result);
+ dumpRootGraph(s, m_minArgs, m_maxArgs);
+ return result;
+}
+
+bool OverloadData::showGraph() const
+{
+ return showDotGraph(referenceFunction()->name(), dumpGraph());
+}
+
+static inline QString toHtml(QString s)
+{
+ s.replace(u'<', u"&lt;"_s);
+ s.replace(u'>', u"&gt;"_s);
+ s.replace(u'&', u"&amp;"_s);
+ return s;
+}
+
+void OverloadDataRootNode::dumpRootGraph(QTextStream &s, int minArgs, int maxArgs) const
+{
+ const auto rfunc = referenceFunction();
+ s << "digraph OverloadedFunction {\n";
+ s << " graph [fontsize=12 fontname=freemono labelloc=t splines=true overlap=false rankdir=LR];\n";
+
+ // Shows all function signatures
+ s << "legend [fontsize=9 fontname=freemono shape=rect label=\"";
+ for (const auto &func : m_overloads) {
+ s << "f" << functionNumber(func) << " : "
+ << toHtml(func->type().cppSignature())
+ << ' ' << toHtml(func->minimalSignature()) << "\\l";
+ }
+ s << "\"];\n";
+
+ // Function box title
+ s << " \"" << rfunc->name() << "\" [shape=plaintext style=\"filled,bold\" margin=0 fontname=freemono fillcolor=white penwidth=1 ";
+ s << "label=<<table border=\"0\" cellborder=\"0\" cellpadding=\"3\" bgcolor=\"white\">";
+ s << "<tr><td bgcolor=\"black\" align=\"center\" cellpadding=\"6\" colspan=\"2\"><font color=\"white\">";
+ if (rfunc->ownerClass())
+ s << rfunc->ownerClass()->name() << "::";
+ s << toHtml(rfunc->name()) << "</font>";
+ if (rfunc->isVirtual()) {
+ s << "<br/><font color=\"white\" point-size=\"10\">&lt;&lt;";
+ if (rfunc->isAbstract())
+ s << "pure ";
+ s << "virtual&gt;&gt;</font>";
+ }
+ s << "</td></tr>";
+
+ // Function return type
+ s << "<tr><td bgcolor=\"gray\" align=\"right\">original type</td><td bgcolor=\"gray\" align=\"left\">"
+ << toHtml(rfunc->type().cppSignature())
+ << "</td></tr>";
+
+ // Shows type changes for all function signatures
+ for (const auto &func : m_overloads) {
+ if (!func->isTypeModified())
+ continue;
+ s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << functionNumber(func);
+ s << "-type</td><td bgcolor=\"gray\" align=\"left\">";
+ s << toHtml(func->modifiedTypeName()) << "</td></tr>";
+ }
+
+ // Minimum and maximum number of arguments
+ s << "<tr><td bgcolor=\"gray\" align=\"right\">minArgs</td><td bgcolor=\"gray\" align=\"left\">";
+ s << minArgs << "</td></tr>";
+ s << "<tr><td bgcolor=\"gray\" align=\"right\">maxArgs</td><td bgcolor=\"gray\" align=\"left\">";
+ s << maxArgs << "</td></tr>";
+
+ if (rfunc->ownerClass()) {
+ if (rfunc->implementingClass() != rfunc->ownerClass())
+ s << "<tr><td align=\"right\">implementor</td><td align=\"left\">" << rfunc->implementingClass()->name() << "</td></tr>";
+ if (rfunc->declaringClass() != rfunc->ownerClass() && rfunc->declaringClass() != rfunc->implementingClass())
+ s << "<tr><td align=\"right\">declarator</td><td align=\"left\">" << rfunc->declaringClass()->name() << "</td></tr>";
+ }
+
+ // Overloads for the signature to present point
+ s << "<tr><td bgcolor=\"gray\" align=\"right\">overloads</td><td bgcolor=\"gray\" align=\"left\">";
+ for (const auto &func : m_overloads)
+ s << 'f' << functionNumber(func) << ' ';
+ s << "</td></tr>";
+
+ s << "</table>> ];\n";
+
+ for (const auto &pd : m_children) {
+ s << " \"" << rfunc->name() << "\" -> ";
+ pd->dumpNodeGraph(s);
+ }
+
+ s << "}\n";
+}
+
+void OverloadDataNode::dumpNodeGraph(QTextStream &s) const
+{
+ QString argId = u"arg_"_s + QString::number(quintptr(this));
+ s << argId << ";\n";
+
+ s << " \"" << argId << "\" [shape=\"plaintext\" style=\"filled,bold\" margin=\"0\" fontname=\"freemono\" fillcolor=\"white\" penwidth=1 ";
+ s << "label=<<table border=\"0\" cellborder=\"0\" cellpadding=\"3\" bgcolor=\"white\">";
+
+ // Argument box title
+ s << "<tr><td bgcolor=\"black\" align=\"left\" cellpadding=\"2\" colspan=\"2\">";
+ s << "<font color=\"white\" point-size=\"11\">arg #" << argPos() << "</font></td></tr>";
+
+ // Argument type information
+ const QString type = modifiedArgType().cppSignature();
+ s << "<tr><td bgcolor=\"gray\" align=\"right\">type</td><td bgcolor=\"gray\" align=\"left\">";
+ s << toHtml(type) << "</td></tr>";
+ if (isTypeModified()) {
+ s << "<tr><td bgcolor=\"gray\" align=\"right\">orig. type</td><td bgcolor=\"gray\" align=\"left\">";
+ s << toHtml(argType().cppSignature()) << "</td></tr>";
+ }
+
+ const OverloadDataRootNode *root = this;
+ while (!root->isRoot())
+ root = root->parent();
+
+ // Overloads for the signature to present point
+ s << "<tr><td bgcolor=\"gray\" align=\"right\">overloads</td><td bgcolor=\"gray\" align=\"left\">";
+ for (const auto &func : m_overloads)
+ s << 'f' << root->functionNumber(func) << ' ';
+ s << "</td></tr>";
+
+ // Show default values (original and modified) for various functions
+ for (const auto &func : m_overloads) {
+ const AbstractMetaArgument *arg = overloadArgument(func);
+ if (!arg)
+ continue;
+ const int n = root->functionNumber(func);
+ QString argDefault = arg->defaultValueExpression();
+ if (!argDefault.isEmpty() ||
+ argDefault != arg->originalDefaultValueExpression()) {
+ s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << n;
+ s << "-default</td><td bgcolor=\"gray\" align=\"left\">";
+ s << argDefault << "</td></tr>";
+ }
+ if (argDefault != arg->originalDefaultValueExpression()) {
+ s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << n;
+ s << "-orig-default</td><td bgcolor=\"gray\" align=\"left\">";
+ s << arg->originalDefaultValueExpression() << "</td></tr>";
+ }
+ }
+
+ s << "</table>>];\n";
+
+ for (const auto &pd : m_children) {
+ s << " " << argId << " -> ";
+ pd->dumpNodeGraph(s);
+ }
+}
+
+int OverloadDataRootNode::functionNumber(const AbstractMetaFunctionCPtr &func) const
+{
+ return m_overloads.indexOf(func);
+}
+
+bool OverloadData::pythonFunctionWrapperUsesListOfArguments() const
+{
+ auto referenceFunction = m_overloads.constFirst();
+ if (referenceFunction->isCallOperator())
+ return true;
+ if (referenceFunction->isOperatorOverload())
+ return false;
+ const int maxArgs = this->maxArgs();
+ const int minArgs = this->minArgs();
+ return (minArgs != maxArgs)
+ || (maxArgs > 1)
+ || referenceFunction->isConstructor()
+ || hasArgumentWithDefaultValue();
+}
+
+bool OverloadData::hasArgumentWithDefaultValue() const
+{
+ if (maxArgs() == 0)
+ return false;
+ for (const auto &func : m_overloads) {
+ if (hasArgumentWithDefaultValue(func))
+ return true;
+ }
+ return false;
+}
+
+bool OverloadData::hasArgumentWithDefaultValue(const AbstractMetaFunctionCPtr &func)
+{
+ const AbstractMetaArgumentList &arguments = func->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+ if (!arg.isModifiedRemoved() && arg.hasDefaultValueExpression())
+ return true;
+ }
+ return false;
+}
+
+AbstractMetaArgumentList OverloadData::getArgumentsWithDefaultValues(const AbstractMetaFunctionCPtr &func)
+{
+ AbstractMetaArgumentList args;
+ const AbstractMetaArgumentList &arguments = func->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+ if (!arg.hasDefaultValueExpression()
+ || arg.isModifiedRemoved())
+ continue;
+ args << arg;
+ }
+ return args;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+void OverloadDataRootNode::formatReferenceFunction(QDebug &d) const
+{
+ auto refFunc = referenceFunction();
+ d << '"';
+ if (auto owner = refFunc->ownerClass())
+ d << owner->qualifiedCppName() << "::";
+ d << refFunc->minimalSignature() << '"';
+ if (m_overloads.constFirst()->isReverseOperator())
+ d << " [reverseop]";
+}
+
+void OverloadDataRootNode::formatOverloads(QDebug &d) const
+{
+ const qsizetype count = m_overloads.size();
+ d << ", overloads[" << count << ']';
+ if (count < 2)
+ return;
+ d << "=(";
+ for (qsizetype i = 0; i < count; ++i) {
+ if (i)
+ d << '\n';
+ d << m_overloads.at(i)->signature();
+ }
+ d << ')';
+}
+
+void OverloadDataRootNode::formatNextOverloadData(QDebug &d) const
+{
+ const qsizetype count = m_children.size();
+ d << ", next[" << count << ']';
+ if (d.verbosity() >= 3) {
+ d << "=(";
+ for (qsizetype i = 0; i < count; ++i) {
+ if (i)
+ d << '\n';
+ m_children.at(i)->formatDebug(d);
+ }
+ d << ')';
+ }
+}
+
+void OverloadDataRootNode::formatDebug(QDebug &d) const
+{
+ formatReferenceFunction(d);
+ formatOverloads(d);
+ formatNextOverloadData(d);
+}
+
+void OverloadDataNode::formatDebug(QDebug &d) const
+{
+ d << "OverloadDataNode(";
+ formatReferenceFunction(d);
+ d << ", argPos=" << m_argPos;
+ if (m_argument.argumentIndex() != m_argPos)
+ d << ", argIndex=" << m_argument.argumentIndex();
+ d << ", argType=\"" << m_argument.type().cppSignature() << '"';
+ if (isTypeModified())
+ d << ", modifiedArgType=\"" << modifiedArgType().cppSignature() << '"';
+ formatOverloads(d);
+ formatNextOverloadData(d);
+ d << ')';
+}
+
+void OverloadData::formatDebug(QDebug &d) const
+{
+ d << "OverloadData(";
+ formatReferenceFunction(d);
+ d << ", minArgs=" << m_minArgs << ", maxArgs=" << m_maxArgs;
+ formatOverloads(d);
+ formatNextOverloadData(d);
+ d << ')';
+}
+
+QDebug operator<<(QDebug d, const OverloadData &od)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ od.formatDebug(d);
+ return d;
+}
+#endif // !QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken6/generator/shiboken/overloaddata.h b/sources/shiboken6/generator/shiboken/overloaddata.h
new file mode 100644
index 000000000..875a5a8b5
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/overloaddata.h
@@ -0,0 +1,183 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef OVERLOADDATA_H
+#define OVERLOADDATA_H
+
+#include <apiextractorresult.h>
+#include <abstractmetaargument.h>
+
+#include <QtCore/QBitArray>
+#include <QtCore/QList>
+
+#include <memory>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+QT_FORWARD_DECLARE_CLASS(QTextStream)
+
+class OverloadDataNode;
+using OverloadDataNodePtr = std::shared_ptr<OverloadDataNode>;
+using OverloadDataList = QList<OverloadDataNodePtr>;
+
+/// The root node of OverloadData. It contains all functions
+class OverloadDataRootNode
+{
+public:
+ virtual ~OverloadDataRootNode();
+
+ OverloadDataRootNode(const OverloadDataRootNode &) = delete;
+ OverloadDataRootNode &operator=(const OverloadDataRootNode &) = delete;
+ OverloadDataRootNode(OverloadDataRootNode &&) = delete;
+ OverloadDataRootNode &operator=(OverloadDataRootNode &&) = delete;
+
+ virtual int argPos() const { return -1; }
+ virtual const OverloadDataRootNode *parent() const { return nullptr; }
+
+ bool isRoot() const { return parent() == nullptr; }
+
+ AbstractMetaFunctionCPtr referenceFunction() const;
+
+ const AbstractMetaFunctionCList &overloads() const { return m_overloads; }
+ const OverloadDataList &children() const { return m_children; }
+
+ bool nextArgumentHasDefaultValue() const;
+
+ /// Returns the function that has a default value at the current OverloadData argument position, otherwise returns null.
+ AbstractMetaFunctionCPtr getFunctionWithDefaultValue() const;
+
+ /// Returns the nearest occurrence, including this instance, of an argument with a default value.
+ const OverloadDataRootNode *findNextArgWithDefault() const;
+ bool isFinalOccurrence(const AbstractMetaFunctionCPtr &func) const;
+
+ int functionNumber(const AbstractMetaFunctionCPtr &func) const;
+
+#ifndef QT_NO_DEBUG_STREAM
+ virtual void formatDebug(QDebug &d) const;
+#endif
+
+ OverloadDataNode *addOverloadDataNode(const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaArgument &arg);
+
+protected:
+ OverloadDataRootNode(const AbstractMetaFunctionCList &o= {});
+
+ void dumpRootGraph(QTextStream &s, int minArgs, int maxArgs) const;
+ void sortNextOverloads(const ApiExtractorResult &api);
+
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatReferenceFunction(QDebug &d) const;
+ void formatOverloads(QDebug &d) const;
+ void formatNextOverloadData(QDebug &d) const;
+#endif
+
+ AbstractMetaFunctionCList m_overloads;
+ OverloadDataList m_children;
+};
+
+/// OverloadDataNode references an argument/type combination.
+class OverloadDataNode : public OverloadDataRootNode
+{
+public:
+ explicit OverloadDataNode(const AbstractMetaFunctionCPtr &func,
+ OverloadDataRootNode *parent,
+ const AbstractMetaArgument &arg, int argPos,
+ const QString argTypeReplaced = {});
+ void addOverload(const AbstractMetaFunctionCPtr &func);
+
+ int argPos() const override { return m_argPos; }
+ const OverloadDataRootNode *parent() const override;
+ void dumpNodeGraph(QTextStream &s) const;
+
+ const AbstractMetaArgument &argument() const
+ { return m_argument; }
+ const AbstractMetaType &argType() const { return m_argument.type(); }
+ const AbstractMetaType &modifiedArgType() const { return m_argument.modifiedType(); }
+
+ bool isTypeModified() const { return m_argument.isTypeModified(); }
+
+ const AbstractMetaArgument *overloadArgument(const AbstractMetaFunctionCPtr &func) const;
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+
+private:
+ AbstractMetaArgument m_argument;
+ QString m_argTypeReplaced;
+ OverloadDataRootNode *m_parent = nullptr;
+
+ int m_argPos = -1; // Position excluding modified/removed arguments.
+};
+
+class OverloadData : public OverloadDataRootNode
+{
+public:
+ explicit OverloadData(const AbstractMetaFunctionCList &overloads,
+ const ApiExtractorResult &api);
+
+ int minArgs() const { return m_minArgs; }
+ int maxArgs() const { return m_maxArgs; }
+
+ /// Returns true if any of the overloads for the current OverloadData has a return type different from void.
+ bool hasNonVoidReturnType() const;
+
+ /// Returns true if any of the overloads for the current OverloadData has a varargs argument.
+ bool hasVarargs() const;
+
+ /// Returns true if any of the overloads for the current OverloadData is static.
+ bool hasStaticFunction() const;
+
+ /// Returns true if any of the overloads passed as argument is static.
+ static bool hasStaticFunction(const AbstractMetaFunctionCList &overloads);
+
+ /// Returns true if any of the overloads for the current OverloadData is classmethod.
+ bool hasClassMethod() const;
+
+ /// Returns true if any of the overloads passed as argument is classmethod.
+ static bool hasClassMethod(const AbstractMetaFunctionCList &overloads);
+
+ /// Returns true if any of the overloads for the current OverloadData is not static.
+ bool hasInstanceFunction() const;
+
+ /// Returns true if any of the overloads passed as argument is not static.
+ static bool hasInstanceFunction(const AbstractMetaFunctionCList &overloads);
+
+ /// Returns true if among the overloads for the current OverloadData there are static and non-static methods altogether.
+ bool hasStaticAndInstanceFunctions() const;
+
+ /// Returns true if among the overloads passed as argument there are static and non-static methods altogether.
+ static bool hasStaticAndInstanceFunctions(const AbstractMetaFunctionCList &overloads);
+
+ QList<int> invalidArgumentLengths() const;
+
+ static int numberOfRemovedArguments(const AbstractMetaFunctionCPtr &func);
+ static int numberOfRemovedArguments(const AbstractMetaFunctionCPtr &func, int finalArgPos);
+
+ void dumpGraph(const QString &filename) const;
+ QString dumpGraph() const;
+ bool showGraph() const;
+
+ /// Returns true if a list of arguments is used (METH_VARARGS)
+ bool pythonFunctionWrapperUsesListOfArguments() const;
+
+ bool hasArgumentWithDefaultValue() const;
+ static bool hasArgumentWithDefaultValue(const AbstractMetaFunctionCPtr &func);
+
+ /// Returns a list of function arguments which have default values and were not removed.
+ static AbstractMetaArgumentList getArgumentsWithDefaultValues(const AbstractMetaFunctionCPtr &func);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &) const override;
+#endif
+
+private:
+ int m_minArgs = 256;
+ int m_maxArgs = 0;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug, const OverloadData &);
+#endif
+
+#endif // OVERLOADDATA_H
diff --git a/sources/shiboken6/generator/shiboken/pytypenames.h b/sources/shiboken6/generator/shiboken/pytypenames.h
new file mode 100644
index 000000000..6c7658ff6
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/pytypenames.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef PYTYPENAMES_H
+#define PYTYPENAMES_H
+
+#include <QtCore/QString>
+
+constexpr auto pyBoolT = QLatin1StringView ("PyBool");
+constexpr auto pyFloatT = QLatin1StringView ("PyFloat");
+constexpr auto pyLongT = QLatin1StringView ("PyLong");
+constexpr auto pyObjectT = QLatin1StringView ("object");
+constexpr auto pyStrT = QLatin1StringView ("str");
+
+// PYSIDE-1499: A custom type determined by existence of an `__fspath__` attribute.
+constexpr auto pyPathLikeT = QLatin1StringView ("PyPathLike");
+
+constexpr auto cPyBufferT = QLatin1StringView ("PyBuffer");
+constexpr auto cPyListT = QLatin1StringView ("PyList");
+constexpr auto cPyObjectT = QLatin1StringView ("PyObject");
+constexpr auto cPySequenceT = QLatin1StringView ("PySequence");
+constexpr auto cPyTypeObjectT = QLatin1StringView ("PyTypeObject");
+
+// numpy
+constexpr auto cPyArrayObjectT = QLatin1StringView ("PyArrayObject");
+
+constexpr auto sbkCharT = QLatin1StringView ("SbkChar");
+
+#endif // PYTYPENAMES_H
diff --git a/sources/shiboken6/generator/shiboken/shibokengenerator.cpp b/sources/shiboken6/generator/shiboken/shibokengenerator.cpp
new file mode 100644
index 000000000..67fd9c994
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/shibokengenerator.cpp
@@ -0,0 +1,2650 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "shibokengenerator.h"
+#include "generatorstrings.h"
+#include "generatorargument.h"
+#include "defaultvalue.h"
+#include "generatorcontext.h"
+#include "apiextractorresult.h"
+#include "codesnip.h"
+#include "customconversion.h"
+#include "ctypenames.h"
+#include <abstractmetabuilder.h>
+#include <abstractmetaenum.h>
+#include <abstractmetafield.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <abstractmetalang_helpers.h>
+#include <usingmember.h>
+#include <exception.h>
+#include <messages.h>
+#include <modifications.h>
+#include "overloaddata.h"
+#include <optionsparser.h>
+#include "propertyspec.h"
+#include "pytypenames.h"
+#include <reporthandler.h>
+#include <textstream.h>
+#include <typedatabase.h>
+#include <containertypeentry.h>
+#include <customtypenentry.h>
+#include <enumtypeentry.h>
+#include <flagstypeentry.h>
+#include <namespacetypeentry.h>
+#include <primitivetypeentry.h>
+#include <pythontypeentry.h>
+#include <smartpointertypeentry.h>
+#include <valuetypeentry.h>
+
+#include <iostream>
+
+#include "qtcompat.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QDebug>
+#include <QtCore/QRegularExpression>
+
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <utility>
+
+using namespace Qt::StringLiterals;
+
+static constexpr auto PARENT_CTOR_HEURISTIC = "enable-parent-ctor-heuristic"_L1;
+static constexpr auto RETURN_VALUE_HEURISTIC = "enable-return-value-heuristic"_L1;
+static constexpr auto DISABLE_VERBOSE_ERROR_MESSAGES = "disable-verbose-error-messages"_L1;
+static constexpr auto USE_ISNULL_AS_NB_BOOL = "use-isnull-as-nb-bool"_L1;
+// FIXME PYSIDE 7: Remove USE_ISNULL_AS_NB_NONZERO/USE_OPERATOR_BOOL_AS_NB_NONZERO
+static constexpr auto USE_ISNULL_AS_NB_NONZERO = "use-isnull-as-nb_nonzero"_L1;
+static constexpr auto USE_OPERATOR_BOOL_AS_NB_BOOL = "use-operator-bool-as-nb-bool"_L1;
+static constexpr auto USE_OPERATOR_BOOL_AS_NB_NONZERO = "use-operator-bool-as-nb-nonzero"_L1;
+static constexpr auto WRAPPER_DIAGNOSTICS = "wrapper-diagnostics"_L1;
+static constexpr auto NO_IMPLICIT_CONVERSIONS = "no-implicit-conversions"_L1;
+static constexpr auto LEAN_HEADERS = "lean-headers"_L1;
+
+QString CPP_ARG_N(int i)
+{
+ return CPP_ARG + QString::number(i);
+}
+
+constexpr auto CPP_ARG_REMOVED_PREFIX = "removed_cppArg"_L1;
+
+QString CPP_ARG_REMOVED(int i)
+{
+ return CPP_ARG_REMOVED_PREFIX + QString::number(i);
+}
+
+const char *const METHOD_DEF_SENTINEL = "{nullptr, nullptr, 0, nullptr} // Sentinel\n";
+const char *const PYTHON_TO_CPPCONVERSION_STRUCT = "Shiboken::Conversions::PythonToCppConversion";
+
+const char *const openTargetExternC = R"(
+// Target ---------------------------------------------------------
+
+extern "C" {
+)";
+const char *const closeExternC = "} // extern \"C\"\n\n";
+const char *const richCompareComment =
+ "// PYSIDE-74: By default, we redirect to object's tp_richcompare (which is `==`, `!=`).\n";
+
+struct ShibokenGeneratorOptions
+{
+ bool useCtorHeuristic = false;
+ bool userReturnValueHeuristic = false;
+ bool verboseErrorMessagesDisabled = false;
+ bool useIsNullAsNbBool = false;
+ // FIXME PYSIDE 7 Flip m_leanHeaders default or remove?
+ bool leanHeaders = false;
+ bool useOperatorBoolAsNbBool = false;
+ // FIXME PYSIDE 7 Flip generateImplicitConversions default or remove?
+ bool generateImplicitConversions = true;
+ bool wrapperDiagnostics = false;
+};
+
+struct GeneratorClassInfoCacheEntry
+{
+ ShibokenGenerator::FunctionGroups functionGroups;
+ QList<AbstractMetaFunctionCList> numberProtocolOperators;
+ BoolCastFunctionOptional boolCastFunctionO;
+ bool needsGetattroFunction = false;
+};
+
+using GeneratorClassInfoCache = QHash<AbstractMetaClassCPtr, GeneratorClassInfoCacheEntry>;
+
+Q_GLOBAL_STATIC(GeneratorClassInfoCache, generatorClassInfoCache)
+
+static const char CHECKTYPE_REGEX[] = R"(%CHECKTYPE\[([^\[]*)\]\()";
+static const char ISCONVERTIBLE_REGEX[] = R"(%ISCONVERTIBLE\[([^\[]*)\]\()";
+static const char CONVERTTOPYTHON_REGEX[] = R"(%CONVERTTOPYTHON\[([^\[]*)\]\()";
+// Capture a '*' leading the variable name into the target
+// so that "*valuePtr = %CONVERTTOCPP..." works as expected.
+static const char CONVERTTOCPP_REGEX[] =
+ R"((\*?%?[a-zA-Z_][\w\.]*(?:\[[^\[^<^>]+\])*)(?:\s+)=(?:\s+)%CONVERTTOCPP\[([^\[]*)\]\()";
+
+const ShibokenGenerator::TypeSystemConverterRegExps &
+ ShibokenGenerator::typeSystemConvRegExps()
+{
+ static const TypeSystemConverterRegExps result = {
+ QRegularExpression(QLatin1StringView(CHECKTYPE_REGEX)),
+ QRegularExpression(QLatin1StringView(ISCONVERTIBLE_REGEX)),
+ QRegularExpression(QLatin1StringView(CONVERTTOCPP_REGEX)),
+ QRegularExpression(QLatin1StringView(CONVERTTOPYTHON_REGEX))
+ };
+ return result;
+}
+
+// Options are static to avoid duplicated handling since ShibokenGenerator
+// is instantiated for HeaderGenerator and CppGenerator.
+ShibokenGeneratorOptions ShibokenGenerator::m_options;
+
+ShibokenGenerator::ShibokenGenerator() = default;
+
+ShibokenGenerator::~ShibokenGenerator() = default;
+
+// Correspondences between primitive and Python types.
+static const QHash<QString, QString> &primitiveTypesCorrespondences()
+{
+ static const QHash<QString, QString> result = {
+ {u"bool"_s, pyBoolT},
+ {u"char"_s, sbkCharT},
+ {u"signed char"_s, sbkCharT},
+ {u"unsigned char"_s, sbkCharT},
+ {intT, pyLongT},
+ {u"signed int"_s, pyLongT},
+ {u"uint"_s, pyLongT},
+ {u"unsigned int"_s, pyLongT},
+ {shortT, pyLongT},
+ {u"ushort"_s, pyLongT},
+ {u"signed short"_s, pyLongT},
+ {u"signed short int"_s, pyLongT},
+ {unsignedShortT, pyLongT},
+ {u"unsigned short int"_s, pyLongT},
+ {longT, pyLongT},
+ {doubleT, pyFloatT},
+ {floatT, pyFloatT},
+ {u"unsigned long"_s, pyLongT},
+ {u"signed long"_s, pyLongT},
+ {u"ulong"_s, pyLongT},
+ {u"unsigned long int"_s, pyLongT},
+ {u"long long"_s, pyLongT},
+ {u"__int64"_s, pyLongT},
+ {u"unsigned long long"_s, pyLongT},
+ {u"unsigned __int64"_s, pyLongT},
+ {u"size_t"_s, pyLongT}
+ };
+ return result;
+}
+
+const QHash<QString, QChar> &ShibokenGenerator::formatUnits()
+{
+ static const QHash<QString, QChar> result = {
+ {u"char"_s, u'b'},
+ {u"unsigned char"_s, u'B'},
+ {intT, u'i'},
+ {u"unsigned int"_s, u'I'},
+ {shortT, u'h'},
+ {unsignedShortT, u'H'},
+ {longT, u'l'},
+ {unsignedLongLongT, u'k'},
+ {longLongT, u'L'},
+ {u"__int64"_s, u'L'},
+ {unsignedLongLongT, u'K'},
+ {u"unsigned __int64"_s, u'K'},
+ {doubleT, u'd'},
+ {floatT, u'f'},
+ };
+ return result;
+}
+
+QString ShibokenGenerator::translateTypeForWrapperMethod(const AbstractMetaType &cType,
+ const AbstractMetaClassCPtr &context,
+ Options options) const
+{
+ if (cType.isArray()) {
+ return translateTypeForWrapperMethod(*cType.arrayElementType(), context, options)
+ + u"[]"_s;
+ }
+
+ if (avoidProtectedHack() && cType.isEnum()) {
+ auto metaEnum = api().findAbstractMetaEnum(cType.typeEntry());
+ if (metaEnum && metaEnum->isProtected())
+ return protectedEnumSurrogateName(metaEnum.value());
+ }
+
+ return translateType(cType, context, options);
+}
+
+bool ShibokenGenerator::shouldGenerateCppWrapper(const AbstractMetaClassCPtr &metaClass)
+{
+ const auto wrapper = metaClass->cppWrapper();
+ return wrapper.testFlag(AbstractMetaClass::CppVirtualMethodWrapper)
+ || (avoidProtectedHack()
+ && wrapper.testFlag(AbstractMetaClass::CppProtectedHackWrapper));
+}
+
+bool ShibokenGenerator::shouldGenerateMetaObjectFunctions(const AbstractMetaClassCPtr &metaClass)
+{
+ return usePySideExtensions()
+ && (!avoidProtectedHack() || !metaClass->hasPrivateDestructor())
+ && !metaClass->typeEntry()->typeFlags()
+ .testFlag(ComplexTypeEntry::DisableQtMetaObjectFunctions)
+ && isQObject(metaClass);
+}
+
+ShibokenGenerator::FunctionGeneration ShibokenGenerator::functionGeneration(
+ const AbstractMetaFunctionCPtr &func)
+{
+ FunctionGeneration result;
+
+ const auto functionType = func->functionType();
+ switch (functionType) {
+ case AbstractMetaFunction::ConversionOperator:
+ case AbstractMetaFunction::AssignmentOperatorFunction:
+ case AbstractMetaFunction::MoveAssignmentOperatorFunction:
+ case AbstractMetaFunction::DestructorFunction:
+ case AbstractMetaFunction::SignalFunction:
+ case AbstractMetaFunction::GetAttroFunction:
+ case AbstractMetaFunction::SetAttroFunction:
+ return result;
+ default:
+ if (func->isUserAdded() || func->usesRValueReferences() || !func->isWhiteListed())
+ return result;
+ break;
+ }
+
+ const bool notModifiedRemoved = !func->isModifiedRemoved();
+ const bool isPrivate = func->isPrivate() && !func->isVisibilityModifiedToPrivate();
+ switch (functionType) {
+ case AbstractMetaFunction::ConstructorFunction:
+ if (!isPrivate && notModifiedRemoved)
+ result.setFlag(FunctionGenerationFlag::WrapperConstructor);
+ return result;
+ case AbstractMetaFunction::CopyConstructorFunction:
+ if (!isPrivate && notModifiedRemoved)
+ result.setFlag(FunctionGenerationFlag::WrapperSpecialCopyConstructor);
+ return result;
+ case AbstractMetaFunction::NormalFunction:
+ case AbstractMetaFunction::SlotFunction:
+ if (avoidProtectedHack() && func->isProtected())
+ result.setFlag(FunctionGenerationFlag::ProtectedWrapper);
+ break;
+ default:
+ break;
+ }
+
+ // Check on virtuals (including operators).
+ const bool isAbstract = func->isAbstract();
+ if (!(isAbstract || func->isVirtual())
+ || func->cppAttributes().testFlag(FunctionAttribute::Final)
+ || func->isModifiedFinal()) {
+ return result;
+ }
+
+ // MetaObject virtuals only need to be declared; CppGenerator creates a
+ // special implementation.
+ if (functionType == AbstractMetaFunction::NormalFunction
+ && usePySideExtensions() && isQObject(func->ownerClass())) {
+ const QString &name = func->name();
+ if (name == u"metaObject"_s || name == u"qt_metacall") {
+ result.setFlag(FunctionGenerationFlag::QMetaObjectMethod);
+ return result;
+ }
+ }
+
+ // Pure virtual functions need a default implementation even if private.
+ if (isAbstract || (notModifiedRemoved && !isPrivate))
+ result.setFlag(FunctionGenerationFlag::VirtualMethod);
+
+ return result;
+}
+
+AbstractMetaFunctionCList ShibokenGenerator::implicitConversions(const TypeEntryCPtr &t) const
+{
+ if (!generateImplicitConversions() || !t->isValue())
+ return {};
+ auto vte = std::static_pointer_cast<const ValueTypeEntry>(t);
+ auto customConversion = vte->customConversion();
+ if (customConversion && customConversion->replaceOriginalTargetToNativeConversions())
+ return {};
+
+ auto result = api().implicitConversions(t);
+ auto end = std::remove_if(result.begin(), result.end(),
+ [](const AbstractMetaFunctionCPtr &f) {
+ return f->isUserAdded();
+ });
+ result.erase(end, result.end());
+ return result;
+}
+
+QString ShibokenGenerator::wrapperName(const AbstractMetaClassCPtr &metaClass)
+{
+ Q_ASSERT(shouldGenerateCppWrapper(metaClass));
+ QString result = metaClass->name();
+ if (metaClass->enclosingClass()) // is a inner class
+ result.replace(u"::"_s, u"_"_s);
+ return result + u"Wrapper"_s;
+}
+
+QString ShibokenGenerator::fullPythonClassName(const AbstractMetaClassCPtr &metaClass)
+{
+ QString fullClassName = metaClass->name();
+ auto enclosing = metaClass->enclosingClass();
+ while (enclosing) {
+ if (NamespaceTypeEntry::isVisibleScope(enclosing->typeEntry()))
+ fullClassName.prepend(enclosing->name() + u'.');
+ enclosing = enclosing->enclosingClass();
+ }
+ fullClassName.prepend(metaClass->typeEntry()->targetLangPackage() + u'.');
+ return fullClassName;
+}
+
+QString ShibokenGenerator::headerFileNameForContext(const GeneratorContext &context)
+{
+ return fileNameForContextHelper(context, u"_wrapper.h"_s);
+}
+
+// PYSIDE-500: When avoiding the protected hack, also include the inherited
+// wrapper classes of the *current* module, because without the protected hack,
+// we sometimes need to cast inherited wrappers. Inherited classes
+// of *other* modules are completely regenerated by the header generator
+// since the wrapper headers are not installed.
+
+IncludeGroup ShibokenGenerator::baseWrapperIncludes(const GeneratorContext &classContext) const
+{
+ IncludeGroup result{u"Wrappers"_s, {}};
+ if (!classContext.useWrapper() || !avoidProtectedHack()
+ || classContext.forSmartPointer()) {
+ return result;
+ }
+
+ const auto moduleEntry = TypeDatabase::instance()->defaultTypeSystemType();
+ const auto &baseClasses = allBaseClasses(classContext.metaClass());
+ for (const auto &base : baseClasses) {
+ const auto te = base->typeEntry();
+ if (te->codeGeneration() == TypeEntry::GenerateCode) { // current module
+ const auto context = contextForClass(base);
+ if (context.useWrapper()) {
+ const QString header = headerFileNameForContext(context);
+ const auto type = typeSystemTypeEntry(te) == moduleEntry
+ ? Include::LocalPath : Include::IncludePath;
+ result.append(Include(type, header));
+ }
+ }
+ }
+ return result;
+}
+
+QString ShibokenGenerator::fullPythonFunctionName(const AbstractMetaFunctionCPtr &func, bool forceFunc)
+{
+ QString funcName;
+ if (func->isOperatorOverload())
+ funcName = ShibokenGenerator::pythonOperatorFunctionName(func);
+ else
+ funcName = func->name();
+ if (func->ownerClass()) {
+ QString fullClassName = fullPythonClassName(func->ownerClass());
+ if (func->isConstructor()) {
+ funcName = fullClassName;
+ if (forceFunc)
+ funcName.append(u".__init__"_s);
+ }
+ else {
+ funcName.prepend(fullClassName + u'.');
+ }
+ }
+ else {
+ funcName = packageName() + u'.' + func->name();
+ }
+ return funcName;
+}
+
+bool ShibokenGenerator::wrapperDiagnostics()
+{
+ return m_options.wrapperDiagnostics;
+}
+
+QString ShibokenGenerator::protectedEnumSurrogateName(const AbstractMetaEnum &metaEnum)
+{
+ QString result = metaEnum.fullName();
+ result.replace(u'.', u'_');
+ result.replace(u"::"_s, u"_"_s);
+ return result + u"_Surrogate"_s;
+}
+
+QString ShibokenGenerator::cpythonFunctionName(const AbstractMetaFunctionCPtr &func)
+{
+ QString result;
+
+ // PYSIDE-331: For inherited functions, we need to find the same labels.
+ // Therefore we use the implementing class.
+ if (func->implementingClass()) {
+ result = cpythonBaseName(func->implementingClass()->typeEntry());
+ if (func->isConstructor()) {
+ result += u"_Init"_s;
+ } else {
+ result += u"Func_"_s;
+ if (func->isOperatorOverload())
+ result += ShibokenGenerator::pythonOperatorFunctionName(func);
+ else
+ result += func->name();
+ }
+ } else {
+ result = u"Sbk"_s + moduleName() + u"Module_"_s + func->name();
+ }
+
+ return result;
+}
+
+QString ShibokenGenerator::cpythonMethodDefinitionName(const AbstractMetaFunctionCPtr &func)
+{
+ if (!func->ownerClass())
+ return {};
+ return cpythonBaseName(func->ownerClass()->typeEntry()) + u"Method_"_s
+ + func->name();
+}
+
+QString ShibokenGenerator::cpythonGettersSettersDefinitionName(const AbstractMetaClassCPtr &metaClass)
+{
+ return cpythonBaseName(metaClass) + u"_getsetlist"_s;
+}
+
+QString ShibokenGenerator::cpythonSetattroFunctionName(const AbstractMetaClassCPtr &metaClass)
+{
+ return cpythonBaseName(metaClass) + u"_setattro"_s;
+}
+
+
+QString ShibokenGenerator::cpythonGetattroFunctionName(const AbstractMetaClassCPtr &metaClass)
+{
+ return cpythonBaseName(metaClass) + u"_getattro"_s;
+}
+
+QString ShibokenGenerator::cpythonGetterFunctionName(const QString &name,
+ const AbstractMetaClassCPtr &enclosingClass)
+{
+ return cpythonBaseName(enclosingClass) + "_get_"_L1 + name;
+}
+
+QString ShibokenGenerator::cpythonSetterFunctionName(const QString &name,
+ const AbstractMetaClassCPtr &enclosingClass)
+{
+ return cpythonBaseName(enclosingClass) + "_set_"_L1 + name;
+}
+
+QString ShibokenGenerator::cpythonGetterFunctionName(const AbstractMetaField &metaField)
+{
+ return cpythonGetterFunctionName(metaField.name(), metaField.enclosingClass());
+}
+
+QString ShibokenGenerator::cpythonSetterFunctionName(const AbstractMetaField &metaField)
+{
+ return cpythonSetterFunctionName(metaField.name(), metaField.enclosingClass());
+}
+
+QString ShibokenGenerator::cpythonGetterFunctionName(const QPropertySpec &property,
+ const AbstractMetaClassCPtr &metaClass)
+{
+ return cpythonGetterFunctionName(property.name(), metaClass);
+}
+
+QString ShibokenGenerator::cpythonSetterFunctionName(const QPropertySpec &property,
+ const AbstractMetaClassCPtr &metaClass)
+{
+ return cpythonSetterFunctionName(property.name(), metaClass);
+}
+
+static QString cpythonEnumFlagsName(const QString &moduleName,
+ const QString &qualifiedCppName)
+{
+ QString result = u"Sbk"_s + moduleName + u'_' + qualifiedCppName;
+ result.replace(u"::"_s, u"_"_s);
+ return result;
+}
+
+QString ShibokenGenerator::cpythonEnumName(const EnumTypeEntryCPtr &enumEntry)
+{
+ QString p = enumEntry->targetLangPackage();
+ p.replace(u'.', u'_');
+ return cpythonEnumFlagsName(p, enumEntry->qualifiedCppName());
+}
+
+QString ShibokenGenerator::cpythonEnumName(const AbstractMetaEnum &metaEnum)
+{
+ return cpythonEnumName(metaEnum.typeEntry());
+}
+
+QString ShibokenGenerator::cpythonFlagsName(const FlagsTypeEntryCPtr &flagsEntry)
+{
+ QString p = flagsEntry->targetLangPackage();
+ p.replace(u'.', u'_');
+ return cpythonEnumFlagsName(p, flagsEntry->originalName());
+}
+
+QString ShibokenGenerator::cpythonFlagsName(const AbstractMetaEnum *metaEnum)
+{
+ const auto flags = metaEnum->typeEntry()->flags();
+ return flags ? cpythonFlagsName(flags) : QString{};
+}
+
+QString ShibokenGenerator::cpythonSpecialCastFunctionName(const AbstractMetaClassCPtr &metaClass)
+{
+ return cpythonBaseName(metaClass->typeEntry()) + u"SpecialCastFunction"_s;
+}
+
+QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaClassCPtr &metaClass,
+ const QString &argName)
+{
+ return cpythonWrapperCPtr(metaClass->typeEntry(), argName);
+}
+
+QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaType &metaType,
+ const QString &argName)
+{
+ if (!metaType.isWrapperType())
+ return {};
+ return u"reinterpret_cast< ::"_s + metaType.cppSignature()
+ + u" *>(Shiboken::Conversions::cppPointer("_s + cpythonTypeNameExt(metaType)
+ + u", reinterpret_cast<SbkObject *>("_s + argName + u")))"_s;
+}
+
+QString ShibokenGenerator::cpythonWrapperCPtr(const TypeEntryCPtr &type,
+ const QString &argName)
+{
+ if (!type->isWrapperType())
+ return QString();
+ return u"reinterpret_cast< "_s + getFullTypeName(type)
+ + u" *>(Shiboken::Conversions::cppPointer("_s + cpythonTypeNameExt(type)
+ + u", reinterpret_cast<SbkObject *>("_s + argName + u")))"_s;
+}
+
+void ShibokenGenerator::writeToPythonConversion(TextStream & s, const AbstractMetaType &type,
+ const AbstractMetaClassCPtr & /* context */,
+ const QString &argumentName)
+{
+ s << cpythonToPythonConversionFunction(type) << argumentName << ')';
+}
+
+void ShibokenGenerator::writeToCppConversion(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass,
+ const QString &inArgName, const QString &outArgName)
+{
+ s << cpythonToCppConversionFunction(metaClass) << inArgName << ", &" << outArgName << ')';
+}
+
+void ShibokenGenerator::writeToCppConversion(TextStream &s, const AbstractMetaType &type,
+ const QString &inArgName,
+ const QString &outArgName)
+{
+ s << cpythonToCppConversionFunction(type) << inArgName << ", &" << outArgName << ')';
+}
+
+bool ShibokenGenerator::shouldRejectNullPointerArgument(const AbstractMetaFunctionCPtr &func,
+ int argIndex)
+{
+ if (argIndex < 0 || argIndex >= func->arguments().size())
+ return false;
+
+ const AbstractMetaArgument &arg = func->arguments().at(argIndex);
+ if (arg.type().isValueTypeWithCopyConstructorOnly())
+ return true;
+
+ // Argument type is not a pointer, a None rejection should not be
+ // necessary because the type checking would handle that already.
+ if (!arg.type().isPointer())
+ return false;
+ if (arg.isModifiedRemoved())
+ return false;
+ for (const auto &funcMod : func->modifications()) {
+ for (const ArgumentModification &argMod : funcMod.argument_mods()) {
+ if (argMod.index() == argIndex + 1 && argMod.noNullPointers())
+ return true;
+ }
+ }
+ return false;
+}
+
+QString ShibokenGenerator::cpythonBaseName(const AbstractMetaType &type)
+{
+ if (type.isCString())
+ return u"PyString"_s;
+ return cpythonBaseName(type.typeEntry());
+}
+
+QString ShibokenGenerator::cpythonBaseName(const AbstractMetaClassCPtr &metaClass)
+{
+ return cpythonBaseName(metaClass->typeEntry());
+}
+
+QString ShibokenGenerator::containerCpythonBaseName(const ContainerTypeEntryCPtr &ctype)
+{
+ switch (ctype->containerKind()) {
+ case ContainerTypeEntry::SetContainer:
+ return u"PySet"_s;
+ case ContainerTypeEntry::MapContainer:
+ case ContainerTypeEntry::MultiMapContainer:
+ return u"PyDict"_s;
+ case ContainerTypeEntry::ListContainer:
+ case ContainerTypeEntry::PairContainer:
+ case ContainerTypeEntry::SpanContainer:
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+ return cPySequenceT;
+}
+
+QString ShibokenGenerator::cpythonBaseName(const TypeEntryCPtr &type)
+{
+ QString baseName;
+ if (type->isWrapperType() || type->isNamespace()) { // && type->referenceType() == NoReference) {
+ baseName = u"Sbk_"_s + type->name();
+ } else if (type->isPrimitive()) {
+ const auto ptype = basicReferencedTypeEntry(type);
+ baseName = ptype->hasTargetLangApiType()
+ ? ptype->targetLangApiName() : pythonPrimitiveTypeName(ptype->name());
+ } else if (type->isEnum()) {
+ baseName = cpythonEnumName(std::static_pointer_cast<const EnumTypeEntry>(type));
+ } else if (type->isFlags()) {
+ baseName = cpythonFlagsName(std::static_pointer_cast<const FlagsTypeEntry>(type));
+ } else if (type->isContainer()) {
+ const auto ctype = std::static_pointer_cast<const ContainerTypeEntry>(type);
+ baseName = containerCpythonBaseName(ctype);
+ } else {
+ baseName = cPyObjectT;
+ }
+ return baseName.replace(u"::"_s, u"_"_s);
+}
+
+QString ShibokenGenerator::cpythonTypeName(const AbstractMetaClassCPtr &metaClass)
+{
+ return cpythonTypeName(metaClass->typeEntry());
+}
+
+QString ShibokenGenerator::cpythonTypeName(const TypeEntryCPtr &type)
+{
+ return cpythonBaseName(type) + u"_TypeF()"_s;
+}
+
+QString ShibokenGenerator::converterObject(const AbstractMetaType &type)
+{
+ if (type.isCString())
+ return u"Shiboken::Conversions::PrimitiveTypeConverter<const char *>()"_s;
+ if (type.isVoidPointer())
+ return u"Shiboken::Conversions::PrimitiveTypeConverter<void *>()"_s;
+ const AbstractMetaTypeList nestedArrayTypes = type.nestedArrayTypes();
+ if (!nestedArrayTypes.isEmpty() && nestedArrayTypes.constLast().isCppPrimitive()) {
+ return "Shiboken::Conversions::ArrayTypeConverter<"_L1
+ + nestedArrayTypes.constLast().minimalSignature()
+ + u">("_s + QString::number(nestedArrayTypes.size())
+ + u')';
+ }
+
+ auto typeEntry = type.typeEntry();
+ if (typeEntry->isContainer() || typeEntry->isSmartPointer()) {
+ return convertersVariableName(typeEntry->targetLangPackage())
+ + u'[' + getTypeIndexVariableName(type) + u']';
+ }
+ return converterObject(typeEntry);
+}
+
+QString ShibokenGenerator::converterObject(const TypeEntryCPtr &type)
+{
+ if (isExtendedCppPrimitive(type))
+ return QString::fromLatin1("Shiboken::Conversions::PrimitiveTypeConverter<%1>()")
+ .arg(type->qualifiedCppName());
+ if (type->isWrapperType())
+ return QString::fromLatin1("PepType_SOTP(reinterpret_cast<PyTypeObject *>(%1))->converter")
+ .arg(cpythonTypeNameExt(type));
+ if (type->isEnum() || type->isFlags())
+ return QString::fromLatin1("PepType_SETP(reinterpret_cast<SbkEnumType *>(%1))->converter")
+ .arg(cpythonTypeNameExt(type));
+
+ if (type->isArray()) {
+ qDebug() << "Warning: no idea how to handle the Qt5 type " << type->qualifiedCppName();
+ return {};
+ }
+
+ /* the typedef'd primitive types case */
+ auto pte = std::dynamic_pointer_cast<const PrimitiveTypeEntry>(type);
+ if (!pte) {
+ qDebug() << "Warning: the Qt5 primitive type is unknown" << type->qualifiedCppName();
+ return {};
+ }
+ pte = basicReferencedTypeEntry(pte);
+ if (pte->isPrimitive() && !isCppPrimitive(pte) && !pte->customConversion()) {
+ return u"Shiboken::Conversions::PrimitiveTypeConverter<"_s
+ + pte->qualifiedCppName() + u">()"_s;
+ }
+
+ return convertersVariableName(type->targetLangPackage())
+ + u'[' + getTypeIndexVariableName(type) + u']';
+}
+
+QString ShibokenGenerator::cpythonTypeNameExtSet(const TypeEntryCPtr &type)
+{
+ return cppApiVariableName(type->targetLangPackage()) + u'['
+ + getTypeIndexVariableName(type) + "].type"_L1;
+}
+
+QString ShibokenGenerator::cpythonTypeNameExtSet(const AbstractMetaType &type)
+{
+ return cppApiVariableName(type.typeEntry()->targetLangPackage()) + u'['
+ + getTypeIndexVariableName(type) + "].type"_L1;
+}
+
+QString ShibokenGenerator::cpythonTypeNameExt(const TypeEntryCPtr &type)
+{
+ return "Shiboken::Module::get("_L1 + cppApiVariableName(type->targetLangPackage())
+ + u'[' + getTypeIndexVariableName(type) + "])"_L1;
+}
+
+QString ShibokenGenerator::cpythonTypeNameExt(const AbstractMetaType &type)
+{
+ return u"Shiboken::Module::get("_s + cppApiVariableName(type.typeEntry()->targetLangPackage())
+ + u'[' + getTypeIndexVariableName(type) + "])"_L1;
+}
+
+QString ShibokenGenerator::fixedCppTypeName(const TargetToNativeConversion &toNative)
+{
+ if (toNative.sourceType())
+ return fixedCppTypeName(toNative.sourceType());
+ return toNative.sourceTypeName();
+}
+QString ShibokenGenerator::fixedCppTypeName(const AbstractMetaType &type)
+{
+ return fixedCppTypeName(type.typeEntry(), type.cppSignature());
+}
+
+static QString _fixedCppTypeName(QString typeName)
+{
+ typeName.remove(u' ');
+ typeName.replace(u'.', u'_');
+ typeName.replace(u',', u'_');
+ typeName.replace(u'<', u'_');
+ typeName.replace(u'>', u'_');
+ typeName.replace(u"::"_s, u"_"_s);
+ typeName.replace(u"*"_s, u"PTR"_s);
+ typeName.replace(u"&"_s, u"REF"_s);
+ return typeName;
+}
+QString ShibokenGenerator::fixedCppTypeName(const TypeEntryCPtr &type, QString typeName)
+{
+ if (typeName.isEmpty())
+ typeName = type->qualifiedCppName();
+ if (!type->generateCode()) {
+ typeName.prepend(u'_');
+ typeName.prepend(type->targetLangPackage());
+ }
+ return _fixedCppTypeName(typeName);
+}
+
+QString ShibokenGenerator::pythonPrimitiveTypeName(const QString &cppTypeName)
+{
+ const auto &mapping = primitiveTypesCorrespondences();
+ const auto it = mapping.constFind(cppTypeName);
+ if (it == mapping.cend())
+ throw Exception(u"Primitive type not found: "_s + cppTypeName);
+ return it.value();
+}
+
+QString ShibokenGenerator::pythonOperatorFunctionName(const AbstractMetaFunctionCPtr &func)
+{
+ QString op = Generator::pythonOperatorFunctionName(func->originalName());
+ if (op.isEmpty()) {
+ qCWarning(lcShiboken).noquote().nospace() << msgUnknownOperator(func.get());
+ return "__UNKNOWN_OPERATOR__"_L1;
+ }
+ if (func->arguments().isEmpty()) {
+ if (op == u"__sub__")
+ op = u"__neg__"_s;
+ else if (op == u"__add__")
+ op = u"__pos__"_s;
+ } else if (func->isStatic() && func->arguments().size() == 2) {
+ // If a operator overload function has 2 arguments and
+ // is static we assume that it is a reverse operator.
+ op = op.insert(2, u'r');
+ }
+ return op;
+}
+
+bool ShibokenGenerator::isNumber(const QString &cpythonApiName)
+{
+ return cpythonApiName == pyFloatT || cpythonApiName == pyLongT
+ || cpythonApiName == pyBoolT;
+}
+
+static std::optional<TypeSystem::CPythonType>
+ targetLangApiCPythonType(const PrimitiveTypeEntryCPtr &t)
+{
+ if (!t->hasTargetLangApiType())
+ return {};
+ const auto cte = t->targetLangApiType();
+ if (cte->type() != TypeEntry::PythonType)
+ return {};
+ return std::static_pointer_cast<const PythonTypeEntry>(cte)->cPythonType();
+}
+
+bool ShibokenGenerator::isNumber(const TypeEntryCPtr &type)
+{
+ if (!type->isPrimitive())
+ return false;
+ const auto pte = basicReferencedTypeEntry(type);
+ const auto cPythonTypeOpt = targetLangApiCPythonType(pte);
+ // FIXME PYSIDE-1660: Return false here after making primitive types built-in?
+ if (!cPythonTypeOpt.has_value()) {
+ const auto &mapping = primitiveTypesCorrespondences();
+ const auto it = mapping.constFind(pte->name());
+ return it != mapping.cend() && isNumber(it.value());
+ }
+ const auto cPythonType = cPythonTypeOpt.value();
+ return cPythonType == TypeSystem::CPythonType::Bool
+ || cPythonType == TypeSystem::CPythonType::Float
+ || cPythonType == TypeSystem::CPythonType::Integer;
+}
+
+bool ShibokenGenerator::isNumber(const AbstractMetaType &type)
+{
+ return isNumber(type.typeEntry());
+}
+
+bool ShibokenGenerator::isPyInt(const TypeEntryCPtr &type)
+{
+ if (!type->isPrimitive())
+ return false;
+ const auto pte = basicReferencedTypeEntry(type);
+ const auto cPythonTypeOpt = targetLangApiCPythonType(pte);
+ // FIXME PYSIDE-1660: Return false here after making primitive types built-in?
+ if (!cPythonTypeOpt.has_value()) {
+ const auto &mapping = primitiveTypesCorrespondences();
+ const auto it = mapping.constFind(pte->name());
+ return it != mapping.cend() && it.value() == pyLongT;
+ }
+ return cPythonTypeOpt.value() == TypeSystem::CPythonType::Integer;
+}
+
+bool ShibokenGenerator::isPyInt(const AbstractMetaType &type)
+{
+ return isPyInt(type.typeEntry());
+}
+
+bool ShibokenGenerator::isNullPtr(const QString &value)
+{
+ return value == u"0" || value == u"nullptr"
+ || value == u"NULLPTR" || value == u"{}";
+}
+
+QString ShibokenGenerator::cpythonCheckFunction(AbstractMetaType metaType)
+{
+ const auto typeEntry = metaType.typeEntry();
+ if (typeEntry->isCustom()) {
+ const auto cte = std::static_pointer_cast<const CustomTypeEntry>(typeEntry);
+ if (cte->hasCheckFunction())
+ return cte->checkFunction();
+ throw Exception(msgUnknownCheckFunction(typeEntry));
+ }
+
+ if (metaType.isExtendedCppPrimitive()) {
+ if (metaType.isCString())
+ return u"Shiboken::String::check"_s;
+ if (metaType.isVoidPointer())
+ return u"true"_s;
+ return cpythonCheckFunction(typeEntry);
+ }
+
+ if (typeEntry->isContainer()) {
+ QString typeCheck = u"Shiboken::Conversions::"_s;
+ ContainerTypeEntry::ContainerKind type =
+ std::static_pointer_cast<const ContainerTypeEntry>(typeEntry)->containerKind();
+ if (type == ContainerTypeEntry::ListContainer
+ || type == ContainerTypeEntry::SetContainer) {
+ const QString containerType = type == ContainerTypeEntry::SetContainer
+ ? u"Iterable"_s : u"Sequence"_s;
+ const AbstractMetaType &type = metaType.instantiations().constFirst();
+ if (type.isPointerToWrapperType()) {
+ typeCheck += u"check"_s + containerType + u"Types("_s
+ + cpythonTypeNameExt(type) + u", "_s;
+ } else if (type.isWrapperType()) {
+ typeCheck += u"convertible"_s + containerType
+ + u"Types("_s + cpythonTypeNameExt(type) + u", "_s;
+ } else {
+ typeCheck += u"convertible"_s + containerType
+ + u"Types("_s + converterObject(type) + u", "_s;
+ }
+ } else if (type == ContainerTypeEntry::MapContainer
+ || type == ContainerTypeEntry::MultiMapContainer
+ || type == ContainerTypeEntry::PairContainer) {
+
+ QString pyType;
+ if (type == ContainerTypeEntry::PairContainer)
+ pyType = u"Pair"_s;
+ else if (type == ContainerTypeEntry::MultiMapContainer)
+ pyType = u"MultiDict"_s;
+ else
+ pyType = u"Dict"_s;
+
+ const AbstractMetaType &firstType = metaType.instantiations().constFirst();
+ const AbstractMetaType &secondType = metaType.instantiations().constLast();
+ if (firstType.isPointerToWrapperType() && secondType.isPointerToWrapperType()) {
+ QTextStream(&typeCheck) << "check" << pyType << "Types("
+ << cpythonTypeNameExt(firstType) << ", "
+ << cpythonTypeNameExt(secondType) << ", ";
+ } else {
+ QTextStream(&typeCheck) << "convertible" << pyType << "Types("
+ << converterObject(firstType) << ", "
+ << (firstType.isPointerToWrapperType() ? "true" : "false")
+ << ", " << converterObject(secondType) << ", "
+ << (secondType.isPointerToWrapperType() ? "true" :"false")
+ << ", ";
+ }
+ }
+ return typeCheck;
+ }
+ return cpythonCheckFunction(typeEntry);
+}
+
+QString ShibokenGenerator::cpythonCheckFunction(TypeEntryCPtr type)
+{
+ if (type->isCustom()) {
+ const auto cte = std::static_pointer_cast<const CustomTypeEntry>(type);
+ if (cte->hasCheckFunction())
+ return cte->checkFunction();
+ throw Exception(msgUnknownCheckFunction(type));
+ }
+
+ if (type->isEnum() || type->isFlags() || type->isWrapperType())
+ return u"SbkObject_TypeCheck("_s + cpythonTypeNameExt(type) + u", "_s;
+
+ if (type->isPrimitive())
+ type = basicReferencedTypeEntry(type);
+
+ if (auto tla = type->targetLangApiType()) {
+ if (tla->hasCheckFunction())
+ return tla->checkFunction();
+ }
+
+ if (isExtendedCppPrimitive(type))
+ return pythonPrimitiveTypeName(type->name()) + u"_Check"_s;
+
+ return cpythonIsConvertibleFunction(type);
+}
+
+QString ShibokenGenerator::cpythonIsConvertibleFunction(const TypeEntryCPtr &type)
+{
+ if (type->isWrapperType()) {
+ QString result = u"Shiboken::Conversions::"_s;
+ bool isValue = false;
+ if (type->isValue()) {
+ const auto cte = std::static_pointer_cast<const ComplexTypeEntry>(type);
+ isValue = !cte->isValueTypeWithCopyConstructorOnly();
+ }
+ result += isValue ? u"isPythonToCppValueConvertible"_s
+ : u"isPythonToCppPointerConvertible"_s;
+ result += u"("_s + cpythonTypeNameExt(type) + u", "_s;
+ return result;
+ }
+ return QString::fromLatin1("Shiboken::Conversions::isPythonToCppConvertible(%1, ")
+ .arg(converterObject(type));
+}
+
+QString ShibokenGenerator::cpythonIsConvertibleFunction(const AbstractMetaType &metaType)
+{
+ const auto typeEntry = metaType.typeEntry();
+ if (typeEntry->isCustom()) {
+ const auto cte = std::static_pointer_cast<const CustomTypeEntry>(typeEntry);
+ if (cte->hasCheckFunction())
+ return cte->checkFunction();
+ throw Exception(msgUnknownCheckFunction(typeEntry));
+ }
+
+ QString result = u"Shiboken::Conversions::"_s;
+ if (metaType.generateOpaqueContainer()) {
+ result += u"pythonToCppReferenceConversion("_s
+ + converterObject(metaType) + u", "_s;
+ return result;
+ }
+ if (metaType.isWrapperType()) {
+ if (metaType.isPointer() || metaType.isValueTypeWithCopyConstructorOnly()) {
+ result += u"pythonToCppPointerConversion"_s;
+ } else if (metaType.referenceType() == LValueReference
+ || (metaType.referenceType() == RValueReference && typeEntry->isObject())) {
+ result += u"pythonToCppReferenceConversion"_s;
+ } else {
+ result += u"pythonToCppValueConversion"_s;
+ }
+ result += u'(' + cpythonTypeNameExt(metaType) + u", "_s;
+ return result;
+ }
+ result += u"pythonToCppConversion("_s + converterObject(metaType);
+ // Write out array sizes if known
+ const AbstractMetaTypeList nestedArrayTypes = metaType.nestedArrayTypes();
+ if (!nestedArrayTypes.isEmpty() && nestedArrayTypes.constLast().isCppPrimitive()) {
+ const int dim1 = metaType.arrayElementCount();
+ const int dim2 = nestedArrayTypes.constFirst().isArray()
+ ? nestedArrayTypes.constFirst().arrayElementCount() : -1;
+ result += u", "_s + QString::number(dim1)
+ + u", "_s + QString::number(dim2);
+ }
+ result += u", "_s;
+ return result;
+}
+
+QString ShibokenGenerator::cpythonIsConvertibleFunction(const AbstractMetaArgument &metaArg)
+{
+ return cpythonIsConvertibleFunction(metaArg.type());
+}
+
+QString ShibokenGenerator::cpythonToCppConversionFunction(const AbstractMetaClassCPtr &metaClass)
+{
+ return u"Shiboken::Conversions::pythonToCppPointer("_s
+ + cpythonTypeNameExt(metaClass->typeEntry()) + u", "_s;
+}
+
+QString ShibokenGenerator::cpythonToCppConversionFunction(const AbstractMetaType &type)
+{
+ if (type.isWrapperType()) {
+ return u"Shiboken::Conversions::pythonToCpp"_s
+ + (type.isPointer() ? u"Pointer"_s : u"Copy"_s)
+ + u'(' + cpythonTypeNameExt(type) + u", "_s;
+ }
+ return "Shiboken::Conversions::pythonToCppCopy("_L1
+ + converterObject(type) + ", "_L1;
+}
+
+QString ShibokenGenerator::cpythonToPythonConversionFunction(const AbstractMetaType &type)
+{
+ if (type.isWrapperType()) {
+ QString conversion;
+ if (type.referenceType() == LValueReference
+ && !(type.isValue() && type.isConstant()) && !type.isPointer()) {
+ conversion = u"reference"_s;
+ } else if (type.isValue() || type.isSmartPointer()) {
+ conversion = u"copy"_s;
+ } else {
+ conversion = u"pointer"_s;
+ }
+ QString result = u"Shiboken::Conversions::"_s + conversion
+ + u"ToPython("_s
+ + cpythonTypeNameExt(type) + u", "_s;
+ if (conversion != u"pointer")
+ result += u'&';
+ return result;
+ }
+
+ const auto indirections = type.indirections() - 1;
+ return u"Shiboken::Conversions::copyToPython("_s + converterObject(type)
+ + u", "_s + AbstractMetaType::dereferencePrefix(indirections);
+}
+
+QString ShibokenGenerator::cpythonToPythonConversionFunction(const AbstractMetaClassCPtr &metaClass)
+{
+ return cpythonToPythonConversionFunction(metaClass->typeEntry());
+}
+
+QString ShibokenGenerator::cpythonToPythonConversionFunction(const TypeEntryCPtr &type)
+{
+ if (type->isWrapperType()) {
+ const QString conversion = type->isValue() ? u"copy"_s : u"pointer"_s;
+ QString result = u"Shiboken::Conversions::"_s + conversion
+ + u"ToPython("_s + cpythonTypeNameExt(type)
+ + u", "_s;
+ if (conversion != u"pointer")
+ result += u'&';
+ return result;
+ }
+
+ return u"Shiboken::Conversions::copyToPython("_s
+ + converterObject(type) + u", &"_s;
+}
+
+QString ShibokenGenerator::argumentString(const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaArgument &argument,
+ Options options) const
+{
+ auto type = options.testFlag(OriginalTypeDescription)
+ ? argument.type() : argument.modifiedType();
+
+
+ QString arg = translateType(type, func->implementingClass(), options);
+
+ if (argument.isTypeModified())
+ arg.replace(u'$', u'.'); // Haehh?
+
+ // "int a", "int a[]"
+ const auto arrayPos = arg.indexOf(u'[');
+ if (arrayPos != -1)
+ arg.insert(arrayPos, u' ' + argument.name());
+ else
+ arg.append(u' ' + argument.name());
+
+ if ((options & Generator::SkipDefaultValues) != Generator::SkipDefaultValues &&
+ !argument.originalDefaultValueExpression().isEmpty())
+ {
+ QString default_value = argument.originalDefaultValueExpression();
+ if (default_value == u"NULL")
+ default_value = NULL_PTR;
+
+ //WORKAROUND: fix this please
+ if (default_value.startsWith(u"new "))
+ default_value.remove(0, 4);
+
+ arg += u" = "_s + default_value;
+ }
+
+ return arg;
+}
+
+void ShibokenGenerator::writeArgument(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaArgument &argument,
+ Options options) const
+{
+ s << argumentString(func, argument, options);
+}
+
+void ShibokenGenerator::writeFunctionArguments(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ Options options) const
+{
+ int argUsed = 0;
+ if (func->isUserAddedPythonOverride()) {
+ s << "Shiboken::GilState &gil, PyObject *" << PYTHON_OVERRIDE_VAR;
+ argUsed += 2;
+ }
+ for (const auto &arg : func->arguments()) {
+ if (options.testFlag(Generator::SkipRemovedArguments) && arg.isModifiedRemoved())
+ continue;
+
+ if (argUsed != 0)
+ s << ", ";
+ writeArgument(s, func, arg, options);
+ argUsed++;
+ }
+}
+
+GeneratorContext ShibokenGenerator::contextForClass(const AbstractMetaClassCPtr &c) const
+{
+ GeneratorContext result = Generator::contextForClass(c);
+ if (shouldGenerateCppWrapper(c)) {
+ result.m_type = GeneratorContext::WrappedClass;
+ result.m_wrappername = wrapperName(c);
+ }
+ return result;
+}
+
+QString ShibokenGenerator::functionReturnType(const AbstractMetaFunctionCPtr &func, Options options) const
+{
+ if (func->isTypeModified() && !options.testFlag(OriginalTypeDescription))
+ return func->modifiedTypeName();
+ return translateType(func->type(), func->implementingClass(), options);
+}
+
+QString ShibokenGenerator::functionSignature(const AbstractMetaFunctionCPtr &func,
+ const QString &prepend,
+ const QString &append,
+ Options options,
+ int /* argCount */) const
+{
+ StringStream s(TextStream::Language::Cpp);
+ // The actual function
+ if (!options.testFlag(Option::SkipDefaultValues) && func->isStatic()) // Declaration
+ s << "static ";
+ if (func->isEmptyFunction() || func->needsReturnType())
+ s << functionReturnType(func, options) << ' ';
+ else
+ options |= Generator::SkipReturnType;
+
+ // name
+ QString name(func->originalName());
+ if (func->isConstructor())
+ name = wrapperName(func->ownerClass());
+
+ s << prepend << name << append << '(';
+ writeFunctionArguments(s, func, options);
+ s << ')';
+
+ if (func->isConstant())
+ s << " const";
+
+ if (func->exceptionSpecification() == ExceptionSpecification::NoExcept)
+ s << " noexcept";
+
+ return s;
+}
+
+void ShibokenGenerator::writeArgumentNames(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ Options options)
+{
+ const AbstractMetaArgumentList arguments = func->arguments();
+ int argCount = 0;
+ for (const auto &argument : arguments) {
+ const int index = argument.argumentIndex() + 1;
+ if (options.testFlag(Generator::SkipRemovedArguments) && argument.isModifiedRemoved())
+ continue;
+ const auto &type = argument.type();
+ if (argCount > 0)
+ s << ", ";
+ const bool isVirtualCall = options.testFlag(Option::VirtualCall);
+ const bool useStdMove = isVirtualCall && type.isUniquePointer() && type.passByValue();
+ s << (useStdMove ? stdMove(argument.name()) : argument.name());
+
+ if (!isVirtualCall
+ && (func->hasConversionRule(TypeSystem::NativeCode, index)
+ || func->hasConversionRule(TypeSystem::TargetLangCode, index))
+ && !func->isConstructor()) {
+ s << CONV_RULE_OUT_VAR_SUFFIX;
+ }
+
+ argCount++;
+ }
+}
+
+void ShibokenGenerator::writeFunctionCall(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ Options options)
+{
+ s << (func->isConstructor() ? func->ownerClass()->qualifiedCppName() : func->originalName())
+ << '(';
+ writeArgumentNames(s, func, options);
+ s << ')';
+}
+
+ShibokenGenerator::ExtendedConverterData ShibokenGenerator::getExtendedConverters() const
+{
+ ExtendedConverterData extConvs;
+ for (const auto &metaClass : api().classes()) {
+ // Use only the classes for the current module.
+ if (!shouldGenerate(metaClass->typeEntry()))
+ continue;
+ const auto &overloads = metaClass->operatorOverloads(OperatorQueryOption::ConversionOp);
+ for (const auto &convOp : overloads) {
+ // Get only the conversion operators that return a type from another module,
+ // that are value-types and were not removed in the type system.
+ const auto convType = convOp->type().typeEntry();
+ if (convType->generateCode() || !convType->isValue()
+ || convOp->isModifiedRemoved())
+ continue;
+ extConvs[convType].append(convOp->ownerClass());
+ }
+ }
+ return extConvs;
+}
+
+QList<CustomConversionPtr> ShibokenGenerator::getPrimitiveCustomConversions()
+{
+ QList<CustomConversionPtr> conversions;
+ const auto &primitiveTypeList = primitiveTypes();
+ for (const auto &type : primitiveTypeList) {
+ if (type->shouldGenerate() && isUserPrimitive(type) && type->hasCustomConversion())
+ conversions << type->customConversion();
+ }
+ return conversions;
+}
+
+static QString getArgumentsFromMethodCall(const QString &str)
+{
+ // It would be way nicer to be able to use a Perl like
+ // regular expression that accepts temporary variables
+ // to count the parenthesis.
+ // For more information check this:
+ // http://perl.plover.com/yak/regex/samples/slide083.html
+ static QLatin1String funcCall("%CPPSELF.%FUNCTION_NAME");
+ auto pos = str.indexOf(funcCall);
+ if (pos == -1)
+ return QString();
+ pos = pos + funcCall.size();
+ while (str.at(pos) == u' ' || str.at(pos) == u'\t')
+ ++pos;
+ if (str.at(pos) == u'(')
+ ++pos;
+ int begin = pos;
+ int counter = 1;
+ while (counter != 0) {
+ if (str.at(pos) == u'(')
+ ++counter;
+ else if (str.at(pos) == u')')
+ --counter;
+ ++pos;
+ }
+ return str.mid(begin, pos-begin-1);
+}
+
+QString ShibokenGenerator::getCodeSnippets(const CodeSnipList &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language)
+{
+ QString code;
+ for (const CodeSnip &snip : codeSnips) {
+ if ((position != TypeSystem::CodeSnipPositionAny && snip.position != position) || !(snip.language & language))
+ continue;
+ code.append(snip.code());
+ }
+ return code;
+}
+
+void ShibokenGenerator::processClassCodeSnip(QString &code, const GeneratorContext &context) const
+{
+ auto metaClass = context.metaClass();
+ // Replace template variable by the Python Type object
+ // for the class context in which the variable is used.
+ code.replace(u"%PYTHONTYPEOBJECT"_s,
+ u"(*"_s + cpythonTypeName(metaClass) + u')');
+ const QString className = context.effectiveClassName();
+ code.replace(u"%TYPE"_s, className);
+ code.replace(u"%CPPTYPE"_s, metaClass->name());
+
+ processCodeSnip(code, context.effectiveClassName());
+}
+
+void ShibokenGenerator::processCodeSnip(QString &code) const
+{
+ // replace "toPython" converters
+ replaceConvertToPythonTypeSystemVariable(code);
+
+ // replace "toCpp" converters
+ replaceConvertToCppTypeSystemVariable(code);
+
+ // replace "isConvertible" check
+ replaceIsConvertibleToCppTypeSystemVariable(code);
+
+ // replace "checkType" check
+ replaceTypeCheckTypeSystemVariable(code);
+}
+
+void ShibokenGenerator::processCodeSnip(QString &code, const QString &context) const
+{
+ try {
+ processCodeSnip(code);
+ } catch (const std::exception &e) {
+ throw Exception(msgSnippetError(context, e.what()));
+ }
+}
+
+ShibokenGenerator::ArgumentVarReplacementList
+ ShibokenGenerator::getArgumentReplacement(const AbstractMetaFunctionCPtr &func,
+ bool usePyArgs, TypeSystem::Language language,
+ const AbstractMetaArgument *lastArg)
+{
+ ArgumentVarReplacementList argReplacements;
+ TypeSystem::Language convLang = (language == TypeSystem::TargetLangCode)
+ ? TypeSystem::NativeCode : TypeSystem::TargetLangCode;
+ int removed = 0;
+ for (qsizetype i = 0; i < func->arguments().size(); ++i) {
+ const AbstractMetaArgument &arg = func->arguments().at(i);
+ QString argValue;
+ if (language == TypeSystem::TargetLangCode) {
+ const bool hasConversionRule = func->hasConversionRule(convLang, i + 1);
+ const bool argRemoved = arg.isModifiedRemoved();
+ if (argRemoved)
+ ++removed;
+ if (argRemoved && hasConversionRule)
+ argValue = arg.name() + CONV_RULE_OUT_VAR_SUFFIX;
+ else if (argRemoved || (lastArg && arg.argumentIndex() > lastArg->argumentIndex()))
+ argValue = CPP_ARG_REMOVED(i);
+ if (!argRemoved && argValue.isEmpty()) {
+ int argPos = i - removed;
+ AbstractMetaType type = arg.modifiedType();
+ if (type.typeEntry()->isCustom()) {
+ argValue = usePyArgs
+ ? pythonArgsAt(argPos) : PYTHON_ARG;
+ } else {
+ argValue = hasConversionRule
+ ? arg.name() + CONV_RULE_OUT_VAR_SUFFIX
+ : CPP_ARG_N(argPos);
+ const auto generatorArg = GeneratorArgument::fromMetaType(type);
+ AbstractMetaType::applyDereference(&argValue, generatorArg.indirections);
+ }
+ }
+ } else {
+ argValue = arg.name();
+ }
+ if (!argValue.isEmpty())
+ argReplacements << ArgumentVarReplacementPair(arg, argValue);
+
+ }
+ return argReplacements;
+}
+
+void ShibokenGenerator::writeClassCodeSnips(TextStream &s,
+ const CodeSnipList &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language,
+ const GeneratorContext &context) const
+{
+ QString code = getCodeSnippets(codeSnips, position, language);
+ if (code.isEmpty())
+ return;
+ processClassCodeSnip(code, context);
+ s << "// Begin code injection\n" << code << "// End of code injection\n\n";
+}
+
+void ShibokenGenerator::writeCodeSnips(TextStream &s,
+ const CodeSnipList &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language) const
+{
+ QString code = getCodeSnippets(codeSnips, position, language);
+ if (code.isEmpty())
+ return;
+ processCodeSnip(code);
+ s << "// Begin code injection\n" << code << "// End of code injection\n\n";
+}
+
+static void replacePyArg0(TypeSystem::Language language, QString *code)
+{
+ static constexpr auto pyArg0 = "%PYARG_0"_L1;
+
+ if (!code->contains(pyArg0))
+ return;
+ if (language != TypeSystem::NativeCode) {
+ code->replace(pyArg0, PYTHON_RETURN_VAR);
+ return;
+ }
+
+ // pyResult is an AutoDecRef in overridden methods of wrapper classes which
+ // has a cast operator for PyObject *. This may however not work in all
+ // situations (fex _PyVarObject_CAST(op) defined as ((PyVarObject*)(op))).
+ // Append ".object()" unless it is followed by a '.' indicating explicit
+ // AutoDecRef member invocation.
+ static const QString pyObject = PYTHON_RETURN_VAR + u".object()"_s;
+ qsizetype pos{};
+ while ( (pos = code->indexOf(pyArg0)) >= 0) {
+ const auto next = pos + pyArg0.size();
+ const bool memberInvocation = next < code->size() && code->at(next) == u'.';
+ code->replace(pos, pyArg0.size(),
+ memberInvocation ? PYTHON_RETURN_VAR : pyObject);
+ }
+}
+
+void ShibokenGenerator::writeCodeSnips(TextStream &s,
+ const CodeSnipList &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language,
+ const AbstractMetaFunctionCPtr &func,
+ bool usePyArgs,
+ const AbstractMetaArgument *lastArg) const
+{
+ QString code = getCodeSnippets(codeSnips, position, language);
+ if (code.isEmpty())
+ return;
+
+ // Replace %PYARG_# variables.
+ replacePyArg0(language, &code);
+
+ static const QRegularExpression pyArgsRegex("%PYARG_(\\d+)"_L1);
+ Q_ASSERT(pyArgsRegex.isValid());
+ if (language == TypeSystem::TargetLangCode) {
+ if (usePyArgs) {
+ code.replace(pyArgsRegex, PYTHON_ARGS + u"[\\1-1]"_s);
+ } else {
+ static const QRegularExpression pyArgsRegexCheck("%PYARG_([2-9]+)"_L1);
+ Q_ASSERT(pyArgsRegexCheck.isValid());
+ const QRegularExpressionMatch match = pyArgsRegexCheck.match(code);
+ if (match.hasMatch()) {
+ qCWarning(lcShiboken).noquote().nospace()
+ << msgWrongIndex("%PYARG", match.captured(1), func.get());
+ return;
+ }
+ code.replace(u"%PYARG_1"_s, PYTHON_ARG);
+ }
+ } else {
+ // Replaces the simplest case of attribution to a
+ // Python argument on the binding virtual method.
+ static const QRegularExpression pyArgsAttributionRegex("%PYARG_(\\d+)\\s*=[^=]\\s*([^;]+)"_L1);
+ Q_ASSERT(pyArgsAttributionRegex.isValid());
+ code.replace(pyArgsAttributionRegex, u"PyTuple_SET_ITEM("_s
+ + PYTHON_ARGS + u".object(), \\1-1, \\2)"_s);
+ code.replace(pyArgsRegex, u"PyTuple_GET_ITEM("_s
+ + PYTHON_ARGS + u".object(), \\1-1)"_s);
+ }
+
+ // Replace %ARG#_TYPE variables.
+ const AbstractMetaArgumentList &arguments = func->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+ QString argTypeVar = u"%ARG"_s + QString::number(arg.argumentIndex() + 1)
+ + u"_TYPE"_s;
+ QString argTypeVal = arg.type().cppSignature();
+ code.replace(argTypeVar, argTypeVal);
+ }
+
+ static const QRegularExpression cppArgTypeRegexCheck("%ARG(\\d+)_TYPE"_L1);
+ Q_ASSERT(cppArgTypeRegexCheck.isValid());
+ QRegularExpressionMatchIterator rit = cppArgTypeRegexCheck.globalMatch(code);
+ while (rit.hasNext()) {
+ QRegularExpressionMatch match = rit.next();
+ qCWarning(lcShiboken).noquote().nospace()
+ << msgWrongIndex("%ARG#_TYPE", match.captured(1), func.get());
+ }
+
+ // Replace template variable for return variable name.
+ if (func->isConstructor()) {
+ code.replace(u"%0."_s, u"cptr->"_s);
+ code.replace(u"%0"_s, u"cptr"_s);
+ } else if (!func->isVoid()) {
+ QString returnValueOp = func->type().isPointerToWrapperType()
+ ? u"%1->"_s : u"%1."_s;
+ if (func->type().isWrapperType())
+ code.replace(u"%0."_s, returnValueOp.arg(CPP_RETURN_VAR));
+ code.replace(u"%0"_s, CPP_RETURN_VAR);
+ }
+
+ // Replace template variable for self Python object.
+ QString pySelf = language == TypeSystem::NativeCode
+ ? u"pySelf"_s : u"self"_s;
+ code.replace(u"%PYSELF"_s, pySelf);
+
+ // Replace template variable for a pointer to C++ of this object.
+ if (func->implementingClass()) {
+ QString replacement = func->isStatic() ? u"%1::"_s : u"%1->"_s;
+ QString cppSelf;
+ if (func->isStatic())
+ cppSelf = func->ownerClass()->qualifiedCppName();
+ else if (language == TypeSystem::NativeCode)
+ cppSelf = u"this"_s;
+ else
+ cppSelf = CPP_SELF_VAR;
+
+ // On comparison operator CPP_SELF_VAR is always a reference.
+ if (func->isComparisonOperator())
+ replacement = u"%1."_s;
+
+ if (func->isVirtual() && !func->isAbstract() && (!avoidProtectedHack() || !func->isProtected())) {
+ QString methodCallArgs = getArgumentsFromMethodCall(code);
+ if (!methodCallArgs.isEmpty()) {
+ const QString pattern = u"%CPPSELF.%FUNCTION_NAME("_s + methodCallArgs + u')';
+ QString replacement = u"(Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>("_s
+ + pySelf + u")) ? "_s;
+ if (func->name() == u"metaObject") {
+ QString wrapperClassName = wrapperName(func->ownerClass());
+ QString cppSelfVar = avoidProtectedHack()
+ ? u"%CPPSELF"_s
+ : u"reinterpret_cast<"_s + wrapperClassName + u" *>(%CPPSELF)"_s;
+ replacement += cppSelfVar + u"->::"_s + wrapperClassName
+ + u"::%FUNCTION_NAME("_s + methodCallArgs
+ + u") : %CPPSELF.%FUNCTION_NAME("_s + methodCallArgs + u"))"_s;
+ } else {
+ replacement += u"%CPPSELF->::%TYPE::%FUNCTION_NAME("_s + methodCallArgs
+ + u") : %CPPSELF.%FUNCTION_NAME("_s + methodCallArgs + u"))"_s;
+ }
+ code.replace(pattern, replacement);
+ }
+ }
+
+ code.replace(u"%CPPSELF."_s, replacement.arg(cppSelf));
+ code.replace(u"%CPPSELF"_s, cppSelf);
+
+ if (code.indexOf(u"%BEGIN_ALLOW_THREADS") > -1) {
+ if (code.count(u"%BEGIN_ALLOW_THREADS"_s) == code.count(u"%END_ALLOW_THREADS"_s)) {
+ code.replace(u"%BEGIN_ALLOW_THREADS"_s, BEGIN_ALLOW_THREADS);
+ code.replace(u"%END_ALLOW_THREADS"_s, END_ALLOW_THREADS);
+ } else {
+ qCWarning(lcShiboken) << "%BEGIN_ALLOW_THREADS and %END_ALLOW_THREADS mismatch";
+ }
+ }
+
+ // replace template variable for the Python Type object for the
+ // class implementing the method in which the code snip is written
+ if (func->isStatic()) {
+ code.replace(u"%PYTHONTYPEOBJECT"_s,
+ u"(*"_s + cpythonTypeName(func->implementingClass()) + u')');
+ } else {
+ code.replace(u"%PYTHONTYPEOBJECT."_s, pySelf + u"->ob_type->"_s);
+ code.replace(u"%PYTHONTYPEOBJECT"_s, pySelf + u"->ob_type"_s);
+ }
+ }
+
+ // Replaces template %ARGUMENT_NAMES and %# variables by argument variables and values.
+ // Replaces template variables %# for individual arguments.
+ const ArgumentVarReplacementList &argReplacements = getArgumentReplacement(func, usePyArgs, language, lastArg);
+
+ QStringList args;
+ for (const ArgumentVarReplacementPair &pair : argReplacements) {
+ if (pair.second.startsWith(CPP_ARG_REMOVED_PREFIX))
+ continue;
+ args << pair.second;
+ }
+ code.replace(u"%ARGUMENT_NAMES"_s, args.join(u", "_s));
+
+ for (const ArgumentVarReplacementPair &pair : argReplacements) {
+ const AbstractMetaArgument &arg = pair.first;
+ int idx = arg.argumentIndex() + 1;
+ AbstractMetaType type = arg.modifiedType();
+ if (type.isWrapperType()) {
+ QString replacement = pair.second;
+ const auto generatorArg = GeneratorArgument::fromMetaType(type);
+ if (generatorArg.indirections > 0)
+ AbstractMetaType::stripDereference(&replacement);
+ if (type.referenceType() == LValueReference || type.isPointer())
+ code.replace(u'%' + QString::number(idx) + u'.', replacement + u"->"_s);
+ }
+ code.replace(CodeSnipAbstract::placeHolderRegex(idx), pair.second);
+ }
+
+ if (language == TypeSystem::NativeCode) {
+ // Replaces template %PYTHON_ARGUMENTS variable with a pointer to the Python tuple
+ // containing the converted virtual method arguments received from C++ to be passed
+ // to the Python override.
+ code.replace(u"%PYTHON_ARGUMENTS"_s, PYTHON_ARGS);
+
+ // replace variable %PYTHON_METHOD_OVERRIDE for a pointer to the Python method
+ // override for the C++ virtual method in which this piece of code was inserted
+ code.replace(u"%PYTHON_METHOD_OVERRIDE"_s, PYTHON_OVERRIDE_VAR);
+ }
+
+ if (avoidProtectedHack()) {
+ // If the function being processed was added by the user via type system,
+ // Shiboken needs to find out if there are other overloads for the same method
+ // name and if any of them is of the protected visibility. This is used to replace
+ // calls to %FUNCTION_NAME on user written custom code for calls to the protected
+ // dispatcher.
+ bool isProtected = func->isProtected();
+ auto owner = func->ownerClass();
+ if (!isProtected && func->isUserAdded() && owner != nullptr) {
+ const auto &funcs = getFunctionGroups(owner).value(func->name());
+ isProtected = std::any_of(funcs.cbegin(), funcs.cend(),
+ [](const AbstractMetaFunctionCPtr &f) {
+ return f->isProtected();
+ });
+ }
+
+ if (isProtected) {
+ code.replace(u"%TYPE::%FUNCTION_NAME"_s,
+ wrapperName(func->ownerClass()) + "::"_L1
+ + func->originalName() + "_protected"_L1);
+ code.replace(u"%FUNCTION_NAME"_s,
+ func->originalName() + u"_protected"_s);
+ }
+ }
+
+ if (func->isConstructor() && shouldGenerateCppWrapper(func->ownerClass()))
+ code.replace(u"%TYPE"_s, wrapperName(func->ownerClass()));
+
+ if (func->ownerClass())
+ code.replace(u"%CPPTYPE"_s, func->ownerClass()->name());
+
+ replaceTemplateVariables(code, func);
+
+ processCodeSnip(code, func->classQualifiedSignature());
+ s << "// Begin code injection\n" << code << "// End of code injection\n\n";
+}
+
+// Returns true if the string is an expression,
+// and false if it is a variable.
+static bool isVariable(const QString &code)
+{
+ static const QRegularExpression expr("^\\s*\\*?\\s*[A-Za-z_][A-Za-z_0-9.]*\\s*(?:\\[[^\\[]+\\])*$"_L1);
+ Q_ASSERT(expr.isValid());
+ return expr.match(code.trimmed()).hasMatch();
+}
+
+// A miniature normalizer that puts a type string into a format
+// suitable for comparison with AbstractMetaType::cppSignature()
+// result.
+static QString miniNormalizer(const QString &varType)
+{
+ QString normalized = varType.trimmed();
+ if (normalized.isEmpty())
+ return normalized;
+ if (normalized.startsWith(u"::"))
+ normalized.remove(0, 2);
+ QString suffix;
+ while (normalized.endsWith(u'*') || normalized.endsWith(u'&')) {
+ suffix.prepend(normalized.at(normalized.size() - 1));
+ normalized.chop(1);
+ normalized = normalized.trimmed();
+ }
+ const QString result = normalized + u' ' + suffix;
+ return result.trimmed();
+}
+// The position must indicate the first character after the opening '('.
+// ATTENTION: do not modify this function to trim any resulting string!
+// This must be done elsewhere.
+static QString getConverterTypeSystemVariableArgument(const QString &code, int pos)
+{
+ QString arg;
+ int parenthesisDepth = 0;
+ int count = 0;
+ while (pos + count < code.size()) {
+ char c = code.at(pos+count).toLatin1(); // toAscii is gone
+ if (c == '(') {
+ ++parenthesisDepth;
+ } else if (c == ')') {
+ if (parenthesisDepth == 0) {
+ arg = code.mid(pos, count).trimmed();
+ break;
+ }
+ --parenthesisDepth;
+ }
+ ++count;
+ }
+ if (parenthesisDepth != 0)
+ throw Exception("Unbalanced parenthesis on type system converter variable call.");
+ return arg;
+}
+
+const QHash<int, QString> &ShibokenGenerator::typeSystemConvName()
+{
+ static const QHash<int, QString> result = {
+ {TypeSystemCheckFunction, u"checkType"_s},
+ {TypeSystemIsConvertibleFunction, u"isConvertible"_s},
+ {TypeSystemToCppFunction, u"toCpp"_s},
+ {TypeSystemToPythonFunction, u"toPython"_s}
+ };
+ return result;
+}
+
+using StringPair = std::pair<QString, QString>;
+
+void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVariable converterVariable,
+ QString &code) const
+{
+ QList<StringPair> replacements;
+ QRegularExpressionMatchIterator rit = typeSystemConvRegExps()[converterVariable].globalMatch(code);
+ while (rit.hasNext()) {
+ const QRegularExpressionMatch match = rit.next();
+ const QStringList list = match.capturedTexts();
+ QString conversionString = list.constFirst();
+ const QString &conversionTypeName = list.constLast();
+ QString message;
+ const auto conversionTypeO = AbstractMetaType::fromString(conversionTypeName, &message);
+ if (!conversionTypeO.has_value()) {
+ throw Exception(msgCannotFindType(conversionTypeName,
+ typeSystemConvName().value(converterVariable),
+ message));
+ }
+ const auto conversionType = conversionTypeO.value();
+ QString conversion;
+ switch (converterVariable) {
+ case TypeSystemToCppFunction: {
+ StringStream c(TextStream::Language::Cpp);
+ int end = match.capturedStart();
+ int start = end;
+ while (start > 0 && code.at(start) != u'\n')
+ --start;
+ while (code.at(start).isSpace())
+ ++start;
+ QString varType = code.mid(start, end - start);
+ conversionString = varType + list.constFirst();
+ varType = miniNormalizer(varType);
+ QString varName = list.at(1).trimmed();
+ if (!varType.isEmpty()) {
+ c << getFullTypeName(conversionType) << ' ' << varName
+ << minimalConstructorExpression(api(), conversionType) << ";\n";
+ }
+ c << cpythonToCppConversionFunction(conversionType);
+ QString prefix;
+ if (!AbstractMetaType::stripDereference(&varName))
+ prefix = u'&';
+ QString arg = getConverterTypeSystemVariableArgument(code, match.capturedEnd());
+ conversionString += arg;
+ c << arg << ", " << prefix << '(' << varName << ')';
+ conversion = c.toString();
+ break;
+ }
+ case TypeSystemCheckFunction:
+ conversion = cpythonCheckFunction(conversionType);
+ if (conversionType.typeEntry()->isPrimitive()
+ && (conversionType.typeEntry()->name() == cPyObjectT
+ || !conversion.endsWith(u' '))) {
+ conversion += u'(';
+ break;
+ }
+ Q_FALLTHROUGH();
+ case TypeSystemIsConvertibleFunction:
+ if (conversion.isEmpty())
+ conversion = cpythonIsConvertibleFunction(conversionType);
+ Q_FALLTHROUGH();
+ case TypeSystemToPythonFunction:
+ if (conversion.isEmpty())
+ conversion = cpythonToPythonConversionFunction(conversionType);
+ Q_FALLTHROUGH();
+ default: {
+ QString arg = getConverterTypeSystemVariableArgument(code, match.capturedEnd());
+ conversionString += arg;
+ if (converterVariable == TypeSystemToPythonFunction && !isVariable(arg)) {
+ QString m;
+ QTextStream(&m) << "Only variables are acceptable as argument to %%CONVERTTOPYTHON type system variable on code snippet: '"
+ << code << '\'';
+ throw Exception(m);
+ }
+ if (conversion.contains(u"%in")) {
+ conversion.prepend(u'(');
+ conversion.replace(u"%in"_s, arg);
+ } else {
+ conversion += arg;
+ }
+ }
+ }
+ replacements.append(std::make_pair(conversionString, conversion));
+ }
+ for (const StringPair &rep : std::as_const(replacements))
+ code.replace(rep.first, rep.second);
+}
+
+bool ShibokenGenerator::injectedCodeCallsCppFunction(const GeneratorContext &context,
+ const AbstractMetaFunctionCPtr &func)
+{
+ if (func->injectedCodeContains(u"%FUNCTION_NAME("))
+ return true;
+ QString funcCall = func->originalName() + u'(';
+ if (func->isConstructor())
+ funcCall.prepend(u"new "_s);
+ if (func->injectedCodeContains(funcCall))
+ return true;
+ if (!func->isConstructor())
+ return false;
+ if (func->injectedCodeContains(u"new %TYPE("))
+ return true;
+ const auto owner = func->ownerClass();
+ if (!owner->isPolymorphic())
+ return false;
+ const QString wrappedCtorCall = u"new "_s + context.effectiveClassName() + u'(';
+ return func->injectedCodeContains(wrappedCtorCall);
+}
+
+bool ShibokenGenerator::useOverrideCaching(const AbstractMetaClassCPtr &metaClass)
+{
+ return metaClass->isPolymorphic();
+}
+
+ShibokenGenerator::AttroCheck ShibokenGenerator::checkAttroFunctionNeeds(
+ const AbstractMetaClassCPtr &metaClass)
+{
+ AttroCheck result;
+ if (metaClass->typeEntry()->isSmartPointer()) {
+ result |= AttroCheckFlag::GetattroSmartPointer | AttroCheckFlag::SetattroSmartPointer;
+ } else {
+ if (getGeneratorClassInfo(metaClass).needsGetattroFunction)
+ result |= AttroCheckFlag::GetattroOverloads;
+ if (metaClass->queryFirstFunction(metaClass->functions(),
+ FunctionQueryOption::GetAttroFunction)) {
+ result |= AttroCheckFlag::GetattroUser;
+ }
+ if (usePySideExtensions() && metaClass->qualifiedCppName() == qObjectT)
+ result |= AttroCheckFlag::SetattroQObject;
+ if (useOverrideCaching(metaClass))
+ result |= AttroCheckFlag::SetattroMethodOverride;
+ if (metaClass->queryFirstFunction(metaClass->functions(),
+ FunctionQueryOption::SetAttroFunction)) {
+ result |= AttroCheckFlag::SetattroUser;
+ }
+ // PYSIDE-1255: If setattro is generated for a class inheriting
+ // QObject, the property code needs to be generated, too.
+ if ((result & AttroCheckFlag::SetattroMask) != 0
+ && !result.testFlag(AttroCheckFlag::SetattroQObject)
+ && isQObject(metaClass)) {
+ result |= AttroCheckFlag::SetattroQObject;
+ }
+ }
+ return result;
+}
+
+bool ShibokenGenerator::classNeedsGetattroFunctionImpl(const AbstractMetaClassCPtr &metaClass)
+{
+ if (!metaClass)
+ return false;
+ if (metaClass->typeEntry()->isSmartPointer())
+ return true;
+ const auto &functionGroup = getFunctionGroups(metaClass);
+ for (auto it = functionGroup.cbegin(), end = functionGroup.cend(); it != end; ++it) {
+ AbstractMetaFunctionCList overloads;
+ for (const auto &func : std::as_const(it.value())) {
+ if (func->isAssignmentOperator() || func->isConversionOperator()
+ || func->isModifiedRemoved()
+ || func->isPrivate() || func->ownerClass() != func->implementingClass()
+ || func->isConstructor() || func->isOperatorOverload())
+ continue;
+ overloads.append(func);
+ }
+ if (overloads.isEmpty())
+ continue;
+ if (OverloadData::hasStaticAndInstanceFunctions(overloads))
+ return true;
+ }
+ return false;
+}
+
+AbstractMetaFunctionCList
+ ShibokenGenerator::getMethodsWithBothStaticAndNonStaticMethods(const AbstractMetaClassCPtr &metaClass)
+{
+ AbstractMetaFunctionCList methods;
+ if (metaClass) {
+ const auto &functionGroups = getFunctionGroups(metaClass);
+ for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) {
+ AbstractMetaFunctionCList overloads;
+ for (const auto &func : std::as_const(it.value())) {
+ if (func->isAssignmentOperator() || func->isConversionOperator()
+ || func->isModifiedRemoved()
+ || func->isPrivate() || func->ownerClass() != func->implementingClass()
+ || func->isConstructor() || func->isOperatorOverload())
+ continue;
+ overloads.append(func);
+ }
+ if (overloads.isEmpty())
+ continue;
+ if (OverloadData::hasStaticAndInstanceFunctions(overloads))
+ methods.append(overloads.constFirst());
+ }
+ }
+ return methods;
+}
+
+AbstractMetaClassCPtr
+ ShibokenGenerator::getMultipleInheritingClass(const AbstractMetaClassCPtr &metaClass)
+{
+ if (!metaClass || metaClass->baseClassNames().isEmpty())
+ return nullptr;
+ if (metaClass->baseClassNames().size() > 1)
+ return metaClass;
+ return getMultipleInheritingClass(metaClass->baseClass());
+}
+
+QString ShibokenGenerator::getModuleHeaderFileBaseName(const QString &moduleName)
+{
+ return moduleCppPrefix(moduleName).toLower() + "_python"_L1;
+}
+
+QString ShibokenGenerator::getModuleHeaderFileName(const QString &moduleName)
+{
+ return getModuleHeaderFileBaseName(moduleName) + ".h"_L1;
+}
+
+QString ShibokenGenerator::getPrivateModuleHeaderFileName(const QString &moduleName)
+{
+ return getModuleHeaderFileBaseName(moduleName) + "_p.h"_L1;
+}
+
+IncludeGroupList ShibokenGenerator::classIncludes(const AbstractMetaClassCPtr &metaClass) const
+{
+ IncludeGroupList result;
+ const auto typeEntry = metaClass->typeEntry();
+ //Extra includes
+ result.append(IncludeGroup{u"Extra includes"_s,
+ typeEntry->extraIncludes()});
+
+ result.append({u"Enum includes"_s, {}});
+ for (const auto &cppEnum : metaClass->enums())
+ result.back().includes.append(cppEnum.typeEntry()->extraIncludes());
+
+ result.append({u"Argument includes"_s, typeEntry->argumentIncludes()});
+ const auto implicitConvs = implicitConversions(typeEntry);
+ for (const auto &f : implicitConvs) {
+ if (f->isConversionOperator()) {
+ const auto source = f->ownerClass();
+ Q_ASSERT(source);
+ result.back().append(source->typeEntry()->include());
+ }
+ }
+ return result;
+}
+
+/*
+static void dumpFunction(AbstractMetaFunctionList lst)
+{
+ qDebug() << "DUMP FUNCTIONS: ";
+ for (AbstractMetaFunction *func : std::as_const(lst))
+ qDebug() << "*" << func->ownerClass()->name()
+ << func->signature()
+ << "Private: " << func->isPrivate()
+ << "Empty: " << func->isEmptyFunction()
+ << "Static:" << func->isStatic()
+ << "Signal:" << func->isSignal()
+ << "ClassImplements: " << (func->ownerClass() != func->implementingClass())
+ << "is operator:" << func->isOperatorOverload()
+ << "is global:" << func->isInGlobalScope();
+}
+*/
+
+static bool isGroupable(const AbstractMetaFunctionCPtr &func)
+{
+ switch (func->functionType()) {
+ case AbstractMetaFunction::DestructorFunction:
+ case AbstractMetaFunction::SignalFunction:
+ case AbstractMetaFunction::GetAttroFunction:
+ case AbstractMetaFunction::SetAttroFunction:
+ case AbstractMetaFunction::ArrowOperator: // weird operator overloads
+ case AbstractMetaFunction::SubscriptOperator:
+ return false;
+ default:
+ break;
+ }
+ if (func->isModifiedRemoved() && !func->isAbstract())
+ return false;
+ return true;
+}
+
+static void insertIntoFunctionGroups(const AbstractMetaFunctionCList &lst,
+ ShibokenGenerator::FunctionGroups *results)
+{
+ for (const auto &func : lst) {
+ if (isGroupable(func))
+ (*results)[func->name()].append(func);
+ }
+}
+
+ShibokenGenerator::FunctionGroups ShibokenGenerator::getGlobalFunctionGroups() const
+{
+ FunctionGroups results;
+ insertIntoFunctionGroups(api().globalFunctions(), &results);
+ for (const auto &nsp : invisibleTopNamespaces())
+ insertIntoFunctionGroups(nsp->functions(), &results);
+ return results;
+}
+
+const GeneratorClassInfoCacheEntry &
+ ShibokenGenerator::getGeneratorClassInfo(const AbstractMetaClassCPtr &scope)
+{
+ auto cache = generatorClassInfoCache();
+ auto it = cache->find(scope);
+ if (it == cache->end()) {
+ it = cache->insert(scope, {});
+ auto &entry = it.value();
+ entry.functionGroups = getFunctionGroupsImpl(scope);
+ entry.needsGetattroFunction = classNeedsGetattroFunctionImpl(scope);
+ entry.numberProtocolOperators = getNumberProtocolOperators(scope);
+ entry.boolCastFunctionO = getBoolCast(scope);
+ }
+ return it.value();
+}
+
+ShibokenGenerator::FunctionGroups
+ ShibokenGenerator::getFunctionGroups(const AbstractMetaClassCPtr &scope)
+{
+ Q_ASSERT(scope);
+ return getGeneratorClassInfo(scope).functionGroups;
+}
+
+QList<AbstractMetaFunctionCList>
+ ShibokenGenerator::numberProtocolOperators(const AbstractMetaClassCPtr &scope)
+{
+ Q_ASSERT(scope);
+ return getGeneratorClassInfo(scope).numberProtocolOperators;
+}
+
+BoolCastFunctionOptional ShibokenGenerator::boolCast(const AbstractMetaClassCPtr &scope)
+{
+ Q_ASSERT(scope);
+ return getGeneratorClassInfo(scope).boolCastFunctionO;
+}
+
+// Use non-const overloads only, for example, "foo()" and "foo()const"
+// the second is removed.
+static void removeConstOverloads(AbstractMetaFunctionCList *overloads)
+{
+ for (qsizetype i = overloads->size() - 1; i >= 0; --i) {
+ const auto &f = overloads->at(i);
+ if (f->isConstant()) {
+ for (qsizetype c = 0, size = overloads->size(); c < size; ++c) {
+ if (f->isConstOverloadOf(overloads->at(c).get())) {
+ overloads->removeAt(i);
+ break;
+ }
+ }
+ }
+ }
+}
+
+ShibokenGenerator::FunctionGroups
+ ShibokenGenerator::getFunctionGroupsImpl(const AbstractMetaClassCPtr &scope)
+{
+ AbstractMetaFunctionCList lst = scope->functions();
+ scope->getFunctionsFromInvisibleNamespacesToBeGenerated(&lst);
+
+ FunctionGroups results;
+ for (const auto &func : lst) {
+ if (isGroupable(func)
+ && func->ownerClass() == func->implementingClass()
+ && func->generateBinding()) {
+ auto it = results.find(func->name());
+ if (it == results.end()) {
+ it = results.insert(func->name(), AbstractMetaFunctionCList(1, func));
+ } else {
+ // If there are virtuals methods in the mix (PYSIDE-570,
+ // QFileSystemModel::index(QString,int) and
+ // QFileSystemModel::index(int,int,QModelIndex)) override, make sure
+ // the overriding method of the most-derived class is seen first
+ // and inserted into the "seenSignatures" set.
+ if (func->isVirtual())
+ it.value().prepend(func);
+ else
+ it.value().append(func);
+ }
+ getInheritedOverloads(scope, &it.value());
+ removeConstOverloads(&it.value());
+ }
+ }
+ return results;
+}
+
+static bool removeNumberProtocolOperator(const AbstractMetaFunctionCPtr &f)
+{
+ return !f->generateBinding()
+ || (f->ownerClass() != f->implementingClass() && !f->isAbstract());
+}
+
+QList<AbstractMetaFunctionCList>
+ ShibokenGenerator::getNumberProtocolOperators(const AbstractMetaClassCPtr &metaClass)
+{
+ QList<AbstractMetaFunctionCList> result;
+ if (metaClass->isNamespace())
+ return result;
+ result = filterGroupedOperatorFunctions(
+ metaClass,
+ OperatorQueryOption::ArithmeticOp
+ | OperatorQueryOption::IncDecrementOp
+ | OperatorQueryOption::LogicalOp
+ | OperatorQueryOption::BitwiseOp
+ | OperatorQueryOption::ConversionOp);
+
+ for (auto i = result.size() - 1; i >= 0; --i) {
+ AbstractMetaFunctionCList &l = result[i];
+ auto rend = std::remove_if(l.begin(), l.end(), removeNumberProtocolOperator);
+ l.erase(rend, l.end());
+ if (l.isEmpty())
+ result.removeAt(i);
+ }
+
+ return result;
+}
+
+BoolCastFunctionOptional
+ShibokenGenerator::getBoolCast(const AbstractMetaClassCPtr &metaClass)
+{
+ if (metaClass->isNamespace())
+ return std::nullopt;
+
+ const auto te = metaClass->typeEntry();
+ if (te->isSmartPointer()) {
+ auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(te);
+
+ auto valueCheckMethod = ste->valueCheckMethod();
+ if (!valueCheckMethod.isEmpty()) {
+ const auto func = metaClass->findFunction(valueCheckMethod);
+ if (!func)
+ throw Exception(msgMethodNotFound(metaClass, valueCheckMethod));
+ return BoolCastFunction{func, false};
+ }
+
+ auto nullCheckMethod = ste->nullCheckMethod();
+ if (!nullCheckMethod.isEmpty()) {
+ const auto func = metaClass->findFunction(nullCheckMethod);
+ if (!func)
+ throw Exception(msgMethodNotFound(metaClass, nullCheckMethod));
+ return BoolCastFunction{func, true};
+ }
+ }
+
+ auto mode = te->operatorBoolMode();
+ if (useOperatorBoolAsNbBool()
+ ? mode != TypeSystem::BoolCast::Disabled : mode == TypeSystem::BoolCast::Enabled) {
+ const auto func = metaClass->findOperatorBool();
+ if (func)
+ return BoolCastFunction{func, false};
+ }
+
+ mode = te->isNullMode();
+ if (useIsNullAsNbBool()
+ ? mode != TypeSystem::BoolCast::Disabled : mode == TypeSystem::BoolCast::Enabled) {
+ const auto func = metaClass->findQtIsNullMethod();
+ if (func)
+ return BoolCastFunction{func, true};
+ }
+ return std::nullopt;
+}
+
+static bool isInplaceAdd(const AbstractMetaFunctionCPtr &func)
+{
+ return func->name() == u"operator+=";
+}
+
+static bool isIncrementOperator(const AbstractMetaFunctionCPtr &func)
+{
+ return func->functionType() == AbstractMetaFunction::IncrementOperator;
+}
+
+static bool isDecrementOperator(const AbstractMetaFunctionCPtr &func)
+{
+ return func->functionType() == AbstractMetaFunction::DecrementOperator;
+}
+
+// Filter predicate for operator functions
+static bool skipOperatorFunc(const AbstractMetaFunctionCPtr &func)
+{
+ if (func->isModifiedRemoved() || func->usesRValueReferences())
+ return true;
+ const auto &name = func->name();
+ return name == u"operator[]" || name == u"operator->" || name == u"operator!"
+ || name == u"operator/="; // __idiv__ is not needed in Python3
+}
+
+QList<AbstractMetaFunctionCList>
+ShibokenGenerator::filterGroupedOperatorFunctions(const AbstractMetaClassCPtr &metaClass,
+ OperatorQueryOptions query)
+{
+ // ( func_name, num_args ) => func_list
+ QMap<std::pair<QString, int>, AbstractMetaFunctionCList> results;
+ auto funcs = metaClass->operatorOverloads(query);
+ auto end = std::remove_if(funcs.begin(), funcs.end(), skipOperatorFunc);
+ funcs.erase(end, funcs.end());
+ // If we have operator+=, we remove the operator++/-- which would
+ // otherwise be used for emulating __iadd__, __isub__.
+ if (std::any_of(funcs.cbegin(), funcs.cend(), isInplaceAdd)) {
+ end = std::remove_if(funcs.begin(), funcs.end(),
+ [] (const AbstractMetaFunctionCPtr &func) {
+ return func->isIncDecrementOperator();
+ });
+ funcs.erase(end, funcs.end());
+ } else {
+ // If both prefix/postfix ++/-- are present, remove one
+ if (std::count_if(funcs.begin(), funcs.end(), isIncrementOperator) > 1)
+ funcs.erase(std::find_if(funcs.begin(), funcs.end(), isIncrementOperator));
+ if (std::count_if(funcs.begin(), funcs.end(), isDecrementOperator) > 1)
+ funcs.erase(std::find_if(funcs.begin(), funcs.end(), isDecrementOperator));
+ }
+ for (const auto &func : funcs) {
+ int args;
+ if (func->isComparisonOperator()) {
+ args = -1;
+ } else {
+ args = func->arguments().size();
+ }
+ auto op = std::make_pair(func->name(), args);
+ results[op].append(func);
+ }
+ QList<AbstractMetaFunctionCList> result;
+ result.reserve(results.size());
+ for (auto it = results.cbegin(), end = results.cend(); it != end; ++it)
+ result.append(it.value());
+ return result;
+}
+
+static bool hidesBaseClassFunctions(const AbstractMetaFunctionCPtr &f)
+{
+ auto attributes = f->cppAttributes();
+ return !attributes.testFlag(FunctionAttribute::Override)
+ && !attributes.testFlag(FunctionAttribute::Final);
+}
+
+void ShibokenGenerator::getInheritedOverloads(const AbstractMetaClassCPtr &scope,
+ AbstractMetaFunctionCList *overloads)
+{
+ if (overloads->isEmpty() || scope->isNamespace() || scope->baseClasses().isEmpty())
+ return;
+
+ // PYSIDE-331: look also into base classes. Check for any non-overriding
+ // function hiding the base class functions.
+ const bool hideBaseClassFunctions =
+ std::any_of(overloads->cbegin(), overloads->cend(), hidesBaseClassFunctions);
+
+ const QString &functionName = overloads->constFirst()->name();
+ const bool hasUsingDeclarations = scope->hasUsingMemberFor(functionName);
+ if (hideBaseClassFunctions && !hasUsingDeclarations)
+ return; // No base function is visible
+
+ // Collect base candidates by name and signature
+ bool staticEncountered = false;
+ QSet<QString> seenSignatures;
+ for (const auto &func : *overloads) {
+ seenSignatures.insert(func->minimalSignature());
+ staticEncountered |= func->isStatic();
+ }
+
+ AbstractMetaFunctionCList baseCandidates;
+
+ auto basePredicate = [&functionName, &seenSignatures, &baseCandidates]
+ (const AbstractMetaClassCPtr &b) {
+ for (const auto &f : b->functions()) {
+ if (f->generateBinding() && f->name() == functionName) {
+ const QString signature = f->minimalSignature();
+ if (!seenSignatures.contains(signature)) {
+ seenSignatures.insert(signature);
+ baseCandidates.append(f);
+ }
+ }
+ }
+ return false; // Keep going
+ };
+
+ for (const auto &baseClass : scope->baseClasses())
+ recurseClassHierarchy(baseClass, basePredicate);
+
+ // Remove the ones that are not made visible with using declarations
+ if (hideBaseClassFunctions && hasUsingDeclarations) {
+ const auto pred = [scope](const AbstractMetaFunctionCPtr &f) {
+ return !scope->isUsingMember(f->ownerClass(), f->name(), f->access());
+ };
+ auto end = std::remove_if(baseCandidates.begin(), baseCandidates.end(), pred);
+ baseCandidates.erase(end, baseCandidates.end());
+ }
+
+ // PYSIDE-886: If the method does not have any static overloads declared
+ // in the class in question, remove all inherited static methods as setting
+ // METH_STATIC in that case can cause crashes for the instance methods.
+ // Manifested as crash when calling QPlainTextEdit::find() (clash with
+ // static QWidget::find(WId)).
+ if (!staticEncountered) {
+ auto end = std::remove_if(baseCandidates.begin(), baseCandidates.end(),
+ [](const AbstractMetaFunctionCPtr &f) { return f->isStatic(); });
+ baseCandidates.erase(end, baseCandidates.end());
+ }
+
+ for (const auto &baseCandidate : baseCandidates) {
+ AbstractMetaFunction *newFunc = baseCandidate->copy();
+ newFunc->setImplementingClass(scope);
+ overloads->append(AbstractMetaFunctionCPtr(newFunc));
+ }
+}
+
+QList<OptionDescription> ShibokenGenerator::options()
+{
+ return {
+ {DISABLE_VERBOSE_ERROR_MESSAGES,
+ u"Disable verbose error messages. Turn the python code hard to debug\n"
+ "but safe few kB on the generated bindings."_s},
+ {PARENT_CTOR_HEURISTIC,
+ u"Enable heuristics to detect parent relationship on constructors."_s},
+ {RETURN_VALUE_HEURISTIC,
+ u"Enable heuristics to detect parent relationship on return values\n"
+ "(USE WITH CAUTION!)"_s},
+ {USE_ISNULL_AS_NB_BOOL,
+ u"If a class have an isNull() const method, it will be used to compute\n"
+ "the value of boolean casts"_s},
+ {LEAN_HEADERS,
+ u"Forward declare classes in module headers"_s},
+ {USE_OPERATOR_BOOL_AS_NB_BOOL,
+ u"If a class has an operator bool, it will be used to compute\n"
+ "the value of boolean casts"_s},
+ {NO_IMPLICIT_CONVERSIONS,
+ u"Do not generate implicit_conversions for function arguments."_s},
+ {WRAPPER_DIAGNOSTICS,
+ u"Generate diagnostic code around wrappers"_s}
+ };
+}
+
+class ShibokenGeneratorOptionsParser : public OptionsParser
+{
+public:
+ explicit ShibokenGeneratorOptionsParser(ShibokenGeneratorOptions *o) : m_options(o) {}
+
+ bool handleBoolOption(const QString & key, OptionSource source) override;
+
+private:
+ ShibokenGeneratorOptions *m_options;
+};
+
+bool ShibokenGeneratorOptionsParser::handleBoolOption(const QString &key, OptionSource source)
+{
+ if (source == OptionSource::CommandLineSingleDash)
+ return false;
+ if (key == PARENT_CTOR_HEURISTIC)
+ return (m_options->useCtorHeuristic = true);
+ if (key == RETURN_VALUE_HEURISTIC)
+ return (m_options->userReturnValueHeuristic = true);
+ if (key == DISABLE_VERBOSE_ERROR_MESSAGES)
+ return (m_options->verboseErrorMessagesDisabled = true);
+ if (key == USE_ISNULL_AS_NB_BOOL || key == USE_ISNULL_AS_NB_NONZERO) {
+ return (m_options->useIsNullAsNbBool = true);
+ }
+ if (key == LEAN_HEADERS)
+ return (m_options->leanHeaders= true);
+ if (key == USE_OPERATOR_BOOL_AS_NB_BOOL || key == USE_OPERATOR_BOOL_AS_NB_NONZERO) {
+ return (m_options->useOperatorBoolAsNbBool = true);
+ }
+ if (key == NO_IMPLICIT_CONVERSIONS) {
+ m_options->generateImplicitConversions = false;
+ return true;
+ }
+ if (key == WRAPPER_DIAGNOSTICS)
+ return (m_options->wrapperDiagnostics = true);
+ return false;
+}
+
+std::shared_ptr<OptionsParser> ShibokenGenerator::createOptionsParser()
+{
+ return std::make_shared<ShibokenGeneratorOptionsParser>(&m_options);
+}
+
+bool ShibokenGenerator::doSetup()
+{
+ return true;
+}
+
+bool ShibokenGenerator::useCtorHeuristic()
+{
+ return m_options.useCtorHeuristic;
+}
+
+bool ShibokenGenerator::useReturnValueHeuristic()
+{
+ return m_options.userReturnValueHeuristic;
+}
+
+bool ShibokenGenerator::useIsNullAsNbBool()
+{
+ return m_options.useIsNullAsNbBool;
+}
+
+bool ShibokenGenerator::leanHeaders()
+{
+ return m_options.leanHeaders;
+}
+
+bool ShibokenGenerator::useOperatorBoolAsNbBool()
+{
+ return m_options.useOperatorBoolAsNbBool;
+}
+
+bool ShibokenGenerator::generateImplicitConversions()
+{
+ return m_options.generateImplicitConversions;
+}
+
+QString ShibokenGenerator::moduleCppPrefix(const QString &moduleName)
+ {
+ QString result = moduleName.isEmpty() ? packageName() : moduleName;
+ result.replace(u'.', u'_');
+ return result;
+}
+
+QString ShibokenGenerator::cppApiVariableNameOld(const QString &moduleName)
+{
+ return "Sbk"_L1 + moduleCppPrefix(moduleName) + "Types"_L1;
+}
+
+QString ShibokenGenerator::cppApiVariableName(const QString &moduleName)
+{
+ return "Sbk"_L1 + moduleCppPrefix(moduleName) + "TypeStructs"_L1;
+}
+
+QString ShibokenGenerator::pythonModuleObjectName(const QString &moduleName)
+{
+ return "Sbk"_L1 + moduleCppPrefix(moduleName) + "ModuleObject"_L1;
+}
+
+QString ShibokenGenerator::convertersVariableName(const QString &moduleName)
+{
+ QString result = cppApiVariableNameOld(moduleName);
+ result.chop(1);
+ result.append(u"Converters"_s);
+ return result;
+}
+
+static QString processInstantiationsVariableName(const AbstractMetaType &type)
+{
+ QString res = u'_' + _fixedCppTypeName(type.typeEntry()->qualifiedCppName());
+ for (const auto &instantiation : type.instantiations()) {
+ res += instantiation.isContainer()
+ ? processInstantiationsVariableName(instantiation)
+ : u'_' + _fixedCppTypeName(instantiation.cppSignature());
+ }
+ return res;
+}
+
+static void appendIndexSuffix(QString *s)
+{
+ if (!s->endsWith(u'_'))
+ s->append(u'_');
+ s->append("IDX"_L1);
+}
+
+QString
+ ShibokenGenerator::getTypeAlternateTemplateIndexVariableName(const AbstractMetaClassCPtr &metaClass)
+{
+ const auto templateBaseClass = metaClass->templateBaseClass();
+ Q_ASSERT(templateBaseClass);
+ QString result = u"SBK_"_s
+ + _fixedCppTypeName(templateBaseClass->typeEntry()->qualifiedCppName());
+ for (const auto &instantiation : metaClass->templateBaseClassInstantiations())
+ result += processInstantiationsVariableName(instantiation);
+ appendIndexSuffix(&result);
+ return result;
+}
+
+QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaClassCPtr &metaClass)
+{
+ return getTypeIndexVariableName(metaClass->typeEntry());
+}
+QString ShibokenGenerator::getTypeIndexVariableName(TypeEntryCPtr type)
+{
+ if (isCppPrimitive(type))
+ type = basicReferencedTypeEntry(type);
+ QString result = u"SBK_"_s;
+ // Disambiguate namespaces per module to allow for extending them.
+ if (type->isNamespace()) {
+ QString package = type->targetLangPackage();
+ const int dot = package.lastIndexOf(u'.');
+ result += QStringView{package}.right(package.size() - (dot + 1));
+ }
+ result += _fixedCppTypeName(type->qualifiedCppName());
+ appendIndexSuffix(&result);
+ return result;
+}
+QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaType &type)
+{
+ QString result = u"SBK"_s;
+ if (type.typeEntry()->isContainer())
+ result += u'_' + moduleName();
+ result += processInstantiationsVariableName(type);
+ appendIndexSuffix(&result);
+ return result;
+}
+
+void collectfromTypeEntry(TypeEntryCPtr entry, QStringList &typeNames)
+{
+ if (entry->shouldGenerate()) {
+ typeNames[entry->sbkIndex()] = entry->qualifiedTargetLangName();
+ if (entry->isEnum()) {
+ auto ete = std::static_pointer_cast<const EnumTypeEntry>(entry);
+ if (ete->flags()) {
+ auto entry = ete->flags();
+ typeNames[entry->sbkIndex()] = entry->qualifiedTargetLangName();
+ }
+ }
+ }
+}
+
+void ShibokenGenerator::collectFullTypeNamesArray(QStringList &typeNames)
+{
+ for (const auto &metaClass : api().classes()) {
+ collectfromTypeEntry(metaClass->typeEntry(), typeNames);
+
+ for (const AbstractMetaEnum &metaEnum : metaClass->enums())
+ collectfromTypeEntry(metaEnum.typeEntry(), typeNames);
+
+ int smartPointerCountIndex = getMaxTypeIndex();
+ for (const auto &smp : api().instantiatedSmartPointers()) {
+ auto entry = smp.type.typeEntry();
+ typeNames[smartPointerCountIndex] =
+ smp.specialized->typeEntry()->qualifiedTargetLangName();
+ ++smartPointerCountIndex;
+ }
+ }
+ for (const AbstractMetaEnum &metaEnum : api().globalEnums())
+ collectfromTypeEntry(metaEnum.typeEntry(), typeNames);
+}
+
+bool ShibokenGenerator::verboseErrorMessagesDisabled()
+{
+ return m_options.verboseErrorMessagesDisabled;
+}
+
+bool ShibokenGenerator::pythonFunctionWrapperUsesListOfArguments(const AbstractMetaFunctionCPtr &func) const
+{
+ const auto &groups = func->implementingClass()
+ ? getFunctionGroups(func->implementingClass())
+ : getGlobalFunctionGroups();
+ OverloadData od(groups.value(func->name()), api());
+ return od.pythonFunctionWrapperUsesListOfArguments();
+}
+
+QString ShibokenGenerator::minimalConstructorExpression(const ApiExtractorResult &api,
+ const AbstractMetaType &type)
+{
+ if (type.isExtendedCppPrimitive() || type.isSmartPointer())
+ return {};
+ QString errorMessage;
+ const auto ctor = minimalConstructor(api, type, &errorMessage);
+ if (ctor.has_value())
+ return ctor->initialization();
+
+ const QString message =
+ msgCouldNotFindMinimalConstructor(QLatin1StringView(__FUNCTION__),
+ type.cppSignature(), errorMessage);
+ qCWarning(lcShiboken()).noquote() << message;
+ return u";\n#error "_s + message + u'\n';
+}
+
+QString ShibokenGenerator::minimalConstructorExpression(const ApiExtractorResult &api,
+ const TypeEntryCPtr &type)
+{
+ if (isExtendedCppPrimitive(type))
+ return {};
+ const auto ctor = minimalConstructor(api, type);
+ if (ctor.has_value())
+ return ctor->initialization();
+
+ const QString message =
+ msgCouldNotFindMinimalConstructor(QLatin1StringView(__FUNCTION__),
+ type->qualifiedCppName());
+ qCWarning(lcShiboken()).noquote() << message;
+ return u";\n#error "_s + message + u'\n';
+}
+
+QString ShibokenGenerator::pythonArgsAt(int i)
+{
+ return PYTHON_ARGS + u'[' + QString::number(i) + u']';
+}
+
+void ShibokenGenerator::replaceTemplateVariables(QString &code,
+ const AbstractMetaFunctionCPtr &func) const
+{
+ const auto cpp_class = func->ownerClass();
+ if (cpp_class)
+ code.replace(u"%TYPE"_s, cpp_class->name());
+
+ const AbstractMetaArgumentList &argument = func->arguments();
+ for (const AbstractMetaArgument &arg : argument)
+ code.replace(u'%' + QString::number(arg.argumentIndex() + 1), arg.name());
+
+ //template values
+ code.replace(u"%RETURN_TYPE"_s, translateType(func->type(), cpp_class));
+ code.replace(u"%FUNCTION_NAME"_s, func->originalName());
+
+ if (code.contains(u"%ARGUMENT_NAMES")) {
+ StringStream aux_stream;
+ writeArgumentNames(aux_stream, func, Generator::SkipRemovedArguments);
+ code.replace(u"%ARGUMENT_NAMES"_s, aux_stream);
+ }
+
+ if (code.contains(u"%ARGUMENTS")) {
+ StringStream aux_stream;
+ writeFunctionArguments(aux_stream, func, Options(SkipDefaultValues) | SkipRemovedArguments);
+ code.replace(u"%ARGUMENTS"_s, aux_stream);
+ }
+}
+
+QString ShibokenGenerator::stdMove(const QString &c)
+{
+ return u"std::move("_s + c + u')';
+}
diff --git a/sources/shiboken6/generator/shiboken/shibokengenerator.h b/sources/shiboken6/generator/shiboken/shibokengenerator.h
new file mode 100644
index 000000000..22ee73fa2
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/shibokengenerator.h
@@ -0,0 +1,491 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef SHIBOKENGENERATOR_H
+#define SHIBOKENGENERATOR_H
+
+#include <generator.h>
+
+#include "customconversion_typedefs.h"
+#include "abstractmetalang_enums.h"
+#include "typesystem_typedefs.h"
+#include "typesystem_enums.h"
+
+#include <QtCore/QRegularExpression>
+
+#include <array>
+#include <optional>
+
+class EnumTypeEntry;
+class FlagsTypeEntry;
+class DocParser;
+class CodeSnip;
+class QPropertySpec;
+class OverloadData;
+class TargetToNativeConversion;
+struct GeneratorClassInfoCacheEntry;
+struct IncludeGroup;
+struct ShibokenGeneratorOptions;
+
+class TextStream;
+
+// Function to be used for implementing nb_bool
+struct BoolCastFunction
+{
+ AbstractMetaFunctionCPtr function;
+ bool invert = false; // Function is "isNull()", (invert result).
+};
+
+using BoolCastFunctionOptional = std::optional<BoolCastFunction>;
+
+/**
+ * Abstract generator that contains common methods used in CppGenerator and HeaderGenerator.
+ */
+class ShibokenGenerator : public Generator
+{
+public:
+ Q_DISABLE_COPY_MOVE(ShibokenGenerator)
+
+ /// Besides the actual bindings (see AbstractMetaFunction::generateBinding(),
+ /// some functions need to be generated into the wrapper class
+ /// (virtual method/avoid protected hack expose).
+ enum class FunctionGenerationFlag
+ {
+ None = 0x0,
+ /// Virtual method overridable in Python
+ VirtualMethod = 0x1,
+ /// Special QObject virtuals
+ QMetaObjectMethod = 0x2,
+ /// Needs a protected wrapper for avoidProtectedHack()
+ /// public "foo_protected()" calling "foo()"
+ ProtectedWrapper = 0x4, //
+ /// Pass through constructor
+ WrapperConstructor = 0x8,
+ /// Generate a special copy constructor
+ /// "FooBar_Wrapper(const Foo&)" for constructing a wrapper from a value
+ WrapperSpecialCopyConstructor = 0x10
+ };
+ Q_DECLARE_FLAGS(FunctionGeneration, FunctionGenerationFlag);
+
+ enum class AttroCheckFlag
+ {
+ None = 0x0,
+ GetattroOverloads = 0x01,
+ GetattroSmartPointer = 0x02,
+ GetattroUser = 0x04, // Injected code
+ GetattroMask = 0x0F,
+ SetattroQObject = 0x10,
+ SetattroSmartPointer = 0x20,
+ SetattroMethodOverride = 0x40,
+ SetattroUser = 0x80, // Injected code
+ SetattroMask = 0xF0,
+ };
+ Q_DECLARE_FLAGS(AttroCheck, AttroCheckFlag);
+
+ using FunctionGroups = QMap<QString, AbstractMetaFunctionCList>; // Sorted
+
+ ShibokenGenerator();
+ ~ShibokenGenerator() override;
+
+ const char *name() const override { return "Shiboken"; }
+
+ static QList<OptionDescription> options();
+ static std::shared_ptr<OptionsParser> createOptionsParser();
+
+ static QString minimalConstructorExpression(const ApiExtractorResult &api,
+ const AbstractMetaType &type);
+ static QString minimalConstructorExpression(const ApiExtractorResult &api,
+ const TypeEntryCPtr &type);
+
+protected:
+ bool doSetup() override;
+
+ GeneratorContext contextForClass(const AbstractMetaClassCPtr &c) const override;
+
+ /**
+ * Returns a map with all functions grouped, the function name is used as key.
+ * Example of return value: { "foo" -> ["foo(int)", "foo(int, long)], "bar" -> "bar(double)"}
+ * \param scope Where to search for functions, null means all global functions.
+ */
+ FunctionGroups getGlobalFunctionGroups() const;
+ static FunctionGroups getFunctionGroups(const AbstractMetaClassCPtr &scope);
+
+ static QList<AbstractMetaFunctionCList>
+ numberProtocolOperators(const AbstractMetaClassCPtr &scope);
+
+ static BoolCastFunctionOptional boolCast(const AbstractMetaClassCPtr &scope);
+
+ /**
+ * Returns all different inherited overloads of func, and includes func as well.
+ * The function can be called multiple times without duplication.
+ * \param func the metafunction to be searched in subclasses.
+ * \param seen the function's minimal signatures already seen.
+ */
+ static AbstractMetaFunctionCList getFunctionAndInheritedOverloads(const AbstractMetaFunctionCPtr &func,
+ QSet<QString> *seen);
+
+ /// Write user's custom code snippets at class or module level.
+ void writeClassCodeSnips(TextStream &s,
+ const QList<CodeSnip> &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language,
+ const GeneratorContext &context) const;
+ void writeCodeSnips(TextStream &s,
+ const QList<CodeSnip> &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language) const;
+ /// Write user's custom code snippets at function level.
+ void writeCodeSnips(TextStream &s,
+ const QList<CodeSnip> &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language,
+ const AbstractMetaFunctionCPtr &func,
+ bool usePyArgs,
+ const AbstractMetaArgument *lastArg) const;
+
+ /// Replaces variables for the user's custom code at global or class level.
+ void processCodeSnip(QString &code) const;
+ void processCodeSnip(QString &code, const QString &context) const;
+ void processClassCodeSnip(QString &code, const GeneratorContext &context) const;
+
+ /**
+ * Verifies if any of the function's code injections makes a call
+ * to the C++ method. This is used by the generator to avoid writing calls
+ * to C++ when the user custom code already does this.
+ * \param func the function to check
+ * \return true if the function's code snippets call the wrapped C++ function
+ */
+ static bool injectedCodeCallsCppFunction(const GeneratorContext &context,
+ const AbstractMetaFunctionCPtr &func);
+
+ /**
+ * Function which parse the metafunction information
+ * \param func the function witch will be parserd
+ * \param option some extra options
+ * \param arg_count the number of function arguments
+ */
+ QString functionSignature(const AbstractMetaFunctionCPtr &func,
+ const QString &prepend = QString(),
+ const QString &append = QString(),
+ Options options = NoOption,
+ int arg_count = -1) const;
+
+ /// Returns the top-most class that has multiple inheritance in the ancestry.
+ static AbstractMetaClassCPtr
+ getMultipleInheritingClass(const AbstractMetaClassCPtr &metaClass);
+
+ static bool useOverrideCaching(const AbstractMetaClassCPtr &metaClass);
+ static AttroCheck checkAttroFunctionNeeds(const AbstractMetaClassCPtr &metaClass);
+
+ /// Returns a list of methods of the given class where each one is part of
+ /// a different overload with both static and non-static method.
+ static AbstractMetaFunctionCList
+ getMethodsWithBothStaticAndNonStaticMethods(const AbstractMetaClassCPtr &metaClass);
+
+ static void writeToPythonConversion(TextStream &s,
+ const AbstractMetaType &type,
+ const AbstractMetaClassCPtr &context,
+ const QString &argumentName);
+ static void writeToCppConversion(TextStream &s,
+ const AbstractMetaType &type,
+ const QString &inArgName,
+ const QString &outArgName);
+ static void writeToCppConversion(TextStream &s,
+ const AbstractMetaClassCPtr &metaClass,
+ const QString &inArgName,
+ const QString &outArgName);
+
+ /// Returns true if the argument is a pointer that rejects nullptr values.
+ static bool shouldRejectNullPointerArgument(const AbstractMetaFunctionCPtr &func,
+ int argIndex);
+
+ /// Verifies if the class should have a C++ wrapper generated for it,
+ /// instead of only a Python wrapper.
+ static bool shouldGenerateCppWrapper(const AbstractMetaClassCPtr &metaClass);
+
+ static bool shouldGenerateMetaObjectFunctions(const AbstractMetaClassCPtr &metaClass);
+
+ /// Returns which functions need to be generated into the wrapper class
+ static FunctionGeneration functionGeneration(const AbstractMetaFunctionCPtr &func);
+
+ // Return a list of implicit conversions if generation is enabled.
+ AbstractMetaFunctionCList implicitConversions(const TypeEntryCPtr &t) const;
+
+ static QString wrapperName(const AbstractMetaClassCPtr &metaClass);
+
+ static QString fullPythonClassName(const AbstractMetaClassCPtr &metaClass);
+
+ static QString headerFileNameForContext(const GeneratorContext &context);
+ IncludeGroup baseWrapperIncludes(const GeneratorContext &classContext) const;
+
+ static QString fullPythonFunctionName(const AbstractMetaFunctionCPtr &func, bool forceFunc);
+
+ static bool wrapperDiagnostics();
+
+ static QString protectedEnumSurrogateName(const AbstractMetaEnum &metaEnum);
+
+ static QString pythonPrimitiveTypeName(const QString &cppTypeName);
+
+ static QString pythonOperatorFunctionName(const AbstractMetaFunctionCPtr &func);
+ static QList<AbstractMetaFunctionCList>
+ filterGroupedOperatorFunctions(const AbstractMetaClassCPtr &metaClass,
+ OperatorQueryOptions query);
+
+ static QString fixedCppTypeName(const TargetToNativeConversion &toNative);
+ static QString fixedCppTypeName(const AbstractMetaType &type);
+ static QString fixedCppTypeName(const TypeEntryCPtr &type, QString typeName = {});
+
+ static bool isNumber(const QString &cpythonApiName);
+ static bool isNumber(const TypeEntryCPtr &type);
+ static bool isNumber(const AbstractMetaType &type);
+ static bool isPyInt(const TypeEntryCPtr &type);
+ static bool isPyInt(const AbstractMetaType &type);
+
+ static bool isNullPtr(const QString &value);
+
+ static QString converterObject(const AbstractMetaType &type) ;
+ static QString converterObject(const TypeEntryCPtr &type);
+
+ static QString cpythonBaseName(const AbstractMetaClassCPtr &metaClass);
+ static QString cpythonBaseName(const TypeEntryCPtr &type);
+ static QString containerCpythonBaseName(const ContainerTypeEntryCPtr &ctype);
+ static QString cpythonBaseName(const AbstractMetaType &type);
+ static QString cpythonTypeName(const AbstractMetaClassCPtr &metaClass);
+ static QString cpythonTypeName(const TypeEntryCPtr &type);
+ static QString cpythonTypeNameExtSet(const TypeEntryCPtr &type);
+ static QString cpythonTypeNameExtSet(const AbstractMetaType &type);
+ static QString cpythonTypeNameExt(const TypeEntryCPtr &type);
+ static QString cpythonTypeNameExt(const AbstractMetaType &type);
+ static QString cpythonCheckFunction(TypeEntryCPtr type);
+ static QString cpythonCheckFunction(AbstractMetaType metaType);
+ static QString cpythonIsConvertibleFunction(const TypeEntryCPtr &type);
+ static QString cpythonIsConvertibleFunction(const AbstractMetaType &metaType);
+ static QString cpythonIsConvertibleFunction(const AbstractMetaArgument &metaArg);
+
+ static QString cpythonToCppConversionFunction(const AbstractMetaClassCPtr &metaClass) ;
+ static QString cpythonToCppConversionFunction(const AbstractMetaType &type);
+ static QString cpythonToPythonConversionFunction(const AbstractMetaType &type);
+ static QString cpythonToPythonConversionFunction(const AbstractMetaClassCPtr &metaClass);
+ static QString cpythonToPythonConversionFunction(const TypeEntryCPtr &type);
+
+ static QString cpythonFunctionName(const AbstractMetaFunctionCPtr &func) ;
+ static QString cpythonMethodDefinitionName(const AbstractMetaFunctionCPtr &func);
+ static QString cpythonGettersSettersDefinitionName(const AbstractMetaClassCPtr &metaClass);
+ static QString cpythonGetattroFunctionName(const AbstractMetaClassCPtr &metaClass);
+ static QString cpythonSetattroFunctionName(const AbstractMetaClassCPtr &metaClass);
+ static QString cpythonGetterFunctionName(const AbstractMetaField &metaField);
+ static QString cpythonSetterFunctionName(const AbstractMetaField &metaField);
+ static QString cpythonGetterFunctionName(const QPropertySpec &property,
+ const AbstractMetaClassCPtr &metaClass);
+ static QString cpythonSetterFunctionName(const QPropertySpec &property,
+ const AbstractMetaClassCPtr &metaClass);
+ static QString cpythonWrapperCPtr(const AbstractMetaClassCPtr &metaClass,
+ const QString &argName = QLatin1StringView("self"));
+ static QString cpythonWrapperCPtr(const AbstractMetaType &metaType,
+ const QString &argName);
+ static QString cpythonWrapperCPtr(const TypeEntryCPtr &type, const QString &argName);
+
+ static QString cpythonEnumName(const EnumTypeEntryCPtr &enumEntry);
+ static QString cpythonEnumName(const AbstractMetaEnum &metaEnum);
+
+ static QString cpythonFlagsName(const FlagsTypeEntryCPtr &flagsEntry);
+ static QString cpythonFlagsName(const AbstractMetaEnum *metaEnum);
+ /// Returns the special cast function name, the function used to proper cast
+ /// class with multiple inheritance.
+ static QString cpythonSpecialCastFunctionName(const AbstractMetaClassCPtr &metaClass);
+
+ /// Returns the file name for the module global header. If no module name
+ /// is provided the current will be used.
+ static QString getModuleHeaderFileName(const QString &moduleName = QString());
+ static QString getPrivateModuleHeaderFileName(const QString &moduleName = QString());
+
+ /// Includes for header (native wrapper class) or binding source
+ QList<IncludeGroup> classIncludes(const AbstractMetaClassCPtr &metaClass) const;
+
+ /// Returns true if the user enabled the so called "parent constructor heuristic".
+ static bool useCtorHeuristic();
+ /// Returns true if the user enabled the so called "return value heuristic".
+ static bool useReturnValueHeuristic();
+ /// Returns true if the generator should use the result of isNull()const to compute boolean casts.
+ static bool useIsNullAsNbBool();
+ /// Whether to generate lean module headers
+ static bool leanHeaders();
+ /// Returns true if the generator should use operator bool to compute boolean casts.
+ static bool useOperatorBoolAsNbBool();
+ /// Generate implicit conversions of function arguments
+ static bool generateImplicitConversions();
+ static QString cppApiVariableNameOld(const QString &moduleName = {});
+ static QString cppApiVariableName(const QString &moduleName = QString());
+ static QString pythonModuleObjectName(const QString &moduleName = QString());
+ static QString convertersVariableName(const QString &moduleName = QString());
+ /// Returns the type index variable name for a given class.
+ static QString getTypeIndexVariableName(const AbstractMetaClassCPtr &metaClass);
+ /// Returns the type index variable name for a given typedef for a template
+ /// class instantiation made of the template class and the instantiation values
+ static QString getTypeAlternateTemplateIndexVariableName(const AbstractMetaClassCPtr &metaClass);
+ static QString getTypeIndexVariableName(TypeEntryCPtr type);
+ static QString getTypeIndexVariableName(const AbstractMetaType &type) ;
+
+ /// Collect all type names as an array for initializing the type/name struct.
+ void collectFullTypeNamesArray(QStringList &typeNames);
+
+ /// Returns true if the user don't want verbose error messages on the generated bindings.
+ static bool verboseErrorMessagesDisabled();
+
+ void collectContainerTypesFromConverterMacros(const QString &code, bool toPythonMacro);
+
+ static void writeFunctionCall(TextStream &s,
+ const AbstractMetaFunctionCPtr &metaFunc,
+ Options options = NoOption);
+
+ // All data about extended converters: the type entries of the target type, and a
+ // list of AbstractMetaClasses accepted as argument for the conversion.
+ using ExtendedConverterData = QHash<TypeEntryCPtr, AbstractMetaClassCList>;
+ /// Returns all extended conversions for the current module.
+ ExtendedConverterData getExtendedConverters() const;
+
+ /// Returns a list of converters for the non wrapper types of the current module.
+ static QList<CustomConversionPtr> getPrimitiveCustomConversions();
+
+ /// Returns true if the Python wrapper for the received OverloadData must accept a list of arguments.
+ bool pythonFunctionWrapperUsesListOfArguments(const AbstractMetaFunctionCPtr &func) const;
+
+ static const QRegularExpression &convertToCppRegEx()
+ { return typeSystemConvRegExps()[TypeSystemToCppFunction]; }
+
+ static QString pythonArgsAt(int i);
+
+ /// Return the format character for C++->Python->C++ conversion (Py_BuildValue)
+ static const QHash<QString, QChar> &formatUnits();
+
+ static QString stdMove(const QString &c);
+
+private:
+ static QString getModuleHeaderFileBaseName(const QString &moduleName = QString());
+ static QString cpythonGetterFunctionName(const QString &name,
+ const AbstractMetaClassCPtr &enclosingClass);
+ static QString cpythonSetterFunctionName(const QString &name,
+ const AbstractMetaClassCPtr &enclosingClass);
+
+ static const GeneratorClassInfoCacheEntry &
+ getGeneratorClassInfo(const AbstractMetaClassCPtr &scope);
+ static FunctionGroups getFunctionGroupsImpl(const AbstractMetaClassCPtr &scope);
+ static QList<AbstractMetaFunctionCList>
+ getNumberProtocolOperators(const AbstractMetaClassCPtr &metaClass);
+ static BoolCastFunctionOptional getBoolCast(const AbstractMetaClassCPtr &metaClass);
+ static bool classNeedsGetattroFunctionImpl(const AbstractMetaClassCPtr &metaClass);
+
+ QString translateTypeForWrapperMethod(const AbstractMetaType &cType,
+ const AbstractMetaClassCPtr &context,
+ Options opt = NoOption) const;
+
+ /**
+ * Returns all different inherited overloads of func.
+ * The function can be called multiple times without duplication.
+ * \param func the metafunction to be searched in subclasses.
+ * \param seen the function's minimal signatures already seen.
+ */
+ static void getInheritedOverloads(const AbstractMetaClassCPtr &scope,
+ AbstractMetaFunctionCList *overloads);
+
+
+ /**
+ * Write a function argument in the C++ in the text stream \p s.
+ * This function just call \code s << argumentString(); \endcode
+ * \param s text stream used to write the output.
+ * \param func the current metafunction.
+ * \param argument metaargument information to be parsed.
+ * \param options some extra options.
+ */
+ void writeArgument(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaArgument &argument,
+ Options options = NoOption) const;
+ /**
+ * Create a QString in the C++ format to an function argument.
+ * \param func the current metafunction.
+ * \param argument metaargument information to be parsed.
+ * \param options some extra options.
+ */
+ QString argumentString(const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaArgument &argument,
+ Options options = NoOption) const;
+
+ QString functionReturnType(const AbstractMetaFunctionCPtr &func, Options options = NoOption) const;
+
+ /// Utility function for writeCodeSnips.
+ using ArgumentVarReplacementPair = std::pair<AbstractMetaArgument, QString>;
+ using ArgumentVarReplacementList = QList<ArgumentVarReplacementPair>;
+ static ArgumentVarReplacementList
+ getArgumentReplacement(const AbstractMetaFunctionCPtr &func,
+ bool usePyArgs, TypeSystem::Language language,
+ const AbstractMetaArgument *lastArg);
+
+ /// Returns a string with the user's custom code snippets that comply with \p position and \p language.
+ static QString getCodeSnippets(const QList<CodeSnip> &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language);
+
+ enum TypeSystemConverterVariable {
+ TypeSystemCheckFunction = 0,
+ TypeSystemIsConvertibleFunction,
+ TypeSystemToCppFunction,
+ TypeSystemToPythonFunction,
+ TypeSystemConverterVariables
+ };
+ void replaceConverterTypeSystemVariable(TypeSystemConverterVariable converterVariable,
+ QString &code) const;
+
+ /// Replaces the %CONVERTTOPYTHON type system variable.
+ inline void replaceConvertToPythonTypeSystemVariable(QString &code) const
+ {
+ replaceConverterTypeSystemVariable(TypeSystemToPythonFunction, code);
+ }
+ /// Replaces the %CONVERTTOCPP type system variable.
+ inline void replaceConvertToCppTypeSystemVariable(QString &code) const
+ {
+ replaceConverterTypeSystemVariable(TypeSystemToCppFunction, code);
+ }
+ /// Replaces the %ISCONVERTIBLE type system variable.
+ inline void replaceIsConvertibleToCppTypeSystemVariable(QString &code) const
+ {
+ replaceConverterTypeSystemVariable(TypeSystemIsConvertibleFunction, code);
+ }
+ /// Replaces the %CHECKTYPE type system variable.
+ inline void replaceTypeCheckTypeSystemVariable(QString &code) const
+ {
+ replaceConverterTypeSystemVariable(TypeSystemCheckFunction, code);
+ }
+
+ /// Return a prefix with '_' suitable for names in C++
+ static QString moduleCppPrefix(const QString &moduleName = QString());
+
+ /// Functions used to write the function arguments on the class buffer.
+ /// \param s the class output buffer
+ /// \param func the pointer to metafunction information
+ /// \param count the number of function arguments
+ /// \param options some extra options used during the parser
+ static void writeArgumentNames(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ Options option);
+
+ void writeFunctionArguments(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ Options options = NoOption) const;
+
+ void replaceTemplateVariables(QString &code,
+ const AbstractMetaFunctionCPtr &func) const;
+
+ static ShibokenGeneratorOptions m_options;
+
+ /// Type system converter variable replacement names and regular expressions.
+ static const QHash<int, QString> &typeSystemConvName();
+
+ using TypeSystemConverterRegExps = std::array<QRegularExpression, TypeSystemConverterVariables>;
+ static const TypeSystemConverterRegExps &typeSystemConvRegExps();
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(ShibokenGenerator::FunctionGeneration);
+Q_DECLARE_OPERATORS_FOR_FLAGS(ShibokenGenerator::AttroCheck);
+
+#endif // SHIBOKENGENERATOR_H
diff --git a/sources/shiboken6/generator/shibokenconfig.h.in b/sources/shiboken6/generator/shibokenconfig.h.in
new file mode 100644
index 000000000..848bad372
--- /dev/null
+++ b/sources/shiboken6/generator/shibokenconfig.h.in
@@ -0,0 +1,6 @@
+#ifndef SHIBOKENCONFIG_H
+#define SHIBOKENCONFIG_H
+
+#define SHIBOKEN_VERSION "@shiboken6_VERSION@"
+
+#endif