aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/generator/shiboken
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/generator/shiboken')
-rw-r--r--sources/shiboken6/generator/shiboken/configurablescope.h33
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator.cpp6820
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator.h567
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator_container.cpp272
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp486
-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.cpp964
-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.cpp2649
-rw-r--r--sources/shiboken6/generator/shiboken/shibokengenerator.h491
16 files changed, 13821 insertions, 0 deletions
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..0eeeec6f9
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp
@@ -0,0 +1,6820 @@
+// 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 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;
+}
+
+// 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(metaType.pythonSignature());
+
+ // 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 << f->arguments().constFirst().type().pythonSignature();
+ }
+ } 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 = f->type().pythonSignature();
+ 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';
+
+ // 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.
+void CppGenerator::writeInitFunc(TextStream &declStr, TextStream &callStr,
+ const QString &initFunctionName,
+ const TypeEntryCPtr &enclosingEntry,
+ const QString &pythonName, bool lazy)
+{
+ const QString functionName = "init_"_L1 + initFunctionName;
+ const bool hasParent = enclosingEntry && enclosingEntry->type() != TypeEntry::TypeSystemType;
+ declStr << "PyTypeObject *" << functionName << "(PyObject *"
+ << (hasParent ? "enclosingClass" : "module") << ");\n";
+
+ 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';
+ }
+ writeInitFunc(s_classInitDecl, s_classPythonDefines,
+ getSimpleClassInitFunctionName(cls),
+ 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 = enclosingClass
+ ? enclosingClass->typeEntry()
+ : targetLangEnclosingEntry(smp.type.typeEntry());
+
+ writeInitFunc(s_classInitDecl, s_classPythonDefines,
+ getInitFunctionName(context),
+ enclosingTypeEntry, smp.type.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';
+ }
+
+ // Implicit smart pointers conversions
+ const auto &smartPointersList = api().instantiatedSmartPointers();
+ if (!smartPointersList.isEmpty()) {
+ s << "// SmartPointers converters.\n\n";
+ for (const auto &smp : smartPointersList) {
+ s << "// C++ to Python conversion for smart pointer type '"
+ << smp.type.cppSignature() << "'.\n";
+ writeSmartPointerConverterFunctions(s, smp.type);
+ }
+ 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 (!smartPointersList.isEmpty()) {
+ s << '\n';
+ for (const auto &smp : smartPointersList) {
+ writeSmartPointerConverterInitialization(s, smp.type);
+ 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..a31c2ca14
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/cppgenerator.h
@@ -0,0 +1,567 @@
+// 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 writeInitFunc(TextStream &declStr, TextStream &callStr,
+ const QString &initFunctionName,
+ 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;
+ /// 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(); }
+
+ std::optional<AbstractMetaType>
+ findSmartPointerInstantiation(const SmartPointerTypeEntryCPtr &pointer,
+ const TypeEntryCPtr &pointee) const;
+ 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..1b893640a
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp
@@ -0,0 +1,486 @@
+// 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;
+}
+
+std::optional<AbstractMetaType>
+ CppGenerator::findSmartPointerInstantiation(const SmartPointerTypeEntryCPtr &pointer,
+ const TypeEntryCPtr &pointee) const
+{
+ for (const auto &smp : api().instantiatedSmartPointers()) {
+ const auto &i = smp.type;
+ if (i.typeEntry() == pointer && i.instantiations().at(0).typeEntry() == pointee)
+ return i;
+ }
+ return {};
+}
+
+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);
+ 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 = findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) {
+ const auto smartTargetType = opt.value();
+ 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 = findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) {
+ const auto smartTargetType = opt.value();
+ 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..35d0d114f
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/headergenerator.cpp
@@ -0,0 +1,964 @@
+// 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;
+
+ // write license comment
+ s << licenseComment();
+
+ // 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..a1417e5d9
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/shibokengenerator.cpp
@@ -0,0 +1,2649 @@
+// 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] = entry->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