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/cppgenerator.cpp6444
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator.h419
-rw-r--r--sources/shiboken6/generator/shiboken/ctypenames.h56
-rw-r--r--sources/shiboken6/generator/shiboken/headergenerator.cpp661
-rw-r--r--sources/shiboken6/generator/shiboken/headergenerator.h70
-rw-r--r--sources/shiboken6/generator/shiboken/overloaddata.cpp1093
-rw-r--r--sources/shiboken6/generator/shiboken/overloaddata.h164
-rw-r--r--sources/shiboken6/generator/shiboken/shibokengenerator.cpp2884
-rw-r--r--sources/shiboken6/generator/shiboken/shibokengenerator.h585
9 files changed, 12376 insertions, 0 deletions
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp
new file mode 100644
index 000000000..1d7649b18
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp
@@ -0,0 +1,6444 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt for Python.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <memory>
+
+#include "cppgenerator.h"
+#include "ctypenames.h"
+#include "fileout.h"
+#include "overloaddata.h"
+#include <abstractmetalang.h>
+#include <messages.h>
+#include <propertyspec.h>
+#include <reporthandler.h>
+#include <typedatabase.h>
+
+#include <QtCore/QDir>
+#include <QtCore/QMetaObject>
+#include <QtCore/QRegularExpression>
+#include <QtCore/QTextStream>
+#include <QtCore/QDebug>
+#include <QMetaType>
+
+#include <algorithm>
+#include <cstring>
+
+static const char CPP_ARG0[] = "cppArg0";
+
+QHash<QString, QString> CppGenerator::m_nbFuncs = QHash<QString, QString>();
+QHash<QString, QString> CppGenerator::m_sqFuncs = QHash<QString, QString>();
+QHash<QString, QString> CppGenerator::m_mpFuncs = QHash<QString, QString>();
+QString CppGenerator::m_currentErrorCode(QLatin1String("{}"));
+
+static const char typeNameFunc[] = R"CPP(
+template <class T>
+static const char *typeNameOf(const T &t)
+{
+ const char *typeName = typeid(t).name();
+ auto size = std::strlen(typeName);
+#if defined(Q_CC_MSVC) // MSVC: "class QPaintDevice * __ptr64"
+ if (auto lastStar = strchr(typeName, '*')) {
+ // MSVC: "class QPaintDevice * __ptr64"
+ while (*--lastStar == ' ') {
+ }
+ size = lastStar - typeName + 1;
+ }
+#else // g++, Clang: "QPaintDevice *" -> "P12QPaintDevice"
+ if (size > 2 && typeName[0] == 'P' && std::isdigit(typeName[1])) {
+ ++typeName;
+ --size;
+ }
+#endif
+ char *result = new char[size + 1];
+ result[size] = '\0';
+ memcpy(result, typeName, size);
+ return result;
+}
+)CPP";
+
+// utility functions
+inline AbstractMetaType getTypeWithoutContainer(const AbstractMetaType &arg)
+{
+ if (arg && arg.typeEntry()->isContainer()) {
+ // only support containers with 1 type
+ if (arg.instantiations().size() == 1)
+ return arg.instantiations().constFirst();
+ }
+ return arg;
+}
+
+// A helper for writing C++ return statements for either void ("return;")
+// or some return value ("return value;")
+class returnStatement
+{
+public:
+ explicit returnStatement(QString s) : m_returnValue(std::move(s)) {}
+
+ friend QTextStream &operator<<(QTextStream &s, const returnStatement &r);
+
+private:
+ const QString m_returnValue;
+};
+
+QTextStream &operator<<(QTextStream &s, const returnStatement &r)
+{
+ s << "return";
+ if (!r.m_returnValue.isEmpty())
+ s << ' ' << r.m_returnValue;
+ s << ';';
+ return s;
+}
+
+CppGenerator::CppGenerator()
+{
+ // Number protocol structure members names
+ m_nbFuncs.insert(QLatin1String("__add__"), QLatin1String("nb_add"));
+ m_nbFuncs.insert(QLatin1String("__sub__"), QLatin1String("nb_subtract"));
+ m_nbFuncs.insert(QLatin1String("__mul__"), QLatin1String("nb_multiply"));
+ m_nbFuncs.insert(QLatin1String("__div__"), QLatin1String("nb_divide"));
+ m_nbFuncs.insert(QLatin1String("__mod__"), QLatin1String("nb_remainder"));
+ m_nbFuncs.insert(QLatin1String("__neg__"), QLatin1String("nb_negative"));
+ m_nbFuncs.insert(QLatin1String("__pos__"), QLatin1String("nb_positive"));
+ m_nbFuncs.insert(QLatin1String("__invert__"), QLatin1String("nb_invert"));
+ m_nbFuncs.insert(QLatin1String("__lshift__"), QLatin1String("nb_lshift"));
+ m_nbFuncs.insert(QLatin1String("__rshift__"), QLatin1String("nb_rshift"));
+ m_nbFuncs.insert(QLatin1String("__and__"), QLatin1String("nb_and"));
+ m_nbFuncs.insert(QLatin1String("__xor__"), QLatin1String("nb_xor"));
+ m_nbFuncs.insert(QLatin1String("__or__"), QLatin1String("nb_or"));
+ m_nbFuncs.insert(QLatin1String("__iadd__"), QLatin1String("nb_inplace_add"));
+ m_nbFuncs.insert(QLatin1String("__isub__"), QLatin1String("nb_inplace_subtract"));
+ m_nbFuncs.insert(QLatin1String("__imul__"), QLatin1String("nb_inplace_multiply"));
+ m_nbFuncs.insert(QLatin1String("__idiv__"), QLatin1String("nb_inplace_divide"));
+ m_nbFuncs.insert(QLatin1String("__imod__"), QLatin1String("nb_inplace_remainder"));
+ m_nbFuncs.insert(QLatin1String("__ilshift__"), QLatin1String("nb_inplace_lshift"));
+ m_nbFuncs.insert(QLatin1String("__irshift__"), QLatin1String("nb_inplace_rshift"));
+ m_nbFuncs.insert(QLatin1String("__iand__"), QLatin1String("nb_inplace_and"));
+ m_nbFuncs.insert(QLatin1String("__ixor__"), QLatin1String("nb_inplace_xor"));
+ m_nbFuncs.insert(QLatin1String("__ior__"), QLatin1String("nb_inplace_or"));
+ m_nbFuncs.insert(QLatin1String("bool"), QLatin1String("nb_nonzero"));
+
+ // sequence protocol functions
+ m_sequenceProtocol.insert(QLatin1String("__len__"),
+ {QLatin1String("PyObject *self"),
+ QLatin1String("Py_ssize_t")});
+ m_sequenceProtocol.insert(QLatin1String("__getitem__"),
+ {QLatin1String("PyObject *self, Py_ssize_t _i"),
+ QLatin1String("PyObject*")});
+ m_sequenceProtocol.insert(QLatin1String("__setitem__"),
+ {QLatin1String("PyObject *self, Py_ssize_t _i, PyObject *_value"),
+ intT()});
+ m_sequenceProtocol.insert(QLatin1String("__getslice__"),
+ {QLatin1String("PyObject *self, Py_ssize_t _i1, Py_ssize_t _i2"),
+ QLatin1String("PyObject*")});
+ m_sequenceProtocol.insert(QLatin1String("__setslice__"),
+ {QLatin1String("PyObject *self, Py_ssize_t _i1, Py_ssize_t _i2, PyObject *_value"),
+ intT()});
+ m_sequenceProtocol.insert(QLatin1String("__contains__"),
+ {QLatin1String("PyObject *self, PyObject *_value"),
+ intT()});
+ m_sequenceProtocol.insert(QLatin1String("__concat__"),
+ {QLatin1String("PyObject *self, PyObject *_other"),
+ QLatin1String("PyObject*")});
+
+ // Sequence protocol structure members names
+ m_sqFuncs.insert(QLatin1String("__concat__"), QLatin1String("sq_concat"));
+ m_sqFuncs.insert(QLatin1String("__contains__"), QLatin1String("sq_contains"));
+ m_sqFuncs.insert(QLatin1String("__getitem__"), QLatin1String("sq_item"));
+ m_sqFuncs.insert(QLatin1String("__getslice__"), QLatin1String("sq_slice"));
+ m_sqFuncs.insert(QLatin1String("__len__"), QLatin1String("sq_length"));
+ m_sqFuncs.insert(QLatin1String("__setitem__"), QLatin1String("sq_ass_item"));
+ m_sqFuncs.insert(QLatin1String("__setslice__"), QLatin1String("sq_ass_slice"));
+
+ // mapping protocol function
+ m_mappingProtocol.insert(QLatin1String("__mlen__"),
+ {QLatin1String("PyObject *self"),
+ QLatin1String("Py_ssize_t")});
+ m_mappingProtocol.insert(QLatin1String("__mgetitem__"),
+ {QLatin1String("PyObject *self, PyObject *_key"),
+ QLatin1String("PyObject*")});
+ m_mappingProtocol.insert(QLatin1String("__msetitem__"),
+ {QLatin1String("PyObject *self, PyObject *_key, PyObject *_value"),
+ intT()});
+
+ // Sequence protocol structure members names
+ m_mpFuncs.insert(QLatin1String("__mlen__"), QLatin1String("mp_length"));
+ m_mpFuncs.insert(QLatin1String("__mgetitem__"), QLatin1String("mp_subscript"));
+ m_mpFuncs.insert(QLatin1String("__msetitem__"), QLatin1String("mp_ass_subscript"));
+}
+
+QString CppGenerator::fileNameSuffix() const
+{
+ return QLatin1String("_wrapper.cpp");
+}
+
+QString CppGenerator::fileNameForContext(const GeneratorContext &context) const
+{
+ const AbstractMetaClass *metaClass = context.metaClass();
+ if (!context.forSmartPointer()) {
+ QString fileNameBase = metaClass->qualifiedCppName().toLower();
+ fileNameBase.replace(QLatin1String("::"), QLatin1String("_"));
+ return fileNameBase + fileNameSuffix();
+ }
+ const AbstractMetaType &smartPointerType = context.preciseType();
+ QString fileNameBase = getFileNameBaseForSmartPointer(smartPointerType, metaClass);
+ return fileNameBase + fileNameSuffix();
+}
+
+QVector<AbstractMetaFunctionList> CppGenerator::filterGroupedOperatorFunctions(const AbstractMetaClass *metaClass,
+ uint queryIn)
+{
+ // ( func_name, num_args ) => func_list
+ QMap<QPair<QString, int>, AbstractMetaFunctionList> results;
+ const AbstractMetaClass::OperatorQueryOptions query(queryIn);
+ const AbstractMetaFunctionList &funcs = metaClass->operatorOverloads(query);
+ for (AbstractMetaFunction *func : funcs) {
+ if (func->isModifiedRemoved()
+ || func->usesRValueReferences()
+ || func->name() == QLatin1String("operator[]")
+ || func->name() == QLatin1String("operator->")
+ || func->name() == QLatin1String("operator!")) {
+ continue;
+ }
+ int args;
+ if (func->isComparisonOperator()) {
+ args = -1;
+ } else {
+ args = func->arguments().size();
+ }
+ QPair<QString, int > op(func->name(), args);
+ results[op].append(func);
+ }
+ QVector<AbstractMetaFunctionList> result;
+ result.reserve(results.size());
+ for (auto it = results.cbegin(), end = results.cend(); it != end; ++it)
+ result.append(it.value());
+ return result;
+}
+
+const AbstractMetaFunction *CppGenerator::boolCast(const AbstractMetaClass *metaClass) const
+{
+ if (!useIsNullAsNbNonZero())
+ return nullptr;
+ // TODO: This could be configurable someday
+ const AbstractMetaFunction *func = metaClass->findFunction(QLatin1String("isNull"));
+ if (!func || func->isVoid() || !func->type().typeEntry()->isPrimitive() || !func->isPublic())
+ return nullptr;
+ auto pte = static_cast<const PrimitiveTypeEntry *>(func->type().typeEntry());
+ while (pte->referencedTypeEntry())
+ pte = pte->referencedTypeEntry();
+ return func && func->isConstant() && pte->name() == QLatin1String("bool")
+ && func->arguments().isEmpty() ? func : nullptr;
+}
+
+AbstractMetaType CppGenerator::findSmartPointerInstantiation(const TypeEntry *entry) const
+{
+ for (const auto &i : instantiatedSmartPointers()) {
+ if (i.instantiations().at(0).typeEntry() == entry)
+ return i;
+ }
+ return {};
+}
+
+using FunctionGroupMap = QMap<QString, AbstractMetaFunctionList>;
+
+// 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 <QDebug>\n";
+
+static QString chopType(QString s)
+{
+ if (s.endsWith(QLatin1String("_Type")))
+ s.chop(5);
+ else if (s.endsWith(QLatin1String("_TypeF()")))
+ s.chop(8);
+ return s;
+}
+
+// Helper for field setters: Check for "const QWidget *" (settable field),
+// but not "int *const" (read-only field).
+static bool isPointerToConst(const AbstractMetaType &t)
+{
+ const AbstractMetaType::Indirections &indirections = t.indirectionsV();
+ return t.isConstant() && !indirections.isEmpty()
+ && indirections.constLast() != Indirection::ConstPointer;
+}
+
+static inline bool canGenerateFieldSetter(const AbstractMetaField *field)
+{
+ const AbstractMetaType &type = field->type();
+ return !type.isConstant() || isPointerToConst(type);
+}
+
+static bool isStdSetterName(QString setterName, QString propertyName)
+{
+ return setterName.size() == propertyName.size() + 3
+ && setterName.startsWith(QLatin1String("set"))
+ && setterName.endsWith(QStringView{propertyName}.right(propertyName.size() - 1))
+ && setterName.at(3) == propertyName.at(0).toUpper();
+}
+
+static QString buildPropertyString(QPropertySpec *spec)
+{
+ QString text;
+ text += QLatin1Char('"');
+ text += spec->name();
+ text += QLatin1Char(':');
+
+ if (spec->read() != spec->name())
+ text += spec->read();
+
+ if (!spec->write().isEmpty()) {
+ text += QLatin1Char(':');
+ if (!isStdSetterName(spec->write(), spec->name()))
+ text += spec->write();
+ }
+
+ text += QLatin1Char('"');
+ return text;
+}
+
+static void writePyGetSetDefEntry(QTextStream &s, const QString &name,
+ const QString &getFunc, const QString &setFunc)
+{
+ s << "{const_cast<char *>(\"" << name << "\"), " << getFunc << ", "
+ << (setFunc.isEmpty() ? QLatin1String(NULL_PTR) : setFunc) << "},\n";
+}
+
+/*!
+ Function used to write the class generated binding code on the buffer
+ \param s the output buffer
+ \param metaClass the pointer to metaclass information
+*/
+void CppGenerator::generateClass(QTextStream &s, const GeneratorContext &classContext)
+{
+ const AbstractMetaClass *metaClass = classContext.metaClass();
+
+ // write license comment
+ s << licenseComment() << Qt::endl;
+
+ if (!avoidProtectedHack() && !metaClass->isNamespace() && !metaClass->hasPrivateDestructor()) {
+ s << "//workaround to access protected functions\n";
+ s << "#define protected public\n\n";
+ }
+
+ // headers
+ s << "// default includes\n";
+ s << "#include <shiboken.h>\n";
+ if (usePySideExtensions()) {
+ s << includeQDebug;
+ s << "#include <pysidesignal.h>\n"
+ << "#include <pysideproperty.h>\n"
+ << "#include <pyside.h>\n"
+ << "#include <pysideqenum.h>\n"
+ << "#include <feature_select.h>\n"
+ << "#include <qapp_macro.h>\n\n"
+ << "QT_WARNING_DISABLE_DEPRECATED\n\n";
+ }
+
+ s << "#include <typeinfo>\n";
+ if (usePySideExtensions() && metaClass->isQObject()) {
+ s << "#include <signalmanager.h>\n";
+ s << "#include <pysidemetafunction.h>\n";
+ }
+
+ // The multiple inheritance initialization function
+ // needs the 'set' class from C++ STL.
+ if (getMultipleInheritingClass(metaClass) != nullptr)
+ s << "#include <algorithm>\n#include <set>\n";
+ if (metaClass->generateExceptionHandling())
+ s << "#include <exception>\n";
+ s << "#include <iterator>\n"; // For containers
+
+ if (wrapperDiagnostics())
+ s << "#include <helper.h>\n#include <iostream>\n";
+
+ s << "\n// module include\n" << "#include \"" << getModuleHeaderFileName() << "\"\n";
+
+ QString headerfile = fileNameForContext(classContext);
+ headerfile.replace(QLatin1String(".cpp"), QLatin1String(".h"));
+ s << "\n// main header\n" << "#include \"" << headerfile << "\"\n";
+
+ s << Qt::endl << "// inner classes\n";
+ const AbstractMetaClassList &innerClasses = metaClass->innerClasses();
+ for (AbstractMetaClass *innerClass : innerClasses) {
+ GeneratorContext innerClassContext = contextForClass(innerClass);
+ if (shouldGenerate(innerClass) && !innerClass->typeEntry()->isSmartPointer()) {
+ QString headerfile = fileNameForContext(innerClassContext);
+ headerfile.replace(QLatin1String(".cpp"), QLatin1String(".h"));
+ s << "#include \"" << headerfile << "\"\n";
+ }
+ }
+
+ AbstractMetaEnumList classEnums = metaClass->enums();
+ metaClass->getEnumsFromInvisibleNamespacesToBeGenerated(&classEnums);
+
+ //Extra includes
+ QVector<Include> includes;
+ if (!classContext.useWrapper())
+ includes += metaClass->typeEntry()->extraIncludes();
+ for (AbstractMetaEnum *cppEnum : qAsConst(classEnums))
+ includes.append(cppEnum->typeEntry()->extraIncludes());
+ if (!includes.isEmpty()) {
+ s << "\n// Extra includes\n";
+ std::sort(includes.begin(), includes.end());
+ for (const Include &inc : qAsConst(includes))
+ s << inc.toString() << Qt::endl;
+ s << '\n';
+ }
+
+ s << "\n#include <cctype>\n#include <cstring>\n";
+
+ if (metaClass->typeEntry()->typeFlags() & ComplexTypeEntry::Deprecated)
+ s << "#Deprecated\n";
+
+ // Use class base namespace
+ {
+ const AbstractMetaClass *context = metaClass->enclosingClass();
+ while (context) {
+ if (context->isNamespace() && !context->enclosingClass()
+ && static_cast<const NamespaceTypeEntry *>(context->typeEntry())->generateUsing()) {
+ s << "\nusing namespace " << context->qualifiedCppName() << ";\n";
+ break;
+ }
+ context = context->enclosingClass();
+ }
+ }
+
+ s << Qt::endl << Qt::endl << typeNameFunc << Qt::endl;
+
+ // Create string literal for smart pointer getter method.
+ if (classContext.forSmartPointer()) {
+ const auto *typeEntry =
+ static_cast<const SmartPointerTypeEntry *>(classContext.preciseType()
+ .typeEntry());
+ QString rawGetter = typeEntry->getter();
+ s << "static const char * " << SMART_POINTER_GETTER << " = \"" << rawGetter << "\";";
+ }
+
+ // class inject-code native/beginning
+ if (!metaClass->typeEntry()->codeSnips().isEmpty()) {
+ writeClassCodeSnips(s, metaClass->typeEntry()->codeSnips(),
+ TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode,
+ classContext);
+ s << Qt::endl;
+ }
+
+ // python conversion rules
+ if (metaClass->typeEntry()->hasTargetConversionRule()) {
+ s << "// Python Conversion\n";
+ s << metaClass->typeEntry()->conversionRule() << Qt::endl;
+ }
+
+ if (classContext.useWrapper()) {
+ s << "// Native ---------------------------------------------------------\n\n";
+
+ if (avoidProtectedHack() && usePySideExtensions()) {
+ s << "void " << classContext.wrapperName() << "::pysideInitQtMetaTypes()\n{\n";
+ Indentation indent(INDENT);
+ writeInitQtMetaTypeFunctionBody(s, classContext);
+ s << "}\n\n";
+ }
+
+ const AbstractMetaFunctionList &funcs = filterFunctions(metaClass);
+ int maxOverrides = 0;
+ writeCacheResetNative(s, classContext);
+ for (const AbstractMetaFunction *func : funcs) {
+ const bool notAbstract = !func->isAbstract();
+ if ((func->isPrivate() && notAbstract && !visibilityModifiedToPrivate(func))
+ || (func->isModifiedRemoved() && notAbstract))
+ continue;
+ if (func->functionType() == AbstractMetaFunction::ConstructorFunction && !func->isUserAdded())
+ writeConstructorNative(s, classContext, func);
+ else if (shouldWriteVirtualMethodNative(func))
+ writeVirtualMethodNative(s, func, maxOverrides++);
+ }
+
+ if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) {
+ if (usePySideExtensions() && metaClass->isQObject())
+ writeMetaObjectMethod(s, classContext);
+ writeDestructorNative(s, classContext);
+ }
+ }
+
+ Indentation indentation(INDENT);
+
+ QString methodsDefinitions;
+ QTextStream md(&methodsDefinitions);
+ QString singleMethodDefinitions;
+ QTextStream smd(&singleMethodDefinitions);
+ QString signaturesString;
+ QTextStream signatureStream(&signaturesString);
+
+ s << "\n// Target ---------------------------------------------------------\n\n"
+ << "extern \"C\" {\n";
+ const auto &functionGroups = getFunctionGroups(metaClass);
+ for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) {
+ AbstractMetaFunctionList overloads;
+ QSet<QString> seenSignatures;
+ bool staticEncountered = false;
+ for (AbstractMetaFunction *func : it.value()) {
+ if (!func->isAssignmentOperator()
+ && !func->usesRValueReferences()
+ && !func->isCastOperator()
+ && !func->isModifiedRemoved()
+ && (!func->isPrivate() || func->functionType() == AbstractMetaFunction::EmptyFunction)
+ && func->ownerClass() == func->implementingClass()
+ && (func->name() != QLatin1String("qt_metacall"))) {
+ // PYSIDE-331: Inheritance works correctly when there are disjoint functions.
+ // But when a function is both in a class and inherited in a subclass,
+ // then we need to search through all subclasses and collect the new signatures.
+ overloads << getFunctionAndInheritedOverloads(func, &seenSignatures);
+ if (func->isStatic())
+ staticEncountered = true;
+ }
+ }
+ // 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) {
+ for (int i = overloads.size() - 1; i >= 0; --i) {
+ if (overloads.at(i)->isStatic())
+ delete overloads.takeAt(i);
+ }
+ }
+
+ if (overloads.isEmpty())
+ continue;
+
+ const AbstractMetaFunction *rfunc = overloads.constFirst();
+ if (m_sequenceProtocol.contains(rfunc->name()) || m_mappingProtocol.contains(rfunc->name()))
+ continue;
+
+ if (rfunc->isConstructor()) {
+ // @TODO: Implement constructor support for smart pointers, so that they can be
+ // instantiated in python code.
+ if (classContext.forSmartPointer())
+ continue;
+ writeConstructorWrapper(s, overloads, classContext);
+ writeSignatureInfo(signatureStream, overloads);
+ }
+ // call operators
+ else if (rfunc->name() == QLatin1String("operator()")) {
+ writeMethodWrapper(s, overloads, classContext);
+ writeSignatureInfo(signatureStream, overloads);
+ }
+ else if (!rfunc->isOperatorOverload()) {
+
+ if (classContext.forSmartPointer()) {
+ const auto *smartPointerTypeEntry =
+ static_cast<const SmartPointerTypeEntry *>(
+ classContext.preciseType().typeEntry());
+
+ if (smartPointerTypeEntry->getter() == rfunc->name()) {
+ // Replace the return type of the raw pointer getter method with the actual
+ // return type.
+ QString innerTypeName =
+ classContext.preciseType().getSmartPointerInnerType().cppSignature();
+ QString pointerToInnerTypeName = innerTypeName + QLatin1Char('*');
+ // @TODO: This possibly leaks, but there are a bunch of other places where this
+ // is done, so this will be fixed in bulk with all the other cases, because the
+ // ownership of the pointers is not clear at the moment.
+ AbstractMetaType pointerToInnerType =
+ buildAbstractMetaTypeFromString(pointerToInnerTypeName);
+
+ AbstractMetaFunction *mutableRfunc = overloads.constFirst();
+ mutableRfunc->setType(pointerToInnerType);
+ } else if (smartPointerTypeEntry->refCountMethodName().isEmpty()
+ || smartPointerTypeEntry->refCountMethodName() != rfunc->name()) {
+ // Skip all public methods of the smart pointer except for the raw getter and
+ // the ref count method.
+ continue;
+ }
+ }
+
+ writeMethodWrapper(s, overloads, classContext);
+ writeSignatureInfo(signatureStream, overloads);
+ if (OverloadData::hasStaticAndInstanceFunctions(overloads)) {
+ QString methDefName = cpythonMethodDefinitionName(rfunc);
+ smd << "static PyMethodDef " << methDefName << " = {\n";
+ smd << INDENT;
+ writeMethodDefinitionEntry(smd, overloads);
+ smd << "\n};\n\n";
+ }
+ writeMethodDefinition(md, overloads);
+ }
+ }
+
+ const QString className = chopType(cpythonTypeName(metaClass));
+
+ if (metaClass->typeEntry()->isValue() || metaClass->typeEntry()->isSmartPointer()) {
+ writeCopyFunction(s, classContext);
+ signatureStream << fullPythonClassName(metaClass) << ".__copy__()\n";
+ }
+
+ // 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";
+ for (const auto &entry : qAsConst(sorter))
+ s << INDENT << entry << ",\n";
+ s << INDENT << NULL_PTR << " // Sentinel\n";
+ s << "};\n\n";
+ }
+
+ // Write methods definition
+ s << "static PyMethodDef " << className << "_methods[] = {\n";
+ s << methodsDefinitions << Qt::endl;
+ if (metaClass->typeEntry()->isValue() || metaClass->typeEntry()->isSmartPointer()) {
+ s << INDENT << "{\"__copy__\", reinterpret_cast<PyCFunction>(" << className << "___copy__)"
+ << ", METH_NOARGS},\n";
+ }
+ s << INDENT << '{' << NULL_PTR << ", " << NULL_PTR << "} // Sentinel\n";
+ s << "};\n\n";
+
+ // Write tp_s/getattro function
+ const AttroCheck attroCheck = checkAttroFunctionNeeds(metaClass);
+ if (attroCheck.testFlag(AttroCheckFlag::GetattroSmartPointer)) {
+ writeSmartPointerGetattroFunction(s, classContext);
+ writeSmartPointerSetattroFunction(s, classContext);
+ } else {
+ if ((attroCheck & AttroCheckFlag::GetattroMask) != 0)
+ writeGetattroFunction(s, attroCheck, classContext);
+ if ((attroCheck & AttroCheckFlag::SetattroMask) != 0)
+ writeSetattroFunction(s, attroCheck, classContext);
+ }
+
+ if (const AbstractMetaFunction *f = boolCast(metaClass)) {
+ ErrorCode errorCode(-1);
+ s << "static int " << cpythonBaseName(metaClass) << "___nb_bool(PyObject *self)\n"
+ << "{\n";
+ writeCppSelfDefinition(s, classContext);
+ if (f->allowThread()) {
+ s << INDENT << "int result;\n";
+ s << INDENT << BEGIN_ALLOW_THREADS << Qt::endl;
+ s << INDENT << "result = !" << CPP_SELF_VAR << "->isNull();\n";
+ s << INDENT << END_ALLOW_THREADS << Qt::endl;
+ s << INDENT << "return result;\n";
+ } else {
+ s << INDENT << "return !" << CPP_SELF_VAR << "->isNull();\n";
+ }
+ s << "}\n\n";
+ }
+
+ if (supportsNumberProtocol(metaClass) && !metaClass->typeEntry()->isSmartPointer()) {
+ const QVector<AbstractMetaFunctionList> opOverloads = filterGroupedOperatorFunctions(
+ metaClass,
+ AbstractMetaClass::ArithmeticOp
+ | AbstractMetaClass::LogicalOp
+ | AbstractMetaClass::BitwiseOp);
+
+ for (const AbstractMetaFunctionList &allOverloads : opOverloads) {
+ AbstractMetaFunctionList overloads;
+ for (AbstractMetaFunction *func : allOverloads) {
+ if (!func->isModifiedRemoved()
+ && !func->isPrivate()
+ && (func->ownerClass() == func->implementingClass() || func->isAbstract()))
+ overloads.append(func);
+ }
+
+ if (overloads.isEmpty())
+ continue;
+
+ writeMethodWrapper(s, overloads, classContext);
+ writeSignatureInfo(signatureStream, overloads);
+ }
+ }
+
+ if (supportsSequenceProtocol(metaClass)) {
+ writeSequenceMethods(s, metaClass, classContext);
+ }
+
+ if (supportsMappingProtocol(metaClass)) {
+ writeMappingMethods(s, metaClass, classContext);
+ }
+
+ if (!metaClass->isNamespace() && metaClass->hasComparisonOperatorOverload()) {
+ s << "// Rich comparison\n";
+ writeRichCompareFunction(s, classContext);
+ }
+
+ if (shouldGenerateGetSetList(metaClass) && !classContext.forSmartPointer()) {
+ const AbstractMetaFieldList &fields = metaClass->fields();
+ for (const AbstractMetaField *metaField : fields) {
+ if (metaField->isStatic())
+ continue;
+ writeGetterFunction(s, metaField, classContext);
+ if (canGenerateFieldSetter(metaField))
+ writeSetterFunction(s, metaField, classContext);
+ s << Qt::endl;
+ }
+
+ 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() << Qt::endl;
+ s << "static PyGetSetDef " << cpythonGettersSettersDefinitionName(metaClass) << "[] = {\n";
+ for (const AbstractMetaField *metaField : fields) {
+ if (!metaField->isStatic()) {
+ s << INDENT;
+ const QString setter = canGenerateFieldSetter(metaField)
+ ? cpythonSetterFunctionName(metaField) : QString();
+ writePyGetSetDefEntry(s, metaField->name(),
+ cpythonGetterFunctionName(metaField), setter);
+ }
+ }
+
+ for (const QPropertySpec *property : metaClass->propertySpecs()) {
+ if (property->generateGetSetDef() || !usePySideExtensions()) {
+ s << INDENT;
+ const QString setter = property->hasWrite()
+ ? cpythonSetterFunctionName(property, metaClass) : QString();
+ writePyGetSetDefEntry(s, property->name(),
+ cpythonGetterFunctionName(property, metaClass), setter);
+ }
+ }
+ s << INDENT << '{' << NULL_PTR << "} // Sentinel\n";
+ s << "};\n\n";
+ }
+
+ s << "} // extern \"C\"\n\n";
+
+ if (!metaClass->typeEntry()->hashFunction().isEmpty())
+ writeHashFunction(s, classContext);
+
+ // Write tp_traverse and tp_clear functions.
+ writeTpTraverseFunction(s, metaClass);
+ writeTpClearFunction(s, metaClass);
+
+ writeClassDefinition(s, metaClass, classContext);
+ s << Qt::endl;
+
+ if (metaClass->isPolymorphic() && metaClass->baseClass())
+ writeTypeDiscoveryFunction(s, metaClass);
+
+ writeFlagsNumberMethodsDefinitions(s, classEnums);
+ s << Qt::endl;
+
+ writeConverterFunctions(s, metaClass, classContext);
+ writeClassRegister(s, metaClass, classContext, signatureStream);
+
+ // class inject-code native/end
+ if (!metaClass->typeEntry()->codeSnips().isEmpty()) {
+ writeClassCodeSnips(s, metaClass->typeEntry()->codeSnips(),
+ TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode,
+ classContext);
+ s << Qt::endl;
+ }
+}
+
+void CppGenerator::writeCacheResetNative(QTextStream &s, const GeneratorContext &classContext)
+{
+ Indentation indentation(INDENT);
+ s << "void " << classContext.wrapperName()
+ << "::resetPyMethodCache()\n{\n";
+ s << INDENT << "std::fill_n(m_PyMethodCache, sizeof(m_PyMethodCache) / sizeof(m_PyMethodCache[0]), false);\n";
+ s << "}\n\n";
+}
+
+void CppGenerator::writeConstructorNative(QTextStream &s, const GeneratorContext &classContext,
+ const AbstractMetaFunction *func)
+{
+ Indentation indentation(INDENT);
+ const QString qualifiedName = classContext.wrapperName() + QLatin1String("::");
+ s << functionSignature(func, qualifiedName, QString(),
+ OriginalTypeDescription | SkipDefaultValues);
+ s << " : ";
+ writeFunctionCall(s, func);
+ s << "\n{\n";
+ if (wrapperDiagnostics())
+ s << INDENT << R"(std::cerr << __FUNCTION__ << ' ' << this << '\n';)" << '\n';
+ const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast();
+ s << INDENT << "resetPyMethodCache();\n";
+ writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode, func, lastArg);
+ s << INDENT << "// ... middle\n";
+ writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode, func, lastArg);
+ s << "}\n\n";
+}
+
+void CppGenerator::writeDestructorNative(QTextStream &s, const GeneratorContext &classContext)
+{
+ Indentation indentation(INDENT);
+ s << classContext.wrapperName() << "::~"
+ << classContext.wrapperName() << "()\n{\n";
+ if (wrapperDiagnostics())
+ s << INDENT << R"(std::cerr << __FUNCTION__ << ' ' << this << '\n';)" << '\n';
+ // kill pyobject
+ s << INDENT << "SbkObject *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(this);\n";
+ s << INDENT << "Shiboken::Object::destroy(wrapper, this);\n";
+ s << "}\n";
+}
+
+static bool allArgumentsRemoved(const AbstractMetaFunction *func)
+{
+ if (func->arguments().isEmpty())
+ return false;
+ const AbstractMetaArgumentList &arguments = func->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+ if (!func->argumentRemoved(arg.argumentIndex() + 1))
+ return false;
+ }
+ return true;
+}
+
+QString CppGenerator::getVirtualFunctionReturnTypeName(const AbstractMetaFunction *func)
+{
+ if (!func->type())
+ return QLatin1String("\"\"");
+
+ if (!func->typeReplaced(0).isEmpty())
+ return QLatin1Char('"') + func->typeReplaced(0) + QLatin1Char('"');
+
+ // SbkType would return null when the type is a container.
+ auto typeEntry = func->type().typeEntry();
+ if (typeEntry->isContainer()) {
+ return QLatin1Char('"')
+ + reinterpret_cast<const ContainerTypeEntry *>(typeEntry)->typeName()
+ + QLatin1Char('"');
+ }
+ if (typeEntry->isSmartPointer())
+ return QLatin1Char('"') + typeEntry->qualifiedCppName() + QLatin1Char('"');
+
+ if (avoidProtectedHack()) {
+ const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(func->type());
+ if (metaEnum && metaEnum->isProtected())
+ return QLatin1Char('"') + protectedEnumSurrogateName(metaEnum) + QLatin1Char('"');
+ }
+
+ if (func->type().isPrimitive())
+ return QLatin1Char('"') + func->type().name() + QLatin1Char('"');
+
+ return QLatin1String("reinterpret_cast<PyTypeObject *>(Shiboken::SbkType< ")
+ + typeEntry->qualifiedCppName() + QLatin1String(" >())->tp_name");
+}
+
+// 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(QTextStream &s,
+ const AbstractMetaFunction *func,
+ const QString &funcName,
+ const CodeSnipList &snips,
+ const AbstractMetaArgument *lastArg,
+ const TypeEntry *retType,
+ const QString &returnStatement)
+{
+ if (!snips.isEmpty()) {
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning,
+ TypeSystem::ShellCode, func, lastArg);
+ }
+
+ if (func->isAbstract()) {
+ s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '"
+ << func->ownerClass()->name() << '.' << funcName
+ << "()' not implemented.\");\n"
+ << INDENT << returnStatement << '\n';
+ return;
+ }
+
+ s << INDENT;
+ 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, lastArg);
+ }
+ s << INDENT << "return;\n";
+}
+
+// Determine the return statement (void or a result value).
+QString CppGenerator::virtualMethodReturn(QTextStream &s,
+ const AbstractMetaFunction *func,
+ const FunctionModificationList &functionModifications)
+{
+ if (func->isVoid())
+ return QLatin1String("return;");
+ 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(QStringLiteral("%(\\d+)"));
+ 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().count()) {
+ 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);
+ return QLatin1String("return ") + defaultReturnExpr.returnValue()
+ + QLatin1Char(';');
+ }
+ }
+ }
+ const DefaultValue defaultReturnExpr = minimalConstructor(returnType);
+ if (!defaultReturnExpr.isValid()) {
+ QString errorMsg = QLatin1String(__FUNCTION__) + QLatin1String(": ");
+ if (const AbstractMetaClass *c = func->implementingClass())
+ errorMsg += c->qualifiedCppName() + QLatin1String("::");
+ errorMsg += func->signature();
+ errorMsg = msgCouldNotFindMinimalConstructor(errorMsg, func->type().cppSignature());
+ qCWarning(lcShiboken).noquote().nospace() << errorMsg;
+ s << Qt::endl << INDENT << "#error " << errorMsg << Qt::endl;
+ }
+ if (returnType.referenceType() == LValueReference) {
+ s << INDENT << "static " << returnType.typeEntry()->qualifiedCppName()
+ << " result;\n";
+ return QLatin1String("return result;");
+ }
+ return QLatin1String("return ") + defaultReturnExpr.returnValue()
+ + QLatin1Char(';');
+}
+
+void CppGenerator::writeVirtualMethodNative(QTextStream &s,
+ const AbstractMetaFunction *func,
+ int cacheIndex)
+{
+ //skip metaObject function, this will be written manually ahead
+ if (usePySideExtensions() && func->ownerClass() && func->ownerClass()->isQObject() &&
+ ((func->name() == QLatin1String("metaObject")) || (func->name() == QLatin1String("qt_metacall"))))
+ return;
+
+ const TypeEntry *retType = func->type().typeEntry();
+ const QString funcName = func->isOperatorOverload() ? pythonOperatorFunctionName(func) : func->name();
+
+ QString prefix = wrapperName(func->ownerClass()) + QLatin1String("::");
+ s << functionSignature(func, prefix, QString(), Generator::SkipDefaultValues|Generator::OriginalTypeDescription)
+ << "\n{\n";
+
+ Indentation indentation(INDENT);
+
+ const FunctionModificationList &functionModifications = func->modifications();
+
+ const QString returnStatement = virtualMethodReturn(s, func, functionModifications);
+
+ if (func->isAbstract() && func->isModifiedRemoved()) {
+ qCWarning(lcShiboken).noquote().nospace()
+ << QString::fromLatin1("Pure virtual method '%1::%2' must be implement but was "\
+ "completely removed on type system.")
+ .arg(func->ownerClass()->name(), func->minimalSignature());
+ s << INDENT << returnStatement << "\n}\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, lastArg);
+ }
+
+ if (wrapperDiagnostics()) {
+ s << INDENT << "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() || func->isAbstract();
+ s << INDENT << "if (m_PyMethodCache[" << cacheIndex << "])" << (multi_line ? " {\n" : "\n");
+ {
+ Indentation indentation(INDENT);
+ writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType,
+ returnStatement);
+ }
+ if (multi_line)
+ s << INDENT << "}\n";
+
+ s << INDENT << "Shiboken::GilState gil;\n";
+
+ // Get out of virtual method call if someone already threw an error.
+ s << INDENT << "if (PyErr_Occurred())\n" << indent(INDENT)
+ << INDENT << returnStatement << '\n' << outdent(INDENT);
+
+ //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)
+ propStr = QString::number(propFlag) + QLatin1Char(':');
+
+ s << INDENT << "static PyObject *nameCache[2] = {};\n";
+ if (propFlag)
+ s << INDENT << "// This method belongs to a property.\n";
+ s << INDENT << "static const char *funcName = \"" << propStr << funcName << "\";\n";
+ s << INDENT << "Shiboken::AutoDecRef " << PYTHON_OVERRIDE_VAR
+ << "(Shiboken::BindingManager::instance().getOverride(this, nameCache, funcName));\n";
+ s << INDENT << "if (" << PYTHON_OVERRIDE_VAR << ".isNull()) {\n"
+ << indent(INDENT) << INDENT << "gil.release();\n";
+ if (useOverrideCaching(func->ownerClass()))
+ s << INDENT << "m_PyMethodCache[" << cacheIndex << "] = true;\n";
+ writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType,
+ returnStatement);
+ s << outdent(INDENT) << INDENT << "}\n\n"; //WS
+
+ writeConversionRule(s, func, TypeSystem::TargetLangCode);
+
+ s << INDENT << "Shiboken::AutoDecRef " << PYTHON_ARGS << "(";
+
+ if (func->arguments().isEmpty() || allArgumentsRemoved(func)) {
+ s << "PyTuple_New(0));\n";
+ } else {
+ QStringList argConversions;
+ const AbstractMetaArgumentList &arguments = func->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+ if (func->argumentRemoved(arg.argumentIndex() + 1))
+ continue;
+
+ QString argConv;
+ QTextStream ac(&argConv);
+ const auto &argType = arg.type();
+ auto argTypeEntry = static_cast<const PrimitiveTypeEntry *>(argType.typeEntry());
+ bool convert = argTypeEntry->isObject()
+ || argTypeEntry->isValue()
+ || argType.isValuePointer()
+ || argType.isNativePointer()
+ || argTypeEntry->isFlags()
+ || argTypeEntry->isEnum()
+ || argTypeEntry->isContainer()
+ || argType.referenceType() == LValueReference;
+
+ if (!convert && argTypeEntry->isPrimitive()) {
+ if (argTypeEntry->basicReferencedTypeEntry())
+ argTypeEntry = argTypeEntry->basicReferencedTypeEntry();
+ convert = !m_formatUnits.contains(argTypeEntry->name());
+ }
+
+ Indentor nested;
+ Indentation indentation(nested);
+ ac << nested;
+ if (!func->conversionRule(TypeSystem::TargetLangCode, arg.argumentIndex() + 1).isEmpty()) {
+ // Has conversion rule.
+ ac << arg.name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX);
+ } else {
+ QString argName = arg.name();
+ if (convert)
+ writeToPythonConversion(ac, arg.type(), func->ownerClass(), argName);
+ else
+ ac << argName;
+ }
+
+ argConversions << argConv;
+ }
+
+ s << "Py_BuildValue(\"(" << getFormatUnitString(func, false) << ")\",\n";
+ s << argConversions.join(QLatin1String(",\n")) << Qt::endl;
+ s << INDENT << "));\n";
+ }
+
+ bool invalidateReturn = false;
+ QSet<int> invalidateArgs;
+ for (const FunctionModification &funcMod : functionModifications) {
+ for (const ArgumentModification &argMod : funcMod.argument_mods) {
+ if (argMod.resetAfterUse && !invalidateArgs.contains(argMod.index)) {
+ invalidateArgs.insert(argMod.index);
+ s << INDENT << "bool invalidateArg" << argMod.index;
+ s << " = PyTuple_GET_ITEM(" << PYTHON_ARGS << ", " << argMod.index - 1 << ")->ob_refcnt == 1;\n";
+ } else if (argMod.index == 0 && argMod.ownerships[TypeSystem::TargetLangCode] == TypeSystem::CppOwnership) {
+ invalidateReturn = true;
+ }
+ }
+ }
+ s << Qt::endl;
+
+ if (!snips.isEmpty()) {
+ if (injectedCodeUsesPySelf(func))
+ s << INDENT << "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, lastArg);
+ }
+
+ if (!injectedCodeCallsPythonOverride(func)) {
+ s << INDENT;
+ s << "Shiboken::AutoDecRef " << PYTHON_RETURN_VAR << "(PyObject_Call("
+ << PYTHON_OVERRIDE_VAR << ", " << PYTHON_ARGS << ", nullptr));\n";
+
+ s << INDENT << "// An error happened in python code!\n";
+ s << INDENT << "if (" << PYTHON_RETURN_VAR << ".isNull()) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "PyErr_Print();\n";
+ s << INDENT << returnStatement << '\n';
+ }
+ s << INDENT << "}\n";
+
+ if (!func->isVoid()) {
+ if (invalidateReturn)
+ s << INDENT << "bool invalidateArg0 = " << PYTHON_RETURN_VAR << "->ob_refcnt == 1;\n";
+
+ if (func->typeReplaced(0) != QLatin1String("PyObject")) {
+
+ s << INDENT << "// Check return type\n";
+ s << INDENT;
+ if (func->typeReplaced(0).isEmpty()) {
+ s << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << " = " << cpythonIsConvertibleFunction(func->type());
+ s << PYTHON_RETURN_VAR << ");\n";
+ s << INDENT << "if (!" << PYTHON_TO_CPP_VAR << ") {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "Shiboken::warning(PyExc_RuntimeWarning, 2, "\
+ "\"Invalid return value in function %s, expected %s, got %s.\", \"";
+ s << func->ownerClass()->name() << '.' << funcName << "\", " << getVirtualFunctionReturnTypeName(func);
+ s << ", Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);\n";
+ s << INDENT << returnStatement << '\n';
+ }
+ s << INDENT << "}\n";
+
+ } else {
+
+ s << INDENT << "// Check return type\n";
+ s << INDENT << "bool typeIsValid = ";
+ writeTypeCheck(s, func->type(), QLatin1String(PYTHON_RETURN_VAR),
+ isNumber(func->type().typeEntry()), func->typeReplaced(0));
+ s << ";\n";
+ s << INDENT << "if (!typeIsValid";
+ if (isPointerToWrapperType(func->type()))
+ s << " && " << PYTHON_RETURN_VAR << " != Py_None";
+ s << ") {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "Shiboken::warning(PyExc_RuntimeWarning, 2, "\
+ "\"Invalid return value in function %s, expected %s, got %s.\", \"";
+ s << func->ownerClass()->name() << '.' << funcName << "\", " << getVirtualFunctionReturnTypeName(func);
+ s << ", Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);\n";
+ s << INDENT << returnStatement << '\n';
+ }
+ s << INDENT << "}\n";
+
+ }
+ }
+
+ if (!func->conversionRule(TypeSystem::NativeCode, 0).isEmpty()) {
+ // Has conversion rule.
+ writeConversionRule(s, func, TypeSystem::NativeCode, QLatin1String(CPP_RETURN_VAR));
+ } else if (!injectedCodeHasReturnValueAttribution(func, TypeSystem::NativeCode)) {
+ writePythonToCppTypeConversion(s, func->type(), QLatin1String(PYTHON_RETURN_VAR),
+ QLatin1String(CPP_RETURN_VAR), func->implementingClass());
+ }
+ }
+ }
+
+ if (invalidateReturn) {
+ s << INDENT << "if (invalidateArg0)\n" << indent(INDENT)
+ << INDENT << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR
+ << ".object());\n" << outdent(INDENT);
+ }
+ for (int argIndex : qAsConst(invalidateArgs)) {
+ s << INDENT << "if (invalidateArg" << argIndex << ")\n" << indent(INDENT)
+ << INDENT << "Shiboken::Object::invalidate(PyTuple_GET_ITEM(" << PYTHON_ARGS
+ << ", " << (argIndex - 1) << "));\n" << outdent(INDENT);
+ }
+
+
+ for (const FunctionModification &funcMod : functionModifications) {
+ for (const ArgumentModification &argMod : funcMod.argument_mods) {
+ if (argMod.ownerships.contains(TypeSystem::NativeCode)
+ && argMod.index == 0 && argMod.ownerships[TypeSystem::NativeCode] == TypeSystem::CppOwnership) {
+ s << INDENT << "if (Shiboken::Object::checkType(" << PYTHON_RETURN_VAR << "))\n";
+ Indentation indent(INDENT);
+ s << INDENT << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR << ");\n";
+ }
+ }
+ }
+
+ if (func->hasInjectedCode()) {
+ s << Qt::endl;
+ const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast();
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode, func, lastArg);
+ }
+
+ if (!func->isVoid()) {
+ s << INDENT << "return ";
+ if (avoidProtectedHack() && retType->isEnum()) {
+ const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(retType);
+ bool isProtectedEnum = metaEnum && metaEnum->isProtected();
+ if (isProtectedEnum) {
+ QString typeCast;
+ if (metaEnum->enclosingClass())
+ typeCast += QLatin1String("::") + metaEnum->enclosingClass()->qualifiedCppName();
+ typeCast += QLatin1String("::") + metaEnum->name();
+ s << '(' << typeCast << ')';
+ }
+ }
+ if (func->type().referenceType() == LValueReference && !isPointer(func->type()))
+ s << " *";
+ s << CPP_RETURN_VAR << ";\n";
+ }
+
+ s<< "}\n\n";
+}
+
+void CppGenerator::writeMetaObjectMethod(QTextStream &s, const GeneratorContext &classContext)
+{
+ Indentation indentation(INDENT);
+ 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)\n"
+ << INDENT << INDENT << "return QObject::d_ptr->dynamicMetaObject();\n";
+ s << INDENT << "SbkObject *pySelf = Shiboken::BindingManager::instance().retrieveWrapper(this);\n";
+ s << INDENT << "if (pySelf == nullptr)\n";
+ s << INDENT << INDENT << "return " << qualifiedCppName << "::metaObject();\n";
+ s << INDENT << "return PySide::SignalManager::retrieveMetaObject(reinterpret_cast<PyObject *>(pySelf));\n";
+ s<< "}\n\n";
+
+ // qt_metacall function
+ s << "int " << wrapperClassName << "::qt_metacall(QMetaObject::Call call, int id, void **args)\n";
+ s << "{\n";
+
+ AbstractMetaFunction *func = nullptr;
+ AbstractMetaFunctionList list =
+ classContext.metaClass()->queryFunctionsByName(QLatin1String("qt_metacall"));
+ if (list.size() == 1)
+ func = list[0];
+
+ CodeSnipList snips;
+ if (func) {
+ snips = func->injectedCodeSnips();
+ if (func->isUserAdded()) {
+ CodeSnipList snips = func->injectedCodeSnips();
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionAny, TypeSystem::NativeCode, func);
+ }
+ }
+
+ s << INDENT << "int result = " << qualifiedCppName << "::qt_metacall(call, id, args);\n";
+ s << INDENT << "return result < 0 ? result : PySide::SignalManager::qt_metacall(this, call, id, args);\n";
+ s << "}\n\n";
+
+ // qt_metacast function
+ writeMetaCast(s, classContext);
+}
+
+void CppGenerator::writeMetaCast(QTextStream &s, const GeneratorContext &classContext)
+{
+ Indentation indentation(INDENT);
+ const QString wrapperClassName = classContext.wrapperName();
+ const QString qualifiedCppName = classContext.metaClass()->qualifiedCppName();
+ s << "void *" << wrapperClassName << "::qt_metacast(const char *_clname)\n{\n";
+ s << INDENT << "if (!_clname) return {};\n";
+ s << INDENT << "SbkObject *pySelf = Shiboken::BindingManager::instance().retrieveWrapper(this);\n";
+ s << INDENT << "if (pySelf && PySide::inherits(Py_TYPE(pySelf), _clname))\n";
+ s << INDENT << INDENT << "return static_cast<void *>(const_cast< " << wrapperClassName << " *>(this));\n";
+ s << INDENT << "return " << qualifiedCppName << "::qt_metacast(_clname);\n";
+ s << "}\n\n";
+}
+
+void CppGenerator::writeEnumConverterFunctions(QTextStream &s, const AbstractMetaEnum *metaEnum)
+{
+ if (metaEnum->isPrivate() || metaEnum->isAnonymous())
+ return;
+ writeEnumConverterFunctions(s, metaEnum->typeEntry());
+}
+
+void CppGenerator::writeEnumConverterFunctions(QTextStream &s, const TypeEntry *enumType)
+{
+ if (!enumType)
+ return;
+ QString typeName = fixedCppTypeName(enumType);
+ QString enumPythonType = cpythonTypeNameExt(enumType);
+ QString cppTypeName = getFullTypeName(enumType).trimmed();
+ if (avoidProtectedHack()) {
+ const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(enumType);
+ if (metaEnum && metaEnum->isProtected())
+ cppTypeName = protectedEnumSurrogateName(metaEnum);
+ }
+ QString code;
+ QTextStream c(&code);
+ Indentor nested;
+ c << nested << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n"
+ << nested << " ";
+ if (enumType->isFlags())
+ c << cppTypeName << "(QFlag(int(PySide::QFlags::getValue(reinterpret_cast<PySideQFlagsObject *>(pyIn)))))";
+ else
+ c << "static_cast<" << cppTypeName << ">(Shiboken::Enum::getValue(pyIn))";
+ c << ";\n";
+ writePythonToCppFunction(s, code, typeName, typeName);
+
+ QString pyTypeCheck = QStringLiteral("PyObject_TypeCheck(pyIn, %1)").arg(enumPythonType);
+ writeIsPythonConvertibleToCppFunction(s, typeName, typeName, pyTypeCheck);
+
+ code.clear();
+
+ c << nested << "const int castCppIn = int(*reinterpret_cast<const "
+ << cppTypeName << " *>(cppIn));\n";
+ c << nested;
+ c << "return ";
+ if (enumType->isFlags()) {
+ c << "reinterpret_cast<PyObject *>(PySide::QFlags::newObject(castCppIn, "
+ << enumPythonType << "))";
+ } else {
+ c << "Shiboken::Enum::newItem(" << enumPythonType << ", castCppIn)";
+ }
+ c << ";\n";
+ writeCppToPythonFunction(s, code, typeName, typeName);
+ s << Qt::endl;
+
+ if (enumType->isFlags())
+ return;
+
+ auto flags = reinterpret_cast<const EnumTypeEntry *>(enumType)->flags();
+ if (!flags)
+ return;
+
+ // QFlags part.
+
+ writeEnumConverterFunctions(s, flags);
+
+ code.clear();
+ cppTypeName = getFullTypeName(flags).trimmed();
+ c << nested << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n"
+ << nested << " " << cppTypeName
+ << "(QFlag(int(Shiboken::Enum::getValue(pyIn))));\n";
+
+ QString flagsTypeName = fixedCppTypeName(flags);
+ writePythonToCppFunction(s, code, typeName, flagsTypeName);
+ writeIsPythonConvertibleToCppFunction(s, typeName, flagsTypeName, pyTypeCheck);
+
+ code.clear();
+ c << nested << "Shiboken::AutoDecRef pyLong(PyNumber_Long(pyIn));\n";
+ c << nested << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n"
+ << nested << " " << cppTypeName
+ << "(QFlag(int(PyLong_AsLong(pyLong.object()))));\n";
+ // PYSIDE-898: Include an additional condition to detect if the type of the
+ // enum corresponds to the object that is being evaluated.
+ // Using only `PyNumber_Check(...)` is too permissive,
+ // then we would have been unable to detect the difference between
+ // a PolarOrientation and Qt::AlignmentFlag, which was the main
+ // issue of the bug.
+ const QString numberCondition = QStringLiteral("PyNumber_Check(pyIn) && ") + pyTypeCheck;
+ writePythonToCppFunction(s, code, QLatin1String("number"), flagsTypeName);
+ writeIsPythonConvertibleToCppFunction(s, QLatin1String("number"), flagsTypeName, numberCondition);
+
+
+}
+
+void CppGenerator::writeConverterFunctions(QTextStream &s, const AbstractMetaClass *metaClass,
+ const GeneratorContext &classContext)
+{
+ s << "// Type conversion functions.\n\n";
+
+ AbstractMetaEnumList classEnums = metaClass->enums();
+ metaClass->getEnumsFromInvisibleNamespacesToBeGenerated(&classEnums);
+ if (!classEnums.isEmpty())
+ s << "// Python to C++ enum conversion.\n";
+ for (const AbstractMetaEnum *metaEnum : qAsConst(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() + QLatin1String("_PTR");
+ QString code;
+ QTextStream c(&code);
+ Indentor nested;
+ c << nested << "Shiboken::Conversions::pythonToCppPointer(" << cpythonType << ", pyIn, cppOut);";
+ writePythonToCppFunction(s, code, sourceTypeName, targetTypeName);
+
+ // "Is convertible" function for the Python object to C++ pointer conversion.
+ const QString pyTypeCheck = QLatin1String("PyObject_TypeCheck(pyIn, reinterpret_cast<PyTypeObject *>(")
+ + cpythonType + QLatin1String("))");
+ writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, pyTypeCheck, QString(), true);
+ s << Qt::endl;
+
+ // 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";
+ code.clear();
+ if (usePySideExtensions() && metaClass->isQObject())
+ {
+ c << nested << "return PySide::getWrapperForQObject(reinterpret_cast<"
+ << typeName << " *>(const_cast<void *>(cppIn)), " << cpythonType << ");\n";
+ } else {
+ c << nested << "auto pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));\n";
+ c << nested << "if (pyOut) {\n";
+ {
+ Indentation indent(nested);
+ c << nested << "Py_INCREF(pyOut);\n";
+ c << nested << "return pyOut;\n";
+ }
+ c << nested << "}\n";
+ c << nested << "bool changedTypeName = false;\n"
+ << nested << "auto tCppIn = reinterpret_cast<const " << typeName << " *>(cppIn);\n"
+ << nested << "const char *typeName = typeid(*tCppIn).name();\n"
+ << nested << "auto sbkType = Shiboken::ObjectType::typeForTypeName(typeName);\n"
+ << nested << "if (sbkType && Shiboken::ObjectType::hasSpecialCastFunction(sbkType)) {\n"
+ << nested << " typeName = typeNameOf(tCppIn);\n"
+ << nested << " changedTypeName = true;\n"
+ << nested << "}\n"
+ << nested << "PyObject *result = Shiboken::Object::newObject(" << cpythonType
+ << ", const_cast<void *>(cppIn), false, /* exactType */ changedTypeName, typeName);\n"
+ << nested << "if (changedTypeName)\n"
+ << nested << " delete [] typeName;\n"
+ << nested << "return result;";
+ }
+ std::swap(targetTypeName, sourceTypeName);
+ writeCppToPythonFunction(s, code, sourceTypeName, targetTypeName);
+
+ // The conversions for an Object Type end here.
+ if (!metaClass->typeEntry()->isValue() && !metaClass->typeEntry()->isSmartPointer()) {
+ s << Qt::endl;
+ return;
+ }
+
+ // Always copies C++ value (not pointer, and not reference) to a new Python wrapper.
+ s << Qt::endl << "// C++ to Python copy conversion.\n";
+ if (!classContext.forSmartPointer())
+ targetTypeName = metaClass->name();
+ else
+ targetTypeName = classContext.preciseType().name();
+
+ sourceTypeName = targetTypeName + QLatin1String("_COPY");
+
+ code.clear();
+
+ QString computedWrapperName;
+ if (!classContext.forSmartPointer()) {
+ computedWrapperName = classContext.useWrapper()
+ ? classContext.wrapperName() : metaClass->qualifiedCppName();
+ } else {
+ computedWrapperName = classContext.smartPointerWrapperName();
+ }
+
+ c << nested << "return Shiboken::Object::newObject(" << cpythonType
+ << ", new ::" << computedWrapperName << "(*reinterpret_cast<const "
+ << typeName << " *>(cppIn)), true, true);";
+ writeCppToPythonFunction(s, code, sourceTypeName, targetTypeName);
+ s << Qt::endl;
+
+ // Python to C++ copy conversion.
+ s << "// Python to C++ copy conversion.\n";
+ if (!classContext.forSmartPointer())
+ sourceTypeName = metaClass->name();
+ else
+ sourceTypeName = classContext.preciseType().name();
+
+ targetTypeName = QStringLiteral("%1_COPY").arg(sourceTypeName);
+ code.clear();
+
+ QString pyInVariable = QLatin1String("pyIn");
+ QString wrappedCPtrExpression;
+ if (!classContext.forSmartPointer())
+ wrappedCPtrExpression = cpythonWrapperCPtr(metaClass->typeEntry(), pyInVariable);
+ else
+ wrappedCPtrExpression = cpythonWrapperCPtr(classContext.preciseType(), pyInVariable);
+
+ c << nested << "*reinterpret_cast<" << typeName << " *>(cppOut) = *"
+ << wrappedCPtrExpression << ';';
+ writePythonToCppFunction(s, code, sourceTypeName, targetTypeName);
+
+ // "Is convertible" function for the Python object to C++ value copy conversion.
+ writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, pyTypeCheck);
+ s << Qt::endl;
+
+ // User provided implicit conversions.
+ CustomConversion *customConversion = metaClass->typeEntry()->customConversion();
+
+ // Implicit conversions.
+ AbstractMetaFunctionList implicitConvs;
+ if (!customConversion || !customConversion->replaceOriginalTargetToNativeConversions()) {
+ const AbstractMetaFunctionList &allImplicitConvs = implicitConversions(metaClass->typeEntry());
+ for (AbstractMetaFunction *func : allImplicitConvs) {
+ if (!func->isUserAdded())
+ implicitConvs << func;
+ }
+ }
+
+ if (!implicitConvs.isEmpty())
+ s << "// Implicit conversions.\n";
+
+ AbstractMetaType targetType = buildAbstractMetaTypeFromAbstractMetaClass(metaClass);
+ for (const AbstractMetaFunction *conv : qAsConst(implicitConvs)) {
+ if (conv->isModifiedRemoved())
+ continue;
+
+ QString typeCheck;
+ QString toCppConv;
+ QString toCppPreConv;
+ if (conv->isConversionOperator()) {
+ const AbstractMetaClass *sourceClass = conv->ownerClass();
+ typeCheck = QStringLiteral("PyObject_TypeCheck(pyIn, %1)").arg(cpythonTypeNameExt(sourceClass->typeEntry()));
+ toCppConv = QLatin1Char('*') + cpythonWrapperCPtr(sourceClass->typeEntry(), QLatin1String("pyIn"));
+ } else {
+ // Constructor that does implicit conversion.
+ if (!conv->typeReplaced(1).isEmpty() || conv->isModifiedToArray(1))
+ continue;
+ const AbstractMetaType sourceType = conv->arguments().constFirst().type();
+ typeCheck = cpythonCheckFunction(sourceType);
+ bool isUserPrimitiveWithoutTargetLangName = isUserPrimitive(sourceType)
+ && sourceType.typeEntry()->targetLangApiName() == sourceType.typeEntry()->name();
+ if (!isWrapperType(sourceType)
+ && !isUserPrimitiveWithoutTargetLangName
+ && !sourceType.typeEntry()->isEnum()
+ && !sourceType.typeEntry()->isFlags()
+ && !sourceType.typeEntry()->isContainer()) {
+ typeCheck += QLatin1Char('(');
+ }
+ if (isWrapperType(sourceType)) {
+ typeCheck += QLatin1String("pyIn)");
+ toCppConv = (sourceType.referenceType() == LValueReference || !isPointerToWrapperType(sourceType))
+ ? QLatin1String(" *") : QString();
+ toCppConv += cpythonWrapperCPtr(sourceType.typeEntry(), QLatin1String("pyIn"));
+ } else if (typeCheck.contains(QLatin1String("%in"))) {
+ typeCheck.replace(QLatin1String("%in"), QLatin1String("pyIn"));
+ typeCheck.append(QLatin1Char(')'));
+ } else {
+ typeCheck += QLatin1String("pyIn)");
+ }
+
+ if (isUserPrimitive(sourceType)
+ || isCppPrimitive(sourceType)
+ || sourceType.typeEntry()->isContainer()
+ || sourceType.typeEntry()->isEnum()
+ || sourceType.typeEntry()->isFlags()) {
+ QTextStream pc(&toCppPreConv);
+ pc << nested << getFullTypeNameWithoutModifiers(sourceType) << " cppIn";
+ writeMinimalConstructorExpression(pc, sourceType);
+ pc << ";\n";
+ writeToCppConversion(pc, sourceType, nullptr, QLatin1String("pyIn"), QLatin1String("cppIn"));
+ pc << ';';
+ toCppConv.append(QLatin1String("cppIn"));
+ } else if (!isWrapperType(sourceType)) {
+ QTextStream tcc(&toCppConv);
+ writeToCppConversion(tcc, sourceType, metaClass, QLatin1String("pyIn"), QLatin1String("/*BOZO-1061*/"));
+ }
+
+
+ }
+ const AbstractMetaType sourceType = conv->isConversionOperator()
+ ? buildAbstractMetaTypeFromAbstractMetaClass(conv->ownerClass())
+ : conv->arguments().constFirst().type();
+ writePythonToCppConversionFunctions(s, sourceType, targetType, typeCheck, toCppConv, toCppPreConv);
+ }
+
+ writeCustomConverterFunctions(s, customConversion);
+}
+
+void CppGenerator::writeCustomConverterFunctions(QTextStream &s, const CustomConversion *customConversion)
+{
+ if (!customConversion)
+ return;
+ const CustomConversion::TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions();
+ if (toCppConversions.isEmpty())
+ return;
+ s << "// Python to C++ conversions for type '" << customConversion->ownerType()->qualifiedCppName() << "'.\n";
+ for (CustomConversion::TargetToNativeConversion *toNative : toCppConversions)
+ writePythonToCppConversionFunctions(s, toNative, customConversion->ownerType());
+ s << Qt::endl;
+}
+
+void CppGenerator::writeConverterRegister(QTextStream &s, const AbstractMetaClass *metaClass,
+ const GeneratorContext &classContext)
+{
+ if (metaClass->isNamespace())
+ return;
+ s << INDENT << "// Register Converter\n";
+ s << INDENT << "SbkConverter *converter = Shiboken::Conversions::createConverter(";
+ s << cpythonTypeName(metaClass) << ',' << Qt::endl;
+ {
+ Indentation indent(INDENT);
+ QString sourceTypeName = metaClass->name();
+ QString targetTypeName = sourceTypeName + QLatin1String("_PTR");
+ s << INDENT << pythonToCppFunctionName(sourceTypeName, targetTypeName) << ',' << Qt::endl;
+ s << INDENT << convertibleToCppFunctionName(sourceTypeName, targetTypeName) << ',' << Qt::endl;
+ std::swap(targetTypeName, sourceTypeName);
+ s << INDENT << cppToPythonFunctionName(sourceTypeName, targetTypeName);
+ if (metaClass->typeEntry()->isValue() || metaClass->typeEntry()->isSmartPointer()) {
+ s << ',' << Qt::endl;
+ sourceTypeName = metaClass->name() + QLatin1String("_COPY");
+ s << INDENT << cppToPythonFunctionName(sourceTypeName, targetTypeName);
+ }
+ }
+ s << ");\n";
+
+ s << Qt::endl;
+
+ auto writeConversions = [&s, this](const QString &signature)
+ {
+ s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "\");\n";
+ s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "*\");\n";
+ s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "&\");\n";
+ };
+
+ auto writeConversionsForType = [writeConversions](const QString &fullTypeName)
+ {
+ QStringList lst = fullTypeName.split(QLatin1String("::"),
+ Qt::SkipEmptyParts);
+ while (!lst.isEmpty()) {
+ QString signature = lst.join(QLatin1String("::"));
+ 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(QLatin1String("::"),
+ Qt::SkipEmptyParts);
+ while (!lst.isEmpty()) {
+ QString signature = lst.join(QLatin1String("::"));
+ writeConversions(QStringLiteral("%1<%2 >").arg(smartPointerName, signature));
+ lst.removeFirst();
+ }
+
+ writeConversionsForType(smartPointerType);
+ }
+
+ s << INDENT << "Shiboken::Conversions::registerConverterName(converter, typeid(::";
+ QString qualifiedCppNameInvocation;
+ if (!classContext.forSmartPointer())
+ qualifiedCppNameInvocation = metaClass->qualifiedCppName();
+ else
+ qualifiedCppNameInvocation = classContext.preciseType().cppSignature();
+
+ s << qualifiedCppNameInvocation << ").name());\n";
+
+ if (classContext.useWrapper()) {
+ s << INDENT << "Shiboken::Conversions::registerConverterName(converter, typeid(::";
+ s << classContext.wrapperName() << ").name());\n";
+ }
+
+ s << Qt::endl;
+
+ if (!metaClass->typeEntry()->isValue() && !metaClass->typeEntry()->isSmartPointer())
+ return;
+
+ // Python to C++ copy (value, not pointer neither reference) conversion.
+ s << INDENT << "// Add Python to C++ copy (value, not pointer neither reference) conversion to type converter.\n";
+ QString sourceTypeName = metaClass->name();
+ QString targetTypeName = sourceTypeName + QLatin1String("_COPY");
+ QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName);
+ QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName);
+ writeAddPythonToCppConversion(s, QLatin1String("converter"), toCpp, isConv);
+
+ // User provided implicit conversions.
+ CustomConversion *customConversion = metaClass->typeEntry()->customConversion();
+
+ // Add implicit conversions.
+ AbstractMetaFunctionList implicitConvs;
+ if (!customConversion || !customConversion->replaceOriginalTargetToNativeConversions()) {
+ const AbstractMetaFunctionList &allImplicitConvs = implicitConversions(metaClass->typeEntry());
+ for (AbstractMetaFunction *func : allImplicitConvs) {
+ if (!func->isUserAdded())
+ implicitConvs << func;
+ }
+ }
+
+ if (!implicitConvs.isEmpty())
+ s << INDENT << "// Add implicit conversions to type converter.\n";
+
+ AbstractMetaType targetType = buildAbstractMetaTypeFromAbstractMetaClass(metaClass);
+ for (const AbstractMetaFunction *conv : qAsConst(implicitConvs)) {
+ if (conv->isModifiedRemoved())
+ continue;
+ AbstractMetaType sourceType;
+ if (conv->isConversionOperator()) {
+ sourceType = buildAbstractMetaTypeFromAbstractMetaClass(conv->ownerClass());
+ } else {
+ // Constructor that does implicit conversion.
+ if (!conv->typeReplaced(1).isEmpty() || conv->isModifiedToArray(1))
+ continue;
+ sourceType = conv->arguments().constFirst().type();
+ }
+ QString toCpp = pythonToCppFunctionName(sourceType, targetType);
+ QString isConv = convertibleToCppFunctionName(sourceType, targetType);
+ writeAddPythonToCppConversion(s, QLatin1String("converter"), toCpp, isConv);
+ }
+
+ writeCustomConverterRegister(s, customConversion, QLatin1String("converter"));
+}
+
+void CppGenerator::writeCustomConverterRegister(QTextStream &s, const CustomConversion *customConversion, const QString &converterVar)
+{
+ if (!customConversion)
+ return;
+ const CustomConversion::TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions();
+ if (toCppConversions.isEmpty())
+ return;
+ s << INDENT << "// Add user defined implicit conversions to type converter.\n";
+ for (CustomConversion::TargetToNativeConversion *toNative : toCppConversions) {
+ QString toCpp = pythonToCppFunctionName(toNative, customConversion->ownerType());
+ QString isConv = convertibleToCppFunctionName(toNative, customConversion->ownerType());
+ writeAddPythonToCppConversion(s, converterVar, toCpp, isConv);
+ }
+}
+
+void CppGenerator::writeContainerConverterFunctions(QTextStream &s, const AbstractMetaType &containerType)
+{
+ writeCppToPythonFunction(s, containerType);
+ writePythonToCppConversionFunctions(s, containerType);
+}
+
+void CppGenerator::writeSmartPointerConverterFunctions(QTextStream &s, const AbstractMetaType &smartPointerType)
+{
+ const AbstractMetaClass *targetClass = AbstractMetaClass::findClass(classes(), smartPointerType.instantiations().at(0).typeEntry());
+
+ if (targetClass) {
+ const auto *smartPointerTypeEntry =
+ static_cast<const SmartPointerTypeEntry *>(
+ smartPointerType.typeEntry());
+
+ // TODO: Missing conversion to smart pointer pointer type:
+
+ s << "// Register smartpointer conversion for all derived classes\n";
+ const auto classes = getBaseClasses(targetClass);
+ for (auto k : classes) {
+ if (smartPointerTypeEntry->matchesInstantiation(k->typeEntry())) {
+ if (auto smartTargetType = findSmartPointerInstantiation(k->typeEntry())) {
+ s << INDENT << "// SmartPointer derived class: " << smartTargetType.cppSignature() << "\n";
+ writePythonToCppConversionFunctions(s, smartPointerType, smartTargetType, {}, {}, {});
+ }
+ }
+ }
+ }
+}
+
+void CppGenerator::writeMethodWrapperPreamble(QTextStream &s, OverloadData &overloadData,
+ const GeneratorContext &context)
+{
+ const AbstractMetaFunction *rfunc = overloadData.referenceFunction();
+ const AbstractMetaClass *ownerClass = rfunc->targetLangOwner();
+ Q_ASSERT(ownerClass == context.metaClass());
+ int minArgs = overloadData.minArgs();
+ int maxArgs = overloadData.maxArgs();
+ bool initPythonArguments;
+ bool usesNamedArguments;
+
+ // If method is a constructor...
+ if (rfunc->isConstructor()) {
+ // Check if the right constructor was called.
+ if (!ownerClass->hasPrivateDestructor()) {
+ s << INDENT;
+ s << "if (Shiboken::Object::isUserType(self) && !Shiboken::ObjectType::canCallConstructor(self->ob_type, Shiboken::SbkType< ::";
+ QString qualifiedCppName;
+ if (!context.forSmartPointer())
+ qualifiedCppName = ownerClass->qualifiedCppName();
+ else
+ qualifiedCppName = context.preciseType().cppSignature();
+
+ s << qualifiedCppName << " >()))\n";
+ Indentation indent(INDENT);
+ s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl << Qt::endl;
+ }
+ // Declare pointer for the underlying C++ object.
+ s << INDENT << "::";
+ if (!context.forSmartPointer()) {
+ s << (context.useWrapper() ? context.wrapperName() : ownerClass->qualifiedCppName());
+ } else {
+ s << context.smartPointerWrapperName();
+ }
+ s << " *cptr{};\n";
+
+ initPythonArguments = maxArgs > 0;
+ usesNamedArguments = !ownerClass->isQObject() && overloadData.hasArgumentWithDefaultValue();
+
+ } else {
+ if (rfunc->implementingClass() &&
+ (!rfunc->implementingClass()->isNamespace() && overloadData.hasInstanceFunction())) {
+ writeCppSelfDefinition(s, rfunc, context, overloadData.hasStaticFunction());
+ }
+ if (!rfunc->isInplaceOperator() && overloadData.hasNonVoidReturnType())
+ s << INDENT << "PyObject *" << PYTHON_RETURN_VAR << "{};\n";
+
+ initPythonArguments = minArgs != maxArgs || maxArgs > 1;
+ usesNamedArguments = rfunc->isCallOperator() || overloadData.hasArgumentWithDefaultValue();
+ }
+
+ if (maxArgs > 0) {
+ s << INDENT << "int overloadId = -1;\n";
+ s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR;
+ if (pythonFunctionWrapperUsesListOfArguments(overloadData)) {
+ s << "[] = { " << NULL_PTR;
+ for (int i = 1; i < maxArgs; ++i)
+ s << ", " << NULL_PTR;
+ s << " };\n";
+ } else {
+ s << "{};\n";
+ }
+ writeUnusedVariableCast(s, QLatin1String(PYTHON_TO_CPP_VAR));
+ }
+
+ if (usesNamedArguments && !rfunc->isCallOperator())
+ s << INDENT << "const Py_ssize_t numNamedArgs = (kwds ? PyDict_Size(kwds) : 0);\n";
+
+ if (initPythonArguments) {
+ s << INDENT << "const Py_ssize_t numArgs = ";
+ if (minArgs == 0 && maxArgs == 1 && !rfunc->isConstructor() && !pythonFunctionWrapperUsesListOfArguments(overloadData))
+ s << "(" << PYTHON_ARG << " == 0 ? 0 : 1);\n";
+ else
+ writeArgumentsInitializer(s, overloadData);
+ }
+}
+
+void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFunctionList &overloads,
+ const GeneratorContext &classContext)
+{
+ ErrorCode errorCode(-1);
+ OverloadData overloadData(overloads, this);
+
+ const AbstractMetaFunction *rfunc = overloadData.referenceFunction();
+ const AbstractMetaClass *metaClass = rfunc->ownerClass();
+
+ s << "static int\n";
+ s << cpythonFunctionName(rfunc) << "(PyObject *self, PyObject *args, PyObject *kwds)\n{\n";
+
+ QSet<QString> argNamesSet;
+ if (usePySideExtensions() && metaClass->isQObject()) {
+ // Write argNames variable with all known argument names.
+ const OverloadData::MetaFunctionList &overloads = overloadData.overloads();
+ for (const AbstractMetaFunction *func : overloads) {
+ const AbstractMetaArgumentList &arguments = func->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+ if (arg.defaultValueExpression().isEmpty() || func->argumentRemoved(arg.argumentIndex() + 1))
+ continue;
+ argNamesSet << arg.name();
+ }
+ }
+ QStringList argNamesList = argNamesSet.values();
+ std::sort(argNamesList.begin(), argNamesList.end());
+ if (argNamesList.isEmpty()) {
+ s << INDENT << "const char **argNames{};\n";
+ } else {
+ s << INDENT << "const char *argNames[] = {\""
+ << argNamesList.join(QLatin1String("\", \"")) << "\"};\n";
+ }
+ s << INDENT << "const QMetaObject *metaObject;\n";
+ }
+
+ s << INDENT << "SbkObject *sbkSelf = reinterpret_cast<SbkObject *>(self);\n";
+
+ if (metaClass->isAbstract() || metaClass->baseClassNames().size() > 1) {
+ s << INDENT << "SbkObjectType *type = reinterpret_cast<SbkObjectType *>(self->ob_type);\n";
+ s << INDENT << "SbkObjectType *myType = reinterpret_cast<SbkObjectType *>(" << cpythonTypeNameExt(metaClass->typeEntry()) << ");\n";
+ }
+
+ if (metaClass->isAbstract()) {
+ s << INDENT << "if (type == myType) {\n" << indent(INDENT)
+ << INDENT << "PyErr_SetString(PyExc_NotImplementedError,\n" << indent(INDENT)
+ << INDENT << "\"'" << metaClass->qualifiedCppName()
+ << "' represents a C++ abstract class and cannot be instantiated\");\n" << outdent(INDENT)
+ << INDENT << returnStatement(m_currentErrorCode) << '\n' << outdent(INDENT)
+ << INDENT<< "}\n\n";
+ }
+
+ if (metaClass->baseClassNames().size() > 1) {
+ if (!metaClass->isAbstract())
+ s << INDENT << "if (type != myType)\n" << indent(INDENT);
+ s << INDENT << "Shiboken::ObjectType::copyMultipleInheritance(type, myType);\n";
+ if (!metaClass->isAbstract())
+ s << outdent(INDENT) << '\n';
+ }
+
+ writeMethodWrapperPreamble(s, overloadData, classContext);
+
+ s << Qt::endl;
+
+ if (overloadData.maxArgs() > 0)
+ writeOverloadedFunctionDecisor(s, overloadData);
+
+ writeFunctionCalls(s, overloadData, classContext);
+ s << Qt::endl;
+
+ s << INDENT << "if (PyErr_Occurred() || !Shiboken::Object::setCppPointer(sbkSelf, Shiboken::SbkType< ::" << metaClass->qualifiedCppName() << " >(), cptr)) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "delete cptr;\n";
+ s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl;
+ }
+ s << INDENT << "}\n";
+ if (overloadData.maxArgs() > 0) {
+ s << INDENT << "if (!cptr) goto " << cpythonFunctionName(rfunc) << "_TypeError;\n";
+ s << Qt::endl;
+ }
+
+ s << INDENT << "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(overloads.constFirst()->ownerClass()))
+ s << INDENT << "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 << INDENT << "if (Shiboken::BindingManager::instance().hasWrapper(cptr)) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "Shiboken::BindingManager::instance().releaseWrapper(Shiboken::BindingManager::instance().retrieveWrapper(cptr));\n";
+ }
+ s << INDENT << "}\n";
+ s << INDENT << "Shiboken::BindingManager::instance().registerWrapper(sbkSelf, cptr);\n";
+
+ // Create metaObject and register signal/slot
+ if (metaClass->isQObject() && usePySideExtensions()) {
+ s << Qt::endl << INDENT << "// QObject setup\n";
+ s << INDENT << "PySide::Signal::updateSourceObject(self);\n";
+ s << INDENT << "metaObject = cptr->metaObject(); // <- init python qt properties\n";
+ s << INDENT << "if (kwds && !PySide::fillQtProperties(self, metaObject, kwds, argNames, "
+ << argNamesSet.count() << "))\n" << indent(INDENT)
+ << INDENT << returnStatement(m_currentErrorCode) << '\n' << outdent(INDENT);
+ }
+
+ // Constructor code injections, position=end
+ bool hasCodeInjectionsAtEnd = false;
+ for (AbstractMetaFunction *func : 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 << INDENT << "switch (overloadId) {\n";
+ for (AbstractMetaFunction *func : overloads) {
+ Indentation indent(INDENT);
+ const CodeSnipList &injectedCodeSnips = func->injectedCodeSnips();
+ for (const CodeSnip &cs : injectedCodeSnips) {
+ if (cs.position == TypeSystem::CodeSnipPositionEnd) {
+ s << INDENT << "case " << metaClass->functions().indexOf(func) << ':' << Qt::endl;
+ s << INDENT << "{\n";
+ {
+ Indentation indent(INDENT);
+ writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode, func);
+ }
+ s << INDENT << "}\n";
+ break;
+ }
+ }
+ }
+ s << "}\n";
+ }
+
+ s << Qt::endl;
+ s << Qt::endl << INDENT << "return 1;\n";
+ if (overloadData.maxArgs() > 0)
+ writeErrorSection(s, overloadData);
+ s<< "}\n\n";
+}
+
+void CppGenerator::writeMethodWrapper(QTextStream &s, const AbstractMetaFunctionList &overloads,
+ const GeneratorContext &classContext)
+{
+ OverloadData overloadData(overloads, this);
+ const AbstractMetaFunction *rfunc = overloadData.referenceFunction();
+
+ int maxArgs = overloadData.maxArgs();
+
+ s << "static PyObject *";
+ s << cpythonFunctionName(rfunc) << "(PyObject *self";
+ if (maxArgs > 0) {
+ s << ", PyObject *" << (pythonFunctionWrapperUsesListOfArguments(overloadData) ? "args" : PYTHON_ARG);
+ if (overloadData.hasArgumentWithDefaultValue() || rfunc->isCallOperator())
+ s << ", PyObject *kwds";
+ }
+ s << ")\n{\n";
+
+ writeMethodWrapperPreamble(s, overloadData, classContext);
+
+ s << Qt::endl;
+
+ /*
+ * 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
+ * http://bugs.openbossa.org/show_bug.cgi?id=119
+ */
+ bool hasReturnValue = overloadData.hasNonVoidReturnType();
+ bool callExtendedReverseOperator = hasReturnValue
+ && !rfunc->isInplaceOperator()
+ && !rfunc->isCallOperator()
+ && rfunc->isOperatorOverload();
+
+ QScopedPointer<Indentation> reverseIndent;
+
+ if (callExtendedReverseOperator) {
+ QString revOpName = ShibokenGenerator::pythonOperatorFunctionName(rfunc).insert(2, QLatin1Char('r'));
+ // For custom classes, operations like __radd__ and __rmul__
+ // will enter an infinite loop.
+ if (rfunc->isBinaryOperator() && revOpName.contains(QLatin1String("shift"))) {
+ s << INDENT << "Shiboken::AutoDecRef attrName(Py_BuildValue(\"s\", \"" << revOpName << "\"));\n";
+ s << INDENT << "if (!isReverse\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "&& Shiboken::Object::checkType(" << PYTHON_ARG << ")\n";
+ s << INDENT << "&& !PyObject_TypeCheck(" << PYTHON_ARG << ", self->ob_type)\n";
+ s << INDENT << "&& PyObject_HasAttr(" << PYTHON_ARG << ", attrName)) {\n";
+
+ // This PyObject_CallMethod call will emit lots of warnings like
+ // "deprecated conversion from string constant to char *" during compilation
+ // due to the method name argument being declared as "char *" instead of "const char *"
+ // issue 6952 http://bugs.python.org/issue6952
+ s << INDENT << "PyObject *revOpMethod = PyObject_GetAttr(" << PYTHON_ARG << ", attrName);\n";
+ s << INDENT << "if (revOpMethod && PyCallable_Check(revOpMethod)) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << PYTHON_RETURN_VAR << " = PyObject_CallFunction(revOpMethod, const_cast<char *>(\"O\"), self);\n";
+ s << INDENT << "if (PyErr_Occurred() && (PyErr_ExceptionMatches(PyExc_NotImplementedError)";
+ s << " || PyErr_ExceptionMatches(PyExc_AttributeError))) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "PyErr_Clear();\n";
+ s << INDENT << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");\n";
+ s << INDENT << PYTHON_RETURN_VAR << " = " << NULL_PTR << ";\n";
+ }
+ s << INDENT << "}\n";
+ }
+ s << INDENT << "}\n";
+ s << INDENT << "Py_XDECREF(revOpMethod);\n\n";
+ } //
+ s << INDENT << "}\n\n";
+ s << INDENT << "// Do not enter here if other object has implemented a reverse operator.\n";
+ s << INDENT << "if (!" << PYTHON_RETURN_VAR << ") {\n";
+ reverseIndent.reset(new Indentation(INDENT));
+ } // binary shift operator
+ }
+
+ if (maxArgs > 0)
+ writeOverloadedFunctionDecisor(s, overloadData);
+
+ writeFunctionCalls(s, overloadData, classContext);
+
+ if (!reverseIndent.isNull()) { // binary shift operator
+ reverseIndent.reset();
+ s << Qt::endl << INDENT << "} // End of \"if (!" << PYTHON_RETURN_VAR << ")\"\n";
+ }
+
+ s << Qt::endl;
+
+ writeFunctionReturnErrorCheckSection(s, hasReturnValue && !rfunc->isInplaceOperator());
+
+ if (hasReturnValue) {
+ if (rfunc->isInplaceOperator()) {
+ s << INDENT << "Py_INCREF(self);\n";
+ s << INDENT << "return self;\n";
+ } else {
+ s << INDENT << "return " << PYTHON_RETURN_VAR << ";\n";
+ }
+ } else {
+ s << INDENT << "Py_RETURN_NONE;\n";
+ }
+
+ if (maxArgs > 0)
+ writeErrorSection(s, overloadData);
+
+ s<< "}\n\n";
+}
+
+void CppGenerator::writeArgumentsInitializer(QTextStream &s, OverloadData &overloadData)
+{
+ const AbstractMetaFunction *rfunc = overloadData.referenceFunction();
+ s << "PyTuple_GET_SIZE(args);\n";
+ writeUnusedVariableCast(s, QLatin1String("numArgs"));
+
+ int minArgs = overloadData.minArgs();
+ int maxArgs = overloadData.maxArgs();
+
+ s << INDENT << "PyObject *";
+ s << PYTHON_ARGS << "[] = {"
+ << QString(maxArgs, QLatin1Char('0')).split(QLatin1String(""), Qt::SkipEmptyParts).join(QLatin1String(", "))
+ << "};\n";
+ s << Qt::endl;
+
+ if (overloadData.hasVarargs()) {
+ maxArgs--;
+ if (minArgs > maxArgs)
+ minArgs = maxArgs;
+
+ s << INDENT << "PyObject *nonvarargs = PyTuple_GetSlice(args, 0, " << maxArgs << ");\n";
+ s << INDENT << "Shiboken::AutoDecRef auto_nonvarargs(nonvarargs);\n";
+ s << INDENT << PYTHON_ARGS << '[' << maxArgs << "] = PyTuple_GetSlice(args, " << maxArgs << ", numArgs);\n";
+ s << INDENT << "Shiboken::AutoDecRef auto_varargs(" << PYTHON_ARGS << "[" << maxArgs << "]);\n";
+ s << Qt::endl;
+ }
+
+ bool usesNamedArguments = overloadData.hasArgumentWithDefaultValue();
+
+ s << INDENT << "// invalid argument lengths\n";
+ bool ownerClassIsQObject = rfunc->ownerClass() && rfunc->ownerClass()->isQObject() && rfunc->isConstructor();
+ if (usesNamedArguments) {
+ if (!ownerClassIsQObject) {
+ s << INDENT << "if (numArgs" << (overloadData.hasArgumentWithDefaultValue() ? " + numNamedArgs" : "") << " > " << maxArgs << ") {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "PyErr_SetString(PyExc_TypeError, \"" << fullPythonFunctionName(rfunc) << "(): too many arguments\");\n";
+ s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl;
+ }
+ s << INDENT << '}';
+ }
+ if (minArgs > 0) {
+ if (ownerClassIsQObject)
+ s << INDENT;
+ else
+ s << " else ";
+ s << "if (numArgs < " << minArgs << ") {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "PyErr_SetString(PyExc_TypeError, \"" << fullPythonFunctionName(rfunc) << "(): not enough arguments\");\n";
+ s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl;
+ }
+ s << INDENT << '}';
+ }
+ }
+ const QVector<int> invalidArgsLength = overloadData.invalidArgumentLengths();
+ if (!invalidArgsLength.isEmpty()) {
+ QStringList invArgsLen;
+ for (int i : qAsConst(invalidArgsLength))
+ invArgsLen << QStringLiteral("numArgs == %1").arg(i);
+ if (usesNamedArguments && (!ownerClassIsQObject || minArgs > 0))
+ s << " else ";
+ else
+ s << INDENT;
+ s << "if (" << invArgsLen.join(QLatin1String(" || ")) << ")\n";
+ Indentation indent(INDENT);
+ s << INDENT << "goto " << cpythonFunctionName(rfunc) << "_TypeError;";
+ }
+ s << Qt::endl << Qt::endl;
+
+ QString funcName;
+ if (rfunc->isOperatorOverload())
+ funcName = ShibokenGenerator::pythonOperatorFunctionName(rfunc);
+ else
+ funcName = rfunc->name();
+
+ QString argsVar = overloadData.hasVarargs() ? QLatin1String("nonvarargs") : QLatin1String("args");
+ s << INDENT << "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 << "))\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl;
+ }
+ s << Qt::endl;
+}
+
+void CppGenerator::writeCppSelfConversion(QTextStream &s, const GeneratorContext &context,
+ const QString &className, bool useWrapperClass)
+{
+ static const QString pythonSelfVar = QLatin1String("self");
+ if (useWrapperClass)
+ s << "static_cast<" << className << " *>(";
+ if (!context.forSmartPointer())
+ s << cpythonWrapperCPtr(context.metaClass(), pythonSelfVar);
+ else
+ s << cpythonWrapperCPtr(context.preciseType(), pythonSelfVar);
+ if (useWrapperClass)
+ s << ')';
+}
+
+void CppGenerator::writeCppSelfDefinition(QTextStream &s,
+ const GeneratorContext &context,
+ bool hasStaticOverload,
+ bool cppSelfAsReference)
+{
+ Q_ASSERT(!(cppSelfAsReference && hasStaticOverload));
+
+ const AbstractMetaClass *metaClass = context.metaClass();
+ bool useWrapperClass = avoidProtectedHack() && metaClass->hasProtectedMembers()
+ && !metaClass->attributes().testFlag(AbstractMetaAttributes::FinalCppClass);
+ Q_ASSERT(!useWrapperClass || context.useWrapper());
+ QString className;
+ if (!context.forSmartPointer()) {
+ className = useWrapperClass
+ ? context.wrapperName()
+ : (QLatin1String("::") + metaClass->qualifiedCppName());
+ } else {
+ className = context.smartPointerWrapperName();
+ }
+
+ writeInvalidPyObjectCheck(s, QLatin1String("self"));
+
+ if (cppSelfAsReference) {
+ s << INDENT << "auto &" << CPP_SELF_VAR << " = *";
+ writeCppSelfConversion(s, context, className, useWrapperClass);
+ s << ";\n";
+ return;
+ }
+
+ if (!hasStaticOverload) {
+ s << INDENT << "auto " << CPP_SELF_VAR << " = ";
+ writeCppSelfConversion(s, context, className, useWrapperClass);
+ s << ";\n";
+ writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR));
+ return;
+ }
+
+ s << INDENT << className << " *" << CPP_SELF_VAR << " = nullptr;\n";
+ writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR));
+
+ // Checks if the underlying C++ object is valid.
+ s << INDENT << "if (self)\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << CPP_SELF_VAR << " = ";
+ writeCppSelfConversion(s, context, className, useWrapperClass);
+ s << ";\n";
+ }
+}
+
+void CppGenerator::writeCppSelfDefinition(QTextStream &s,
+ const AbstractMetaFunction *func,
+ const GeneratorContext &context,
+ bool hasStaticOverload)
+{
+ if (!func->ownerClass() || func->isConstructor())
+ return;
+
+ if (func->isOperatorOverload() && func->isBinaryOperator()) {
+ QString checkFunc = cpythonCheckFunction(func->ownerClass()->typeEntry());
+ s << INDENT << "bool isReverse = " << checkFunc << PYTHON_ARG << ")\n";
+ {
+ Indentation indent1(INDENT, 4);
+ s << INDENT << "&& !" << checkFunc << "self);\n";
+ }
+ s << INDENT << "if (isReverse)\n";
+ Indentation indent(INDENT);
+ s << INDENT << "std::swap(self, " << PYTHON_ARG << ");\n";
+ }
+
+ writeCppSelfDefinition(s, context, hasStaticOverload);
+}
+
+void CppGenerator::writeErrorSection(QTextStream &s, OverloadData &overloadData)
+{
+ const AbstractMetaFunction *rfunc = overloadData.referenceFunction();
+ s << Qt::endl << INDENT << cpythonFunctionName(rfunc) << "_TypeError:\n";
+ Indentation indentation(INDENT);
+ QString funcName = fullPythonFunctionName(rfunc);
+
+ QString argsVar = pythonFunctionWrapperUsesListOfArguments(overloadData)
+ ? QLatin1String("args") : QLatin1String(PYTHON_ARG);
+ s << INDENT << "Shiboken::setErrorAboutWrongArguments(" << argsVar << ", \"" << funcName << "\");\n";
+ s << INDENT << "return " << m_currentErrorCode << ";\n";
+}
+
+void CppGenerator::writeFunctionReturnErrorCheckSection(QTextStream &s, bool hasReturnValue)
+{
+ s << INDENT << "if (PyErr_Occurred()";
+ if (hasReturnValue)
+ s << " || !" << PYTHON_RETURN_VAR;
+ s << ") {\n";
+ {
+ Indentation indent(INDENT);
+ if (hasReturnValue)
+ s << INDENT << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");\n";
+ s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl;
+ }
+ s << INDENT << "}\n";
+}
+
+void CppGenerator::writeInvalidPyObjectCheck(QTextStream &s, const QString &pyObj)
+{
+ s << INDENT << "if (!Shiboken::Object::isValid(" << pyObj << "))\n";
+ Indentation indent(INDENT);
+ s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl;
+}
+
+static QString pythonToCppConverterForArgumentName(const QString &argumentName)
+{
+ static const QRegularExpression pyArgsRegex(QLatin1String(PYTHON_ARGS)
+ + QLatin1String(R"((\[\d+[-]?\d*\]))"));
+ Q_ASSERT(pyArgsRegex.isValid());
+ const QRegularExpressionMatch match = pyArgsRegex.match(argumentName);
+ QString result = QLatin1String(PYTHON_TO_CPP_VAR);
+ if (match.hasMatch())
+ result += match.captured(1);
+ return result;
+}
+
+void CppGenerator::writeTypeCheck(QTextStream &s, AbstractMetaType argType,
+ const QString &argumentName, bool isNumber,
+ const QString &customType, bool rejectNull)
+{
+ QString customCheck;
+ if (!customType.isEmpty()) {
+ AbstractMetaType metaType;
+ // PYSIDE-795: Note: XML-Overrides are handled in this shibokengenerator function!
+ // This enables iterables for QMatrix4x4 for instance.
+ customCheck = guessCPythonCheckFunction(customType, &metaType);
+ if (metaType)
+ argType = metaType;
+ }
+
+ // TODO-CONVERTER: merge this with the code below.
+ QString typeCheck;
+ if (customCheck.isEmpty())
+ typeCheck = cpythonIsConvertibleFunction(argType, argType.isEnum() ? false : isNumber);
+ else
+ typeCheck = customCheck;
+ typeCheck.append(QString::fromLatin1("(%1)").arg(argumentName));
+
+ // TODO-CONVERTER -----------------------------------------------------------------------
+ if (customCheck.isEmpty() && !argType.typeEntry()->isCustom()) {
+ typeCheck = QString::fromLatin1("(%1 = %2))").arg(pythonToCppConverterForArgumentName(argumentName), typeCheck);
+ if (!isNumber && argType.typeEntry()->isCppPrimitive())
+ typeCheck.prepend(QString::fromLatin1("%1(%2) && ").arg(cpythonCheckFunction(argType), argumentName));
+ }
+ // TODO-CONVERTER -----------------------------------------------------------------------
+
+ if (rejectNull)
+ typeCheck = QString::fromLatin1("(%1 != Py_None && %2)").arg(argumentName, typeCheck);
+
+ s << typeCheck;
+}
+
+static void checkTypeViability(const AbstractMetaFunction *func, const AbstractMetaType &type, int argIdx)
+{
+ if (!type
+ || type.isVoid()
+ || !type.typeEntry()->isPrimitive()
+ || type.indirections() == 0
+ || (type.indirections() == 1 && type.typeUsagePattern() == AbstractMetaType::NativePointerAsArrayPattern)
+ || ShibokenGenerator::isCString(type)
+ || func->argumentRemoved(argIdx)
+ || !func->typeReplaced(argIdx).isEmpty()
+ || !func->conversionRule(TypeSystem::All, argIdx).isEmpty()
+ || 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 AbstractMetaFunction *func)
+{
+ if (func->isUserAdded())
+ return;
+ checkTypeViability(func, func->type(), 0);
+ for (int i = 0; i < func->arguments().count(); ++i)
+ checkTypeViability(func, func->arguments().at(i).type(), i + 1);
+}
+
+void CppGenerator::writeTypeCheck(QTextStream &s, const OverloadData *overloadData, QString argumentName)
+{
+ QSet<const TypeEntry *> numericTypes;
+ const OverloadDataList &overloads = overloadData->previousOverloadData()->nextOverloadData();
+ for (OverloadData *od : overloads) {
+ const OverloadData::MetaFunctionList &odOverloads = od->overloads();
+ for (const AbstractMetaFunction *func : odOverloads) {
+ checkTypeViability(func);
+ const AbstractMetaType &argType = od->argument(func)->type();
+ if (!argType.isPrimitive())
+ continue;
+ if (ShibokenGenerator::isNumber(argType.typeEntry()))
+ numericTypes << argType.typeEntry();
+ }
+ }
+
+ // This condition trusts that the OverloadData object will arrange for
+ // PyInt type to come after the more precise numeric types (e.g. float and bool)
+ AbstractMetaType argType = overloadData->argType();
+ if (auto viewOn = argType.viewOn())
+ argType = *viewOn;
+ bool numberType = numericTypes.count() == 1 || ShibokenGenerator::isPyInt(argType);
+ QString customType = (overloadData->hasArgumentTypeReplace() ? overloadData->argumentTypeReplaced() : QString());
+ bool rejectNull = shouldRejectNullPointerArgument(overloadData->referenceFunction(), overloadData->argPos());
+ writeTypeCheck(s, argType, argumentName, numberType, customType, rejectNull);
+}
+
+void CppGenerator::writeArgumentConversion(QTextStream &s,
+ const AbstractMetaType &argType,
+ const QString &argName, const QString &pyArgName,
+ const AbstractMetaClass *context,
+ const QString &defaultValue,
+ bool castArgumentAsUnused)
+{
+ if (argType.typeEntry()->isCustom() || argType.typeEntry()->isVarargs())
+ return;
+ if (isWrapperType(argType))
+ writeInvalidPyObjectCheck(s, pyArgName);
+ writePythonToCppTypeConversion(s, argType, pyArgName, argName, context, defaultValue);
+ if (castArgumentAsUnused)
+ writeUnusedVariableCast(s, argName);
+}
+
+const AbstractMetaType CppGenerator::getArgumentType(const AbstractMetaFunction *func, int argPos)
+{
+ if (argPos < 0 || argPos > func->arguments().size()) {
+ qCWarning(lcShiboken).noquote().nospace()
+ << QStringLiteral("Argument index for function '%1' out of range.").arg(func->signature());
+ return {};
+ }
+
+ QString typeReplaced = func->typeReplaced(argPos);
+ if (typeReplaced.isEmpty()) {
+ if (argPos == 0)
+ return func->type();
+ auto argType = func->arguments().at(argPos - 1).type();
+ return argType.viewOn() ? *argType.viewOn() : argType;
+ }
+
+ auto argType = buildAbstractMetaTypeFromString(typeReplaced);
+ if (!argType && !m_knownPythonTypes.contains(typeReplaced)) {
+ qCWarning(lcShiboken).noquote().nospace()
+ << QString::fromLatin1("Unknown type '%1' used as argument type replacement "\
+ "in function '%2', the generated code may be broken.")
+ .arg(typeReplaced, func->signature());
+ }
+ return argType;
+}
+
+static inline QString arrayHandleType(const AbstractMetaTypeList &nestedArrayTypes)
+{
+ switch (nestedArrayTypes.size()) {
+ case 1:
+ return QStringLiteral("Shiboken::Conversions::ArrayHandle<")
+ + nestedArrayTypes.constLast().minimalSignature()
+ + QLatin1Char('>');
+ case 2:
+ return QStringLiteral("Shiboken::Conversions::Array2Handle<")
+ + nestedArrayTypes.constLast().minimalSignature()
+ + QStringLiteral(", ")
+ + QString::number(nestedArrayTypes.constFirst().arrayElementCount())
+ + QLatin1Char('>');
+ }
+ return QString();
+}
+
+void CppGenerator::writePythonToCppTypeConversion(QTextStream &s,
+ const AbstractMetaType &type,
+ const QString &pyIn,
+ const QString &cppOut,
+ const AbstractMetaClass * /* context */,
+ const QString &defaultValue)
+{
+ const TypeEntry *typeEntry = type.typeEntry();
+ if (typeEntry->isCustom() || typeEntry->isVarargs())
+ return;
+
+ QString cppOutAux = cppOut + QLatin1String("_local");
+
+ bool treatAsPointer = isValueTypeWithCopyConstructorOnly(type);
+ bool isPointerOrObjectType = (isObjectType(type) || isPointer(type)) && !isUserPrimitive(type) && !isCppPrimitive(type);
+ bool isNotContainerEnumOrFlags = !typeEntry->isContainer() && !typeEntry->isEnum() && !typeEntry->isFlags();
+ bool mayHaveImplicitConversion = type.referenceType() == LValueReference
+ && !isUserPrimitive(type)
+ && !isCppPrimitive(type)
+ && isNotContainerEnumOrFlags
+ && !(treatAsPointer || isPointerOrObjectType);
+
+ const AbstractMetaTypeList &nestedArrayTypes = type.nestedArrayTypes();
+ const bool isCppPrimitiveArray = !nestedArrayTypes.isEmpty()
+ && nestedArrayTypes.constLast().isCppPrimitive();
+ QString typeName = isCppPrimitiveArray
+ ? arrayHandleType(nestedArrayTypes)
+ : getFullTypeNameWithoutModifiers(type);
+
+ bool isProtectedEnum = false;
+
+ if (mayHaveImplicitConversion) {
+ s << INDENT << typeName << ' ' << cppOutAux;
+ writeMinimalConstructorExpression(s, type, defaultValue);
+ s << ";\n";
+ } else if (avoidProtectedHack() && type.typeEntry()->isEnum()) {
+ const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(type);
+ if (metaEnum && metaEnum->isProtected()) {
+ typeName = QLatin1String("long");
+ isProtectedEnum = true;
+ }
+ }
+
+ s << INDENT << typeName;
+ if (isCppPrimitiveArray) {
+ s << ' ' << cppOut;
+ } else if (treatAsPointer || isPointerOrObjectType) {
+ 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 << ')';
+ }
+ } else if (type.referenceType() == LValueReference && !typeEntry->isPrimitive() && isNotContainerEnumOrFlags) {
+ s << " *" << cppOut << " = &" << cppOutAux;
+ } else {
+ s << ' ' << cppOut;
+ if (isProtectedEnum && avoidProtectedHack()) {
+ s << " = ";
+ if (defaultValue.isEmpty())
+ s << "0";
+ else
+ s << "(long)" << defaultValue;
+ } else if (isUserPrimitive(type) || typeEntry->isEnum() || typeEntry->isFlags()) {
+ writeMinimalConstructorExpression(s, typeEntry, defaultValue);
+ } else if (!type.isContainer() && !type.isSmartPointer()) {
+ writeMinimalConstructorExpression(s, type, defaultValue);
+ }
+ }
+ s << ";\n";
+
+ QString pythonToCppFunc = pythonToCppConverterForArgumentName(pyIn);
+
+ s << INDENT;
+ if (!defaultValue.isEmpty())
+ s << "if (" << pythonToCppFunc << ") ";
+
+ QString pythonToCppCall = QString::fromLatin1("%1(%2, &%3)").arg(pythonToCppFunc, pyIn, cppOut);
+ if (!mayHaveImplicitConversion) {
+ s << pythonToCppCall << ";\n";
+ return;
+ }
+
+ if (!defaultValue.isEmpty())
+ s << "{\n" << INDENT;
+
+ s << "if (Shiboken::Conversions::isImplicitConversion(reinterpret_cast<SbkObjectType *>("
+ << cpythonTypeNameExt(type) << "), " << pythonToCppFunc << "))\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << pythonToCppFunc << '(' << pyIn << ", &" << cppOutAux << ");\n";
+ }
+ s << INDENT << "else\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << pythonToCppCall << ";\n";
+ }
+
+ if (!defaultValue.isEmpty())
+ s << INDENT << '}';
+ s << Qt::endl;
+}
+
+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(QLatin1String("%in"), inputName);
+ rule.replace(QLatin1String("%out"), outputName + QLatin1String("_out"));
+ } else {
+ rule.replace(QLatin1String("%out"), outputName);
+ }
+ CodeSnip snip(snippetLanguage);
+ snip.position = (snippetLanguage == TypeSystem::NativeCode) ? TypeSystem::CodeSnipPositionAny : TypeSystem::CodeSnipPositionBeginning;
+ snip.addCode(rule);
+ snippetList << snip;
+}
+
+void CppGenerator::writeConversionRule(QTextStream &s, const AbstractMetaFunction *func, TypeSystem::Language language)
+{
+ 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);
+}
+
+void CppGenerator::writeConversionRule(QTextStream &s, const AbstractMetaFunction *func, TypeSystem::Language language, const QString &outputVar)
+{
+ CodeSnipList snippets;
+ QString rule = func->conversionRule(language, 0);
+ addConversionRuleCodeSnippet(snippets, rule, language, language, outputVar);
+ writeCodeSnips(s, snippets, TypeSystem::CodeSnipPositionAny, language, func);
+}
+
+void CppGenerator::writeNoneReturn(QTextStream &s, const AbstractMetaFunction *func, bool thereIsReturnValue)
+{
+ if (thereIsReturnValue && (func->isVoid() || func->argumentRemoved(0)) && !injectedCodeHasReturnValueAttribution(func)) {
+ s << INDENT << PYTHON_RETURN_VAR << " = Py_None;\n";
+ s << INDENT << "Py_INCREF(Py_None);\n";
+ }
+}
+
+void CppGenerator::writeOverloadedFunctionDecisor(QTextStream &s, const OverloadData &overloadData)
+{
+ s << INDENT << "// Overloaded function decisor\n";
+ const AbstractMetaFunction *rfunc = overloadData.referenceFunction();
+ const OverloadData::MetaFunctionList &functionOverloads = overloadData.overloadsWithoutRepetition();
+ for (int i = 0; i < functionOverloads.count(); i++) {
+ const auto func = functionOverloads.at(i);
+ s << INDENT << "// " << i << ": ";
+ if (func->isStatic())
+ s << "static ";
+ if (const auto *decl = func->declaringClass())
+ s << decl->name() << "::";
+ s << func->minimalSignature() << Qt::endl;
+ }
+ writeOverloadedFunctionDecisorEngine(s, &overloadData);
+ s << Qt::endl;
+
+ // Ensure that the direct overload that called this reverse
+ // is called.
+ if (rfunc->isOperatorOverload() && !rfunc->isCallOperator()) {
+ s << INDENT << "if (isReverse && overloadId == -1) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"reverse operator not implemented.\");\n";
+ s << INDENT << "return {};\n";
+ }
+ s << INDENT << "}\n\n";
+ }
+
+ s << INDENT << "// Function signature not found.\n";
+ s << INDENT << "if (overloadId == -1) goto " << cpythonFunctionName(overloadData.referenceFunction()) << "_TypeError;\n";
+ s << Qt::endl;
+}
+
+void CppGenerator::writeOverloadedFunctionDecisorEngine(QTextStream &s, const OverloadData *parentOverloadData)
+{
+ bool hasDefaultCall = parentOverloadData->nextArgumentHasDefaultValue();
+ const AbstractMetaFunction *referenceFunction = parentOverloadData->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) {
+ const OverloadData::MetaFunctionList &overloads = parentOverloadData->overloads();
+ for (const AbstractMetaFunction *func : overloads) {
+ if (parentOverloadData->isFinalOccurrence(func)) {
+ referenceFunction = func;
+ hasDefaultCall = true;
+ break;
+ }
+ }
+ }
+
+ int maxArgs = parentOverloadData->maxArgs();
+ // Python constructors always receive multiple arguments.
+ bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(*parentOverloadData);
+
+ // Functions without arguments are identified right away.
+ if (maxArgs == 0) {
+ s << INDENT << "overloadId = " << parentOverloadData->headOverloadData()->overloads().indexOf(referenceFunction);
+ s << "; // " << referenceFunction->minimalSignature() << Qt::endl;
+ 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 (!parentOverloadData->isHeadOverloadData()) {
+ bool isLastArgument = parentOverloadData->nextOverloadData().isEmpty();
+ bool signatureFound = parentOverloadData->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 AbstractMetaFunction *func = parentOverloadData->referenceFunction();
+ s << INDENT << "overloadId = " << parentOverloadData->headOverloadData()->overloads().indexOf(func);
+ s << "; // " << func->minimalSignature() << Qt::endl;
+ 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 &overloads = parentOverloadData->nextOverloadData();
+ if (hasDefaultCall) {
+ isFirst = false;
+ int numArgs = parentOverloadData->argPos() + 1;
+ s << INDENT << "if (numArgs == " << numArgs << ") {\n";
+ {
+ Indentation indent(INDENT);
+ const AbstractMetaFunction *func = referenceFunction;
+ for (OverloadData *overloadData : overloads) {
+ const AbstractMetaFunction *defValFunc = overloadData->getFunctionWithDefaultValue();
+ if (defValFunc) {
+ func = defValFunc;
+ break;
+ }
+ }
+ s << INDENT << "overloadId = " << parentOverloadData->headOverloadData()->overloads().indexOf(func);
+ s << "; // " << func->minimalSignature() << Qt::endl;
+ }
+ s << INDENT << '}';
+ }
+
+ for (OverloadData *overloadData : overloads) {
+ bool signatureFound = overloadData->overloads().size() == 1
+ && !overloadData->getFunctionWithDefaultValue()
+ && !overloadData->findNextArgWithDefault();
+
+ const AbstractMetaFunction *refFunc = overloadData->referenceFunction();
+
+ QStringList typeChecks;
+
+ QString pyArgName = (usePyArgs && maxArgs > 1)
+ ? pythonArgsAt(overloadData->argPos())
+ : QLatin1String(PYTHON_ARG);
+ OverloadData *od = overloadData;
+ int startArg = od->argPos();
+ int sequenceArgCount = 0;
+ while (od && !od->argType().isVarargs()) {
+ bool typeReplacedByPyObject = od->argumentTypeReplaced() == QLatin1String("PyObject");
+ if (!typeReplacedByPyObject) {
+ if (usePyArgs)
+ pyArgName = pythonArgsAt(od->argPos());
+ QString typeCheck;
+ QTextStream tck(&typeCheck);
+ const AbstractMetaFunction *func = od->referenceFunction();
+
+ if (func->isConstructor() && func->arguments().count() == 1) {
+ const AbstractMetaClass *ownerClass = func->ownerClass();
+ const ComplexTypeEntry *baseContainerType = ownerClass->typeEntry()->baseContainerType();
+ if (baseContainerType && baseContainerType == func->arguments().constFirst().type().typeEntry()
+ && isCopyable(ownerClass)) {
+ tck << '!' << cpythonCheckFunction(ownerClass->typeEntry()) << pyArgName << ")\n";
+ Indentation indent(INDENT);
+ tck << INDENT << "&& ";
+ }
+ }
+ writeTypeCheck(tck, od, pyArgName);
+ typeChecks << typeCheck;
+ }
+
+ sequenceArgCount++;
+
+ if (od->nextOverloadData().isEmpty()
+ || od->nextArgumentHasDefaultValue()
+ || od->nextOverloadData().size() != 1
+ || od->overloads().size() != od->nextOverloadData().constFirst()->overloads().size()) {
+ overloadData = od;
+ od = nullptr;
+ } else {
+ od = od->nextOverloadData().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;
+ typeChecks.prepend(QString::fromLatin1("numArgs %1 %2").arg(isVarargs ? QLatin1String(">=") : QLatin1String("==")).arg(numArgs));
+ } else if (sequenceArgCount > 1) {
+ typeChecks.prepend(QString::fromLatin1("numArgs >= %1").arg(startArg + sequenceArgCount));
+ } else if (refFunc->isOperatorOverload() && !refFunc->isCallOperator()) {
+ typeChecks.prepend(QString::fromLatin1("%1isReverse").arg(refFunc->isReverseOperator() ? QString() : QLatin1String("!")));
+ }
+
+ if (isFirst) {
+ isFirst = false;
+ s << INDENT;
+ } else {
+ s << " else ";
+ }
+ s << "if (";
+ if (typeChecks.isEmpty()) {
+ s << "true";
+ } else {
+ Indentation indent(INDENT);
+ QString separator;
+ QTextStream sep(&separator);
+ sep << Qt::endl << INDENT << "&& ";
+ s << typeChecks.join(separator);
+ }
+ s << ") {\n";
+ {
+ Indentation indent(INDENT);
+ writeOverloadedFunctionDecisorEngine(s, overloadData);
+ }
+ s << INDENT << "}";
+ }
+ s << Qt::endl;
+}
+
+void CppGenerator::writeFunctionCalls(QTextStream &s, const OverloadData &overloadData,
+ const GeneratorContext &context)
+{
+ const OverloadData::MetaFunctionList &overloads = overloadData.overloadsWithoutRepetition();
+ s << INDENT << "// Call function/method\n";
+ s << INDENT << (overloads.count() > 1 ? "switch (overloadId) " : "") << "{\n";
+ {
+ Indentation indent(INDENT);
+ if (overloads.count() == 1) {
+ writeSingleFunctionCall(s, overloadData, overloads.constFirst(), context);
+ } else {
+ for (int i = 0; i < overloads.count(); i++) {
+ const AbstractMetaFunction *func = overloads.at(i);
+ s << INDENT << "case " << i << ": // " << func->signature() << Qt::endl;
+ s << INDENT << "{\n";
+ {
+ Indentation indent(INDENT);
+ writeSingleFunctionCall(s, overloadData, func, context);
+ if (func->attributes().testFlag(AbstractMetaAttributes::Deprecated)) {
+ s << INDENT << "PyErr_WarnEx(PyExc_DeprecationWarning, \"";
+ if (auto cls = context.metaClass())
+ s << cls->name() << '.';
+ s << func->signature() << " is deprecated\", 1);\n";
+ }
+ s << INDENT << "break;\n";
+ }
+ s << INDENT << "}\n";
+ }
+ }
+ }
+ s << INDENT << "}\n";
+}
+
+void CppGenerator::writeSingleFunctionCall(QTextStream &s,
+ const OverloadData &overloadData,
+ const AbstractMetaFunction *func,
+ const GeneratorContext &context)
+{
+ if (func->isDeprecated()) {
+ s << INDENT << "Shiboken::warning(PyExc_DeprecationWarning, 1, \"Function: '"
+ << func->signature().replace(QLatin1String("::"), QLatin1String("."))
+ << "' is marked as deprecated, please check the documentation for more information.\");\n";
+ }
+
+ if (func->functionType() == AbstractMetaFunction::EmptyFunction) {
+ s << INDENT << "PyErr_Format(PyExc_TypeError, \"%s is a private method.\", \""
+ << func->signature().replace(QLatin1String("::"), QLatin1String("."))
+ << "\");\n";
+ s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl;
+ return;
+ }
+
+ bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(overloadData);
+
+ // Handle named arguments.
+ writeNamedArgumentResolution(s, func, usePyArgs);
+
+ bool injectCodeCallsFunc = injectedCodeCallsCppFunction(context, func);
+ bool mayHaveUnunsedArguments = !func->isUserAdded() && func->hasInjectedCode() && injectCodeCallsFunc;
+ int removedArgs = 0;
+ for (int argIdx = 0; argIdx < func->arguments().count(); ++argIdx) {
+ bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode, argIdx + 1).isEmpty();
+ const AbstractMetaArgument &arg = func->arguments().at(argIdx);
+ if (func->argumentRemoved(argIdx + 1)) {
+ if (!arg.defaultValueExpression().isEmpty()) {
+ const QString cppArgRemoved = QLatin1String(CPP_ARG_REMOVED)
+ + QString::number(argIdx);
+ s << INDENT << getFullTypeName(arg.type()) << ' ' << cppArgRemoved;
+ s << " = " << guessScopeForDefaultValue(func, arg) << ";\n";
+ writeUnusedVariableCast(s, 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.
+ qFatal("No way to call '%s::%s' with the modifications described in the type system.",
+ qPrintable(func->ownerClass()->name()), qPrintable(func->signature()));
+ }
+ removedArgs++;
+ continue;
+ }
+ if (hasConversionRule)
+ continue;
+ const AbstractMetaType argType = getArgumentType(func, argIdx + 1);
+ if (!argType || (mayHaveUnunsedArguments && !injectedCodeUsesArgument(func, argIdx)))
+ continue;
+ int argPos = argIdx - removedArgs;
+ QString argName = QLatin1String(CPP_ARG) + QString::number(argPos);
+ QString pyArgName = usePyArgs ? pythonArgsAt(argPos) : QLatin1String(PYTHON_ARG);
+ QString defaultValue = guessScopeForDefaultValue(func, arg);
+ writeArgumentConversion(s, argType, argName, pyArgName, func->implementingClass(), defaultValue, func->isUserAdded());
+ }
+
+ s << Qt::endl;
+
+ int numRemovedArgs = OverloadData::numberOfRemovedArguments(func);
+
+ s << INDENT << "if (!PyErr_Occurred()) {\n" << indent(INDENT);
+ writeMethodCall(s, func, context, func->arguments().size() - numRemovedArgs);
+ if (!func->isConstructor())
+ writeNoneReturn(s, func, overloadData.hasNonVoidReturnType());
+ s << outdent(INDENT) << INDENT << "}\n";
+}
+
+QString CppGenerator::cppToPythonFunctionName(const QString &sourceTypeName, QString targetTypeName)
+{
+ if (targetTypeName.isEmpty())
+ targetTypeName = sourceTypeName;
+ return QString::fromLatin1("%1_CppToPython_%2").arg(sourceTypeName, targetTypeName);
+}
+
+QString CppGenerator::pythonToCppFunctionName(const QString &sourceTypeName, const QString &targetTypeName)
+{
+ return QString::fromLatin1("%1_PythonToCpp_%2").arg(sourceTypeName, targetTypeName);
+}
+QString CppGenerator::pythonToCppFunctionName(const AbstractMetaType &sourceType, const AbstractMetaType &targetType)
+{
+ return pythonToCppFunctionName(fixedCppTypeName(sourceType), fixedCppTypeName(targetType));
+}
+QString CppGenerator::pythonToCppFunctionName(const CustomConversion::TargetToNativeConversion *toNative,
+ const TypeEntry *targetType)
+{
+ return pythonToCppFunctionName(fixedCppTypeName(toNative), fixedCppTypeName(targetType));
+}
+
+QString CppGenerator::convertibleToCppFunctionName(const QString &sourceTypeName, const QString &targetTypeName)
+{
+ return QString::fromLatin1("is_%1_PythonToCpp_%2_Convertible").arg(sourceTypeName, targetTypeName);
+}
+QString CppGenerator::convertibleToCppFunctionName(const AbstractMetaType &sourceType, const AbstractMetaType &targetType)
+{
+ return convertibleToCppFunctionName(fixedCppTypeName(sourceType), fixedCppTypeName(targetType));
+}
+QString CppGenerator::convertibleToCppFunctionName(const CustomConversion::TargetToNativeConversion *toNative,
+ const TypeEntry *targetType)
+{
+ return convertibleToCppFunctionName(fixedCppTypeName(toNative), fixedCppTypeName(targetType));
+}
+
+void CppGenerator::writeCppToPythonFunction(QTextStream &s, const QString &code, const QString &sourceTypeName, QString targetTypeName)
+{
+ QString prettyCode;
+ QTextStream c(&prettyCode);
+ formatCode(c, code, INDENT);
+ processCodeSnip(prettyCode);
+
+ s << "static PyObject *" << cppToPythonFunctionName(sourceTypeName, targetTypeName);
+ s << "(const void *cppIn) {\n";
+ s << prettyCode;
+ s << "}\n";
+}
+
+static void replaceCppToPythonVariables(QString &code, const QString &typeName)
+{
+ const QString line = QLatin1String("auto &cppInRef = *reinterpret_cast<")
+ + typeName + QLatin1String(" *>(const_cast<void *>(cppIn));");
+ CodeSnipAbstract::prependCode(&code, line);
+ code.replace(QLatin1String("%INTYPE"), typeName);
+ code.replace(QLatin1String("%OUTTYPE"), QLatin1String("PyObject *"));
+ code.replace(QLatin1String("%in"), QLatin1String("cppInRef"));
+ code.replace(QLatin1String("%out"), QLatin1String("pyOut"));
+}
+void CppGenerator::writeCppToPythonFunction(QTextStream &s, const CustomConversion *customConversion)
+{
+ QString code = customConversion->nativeToTargetConversion();
+ replaceCppToPythonVariables(code, getFullTypeName(customConversion->ownerType()));
+ writeCppToPythonFunction(s, code, fixedCppTypeName(customConversion->ownerType()));
+}
+void CppGenerator::writeCppToPythonFunction(QTextStream &s, const AbstractMetaType &containerType)
+{
+ const CustomConversion *customConversion = containerType.typeEntry()->customConversion();
+ if (!customConversion) {
+ qFatal("Can't write the C++ to Python conversion function for container type '%s' - "\
+ "no conversion rule was defined for it in the type system.",
+ qPrintable(containerType.typeEntry()->qualifiedCppName()));
+ }
+ if (!containerType.typeEntry()->isContainer()) {
+ writeCppToPythonFunction(s, customConversion);
+ return;
+ }
+ QString code = customConversion->nativeToTargetConversion();
+ for (int i = 0; i < containerType.instantiations().count(); ++i) {
+ const AbstractMetaType &type = containerType.instantiations().at(i);
+ QString typeName = getFullTypeName(type);
+ if (type.isConstant())
+ typeName = QLatin1String("const ") + typeName;
+ code.replace(QString::fromLatin1("%INTYPE_%1").arg(i), typeName);
+ }
+ replaceCppToPythonVariables(code, getFullTypeNameWithoutModifiers(containerType));
+ processCodeSnip(code);
+ writeCppToPythonFunction(s, code, fixedCppTypeName(containerType));
+}
+
+void CppGenerator::writePythonToCppFunction(QTextStream &s, const QString &code, const QString &sourceTypeName, const QString &targetTypeName)
+{
+ QString prettyCode;
+ QTextStream c(&prettyCode);
+ formatCode(c, code, INDENT);
+ processCodeSnip(prettyCode);
+ s << "static void " << pythonToCppFunctionName(sourceTypeName, targetTypeName);
+ s << "(PyObject *pyIn, void *cppOut) {\n";
+ s << prettyCode;
+ s << "}\n";
+}
+
+void CppGenerator::writeIsPythonConvertibleToCppFunction(QTextStream &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";
+ if (acceptNoneAsCppNull) {
+ s << INDENT << "if (pyIn == Py_None)\n";
+ Indentation indent(INDENT);
+ s << INDENT << "return Shiboken::Conversions::nonePythonToCppNullPtr;\n";
+ }
+ s << INDENT << "if (" << condition << ")\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "return " << pythonToCppFuncName << ";\n";
+ }
+ s << INDENT << "return {};\n";
+ s << "}\n";
+}
+
+void CppGenerator::writePythonToCppConversionFunctions(QTextStream &s,
+ const AbstractMetaType &sourceType,
+ const AbstractMetaType &targetType,
+ QString typeCheck,
+ QString conversion,
+ const QString &preConversion)
+{
+ QString sourcePyType = cpythonTypeNameExt(sourceType);
+
+ // Python to C++ conversion function.
+ QString code;
+ QTextStream c(&code);
+ Indentor nested;
+ if (conversion.isEmpty())
+ conversion = QLatin1Char('*') + cpythonWrapperCPtr(sourceType, QLatin1String("pyIn"));
+ if (!preConversion.isEmpty())
+ c << nested << preConversion << Qt::endl;
+ const QString fullTypeName = targetType.isSmartPointer()
+ ? targetType.cppSignature()
+ : getFullTypeName(targetType.typeEntry());
+ c << nested << "*reinterpret_cast<" << fullTypeName << " *>(cppOut) = "
+ << fullTypeName << '(' << conversion << ");";
+ QString sourceTypeName = fixedCppTypeName(sourceType);
+ QString targetTypeName = fixedCppTypeName(targetType);
+ writePythonToCppFunction(s, code, sourceTypeName, targetTypeName);
+
+ // Python to C++ convertible check function.
+ if (typeCheck.isEmpty())
+ typeCheck = QString::fromLatin1("PyObject_TypeCheck(pyIn, %1)").arg(sourcePyType);
+ writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, typeCheck);
+ s << Qt::endl;
+}
+
+void CppGenerator::writePythonToCppConversionFunctions(QTextStream &s,
+ const CustomConversion::TargetToNativeConversion *toNative,
+ const TypeEntry *targetType)
+{
+ // Python to C++ conversion function.
+ QString code = toNative->conversion();
+ QString inType;
+ if (toNative->sourceType())
+ inType = cpythonTypeNameExt(toNative->sourceType());
+ else
+ inType = QString::fromLatin1("(%1_TypeF())").arg(toNative->sourceTypeName());
+ code.replace(QLatin1String("%INTYPE"), inType);
+ code.replace(QLatin1String("%OUTTYPE"), targetType->qualifiedCppName());
+ code.replace(QLatin1String("%in"), QLatin1String("pyIn"));
+ code.replace(QLatin1String("%out"),
+ QLatin1String("*reinterpret_cast<") + getFullTypeName(targetType) + QLatin1String(" *>(cppOut)"));
+
+ 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 == QLatin1String("Py_None") || pyTypeName == QLatin1String("PyNone"))
+ typeCheck = QLatin1String("%in == Py_None");
+ else if (pyTypeName == QLatin1String("SbkEnumType"))
+ typeCheck = QLatin1String("Shiboken::isShibokenEnum(%in)");
+ else if (pyTypeName == QLatin1String("SbkObject"))
+ typeCheck = QLatin1String("Shiboken::Object::checkType(%in)");
+ else if (pyTypeName == QLatin1String("PyTypeObject"))
+ typeCheck = QLatin1String("PyType_Check(%in)");
+ else if (pyTypeName == QLatin1String("PyObject"))
+ typeCheck = QLatin1String("PyObject_TypeCheck(%in, &PyBaseObject_Type)");
+ // PYSIDE-795: We abuse PySequence for iterables
+ else if (pyTypeName == QLatin1String("PySequence"))
+ typeCheck = QLatin1String("Shiboken::String::checkIterable(%in)");
+ else if (pyTypeName.startsWith(QLatin1String("Py")))
+ typeCheck = pyTypeName + QLatin1String("_Check(%in)");
+ }
+ if (typeCheck.isEmpty()) {
+ if (!toNative->sourceType() || toNative->sourceType()->isPrimitive()) {
+ qFatal("User added implicit conversion for C++ type '%s' must provide either an input "\
+ "type check function or a non primitive type entry.",
+ qPrintable(targetType->qualifiedCppName()));
+
+ }
+ typeCheck = QString::fromLatin1("PyObject_TypeCheck(%in, %1)").arg(cpythonTypeNameExt(toNative->sourceType()));
+ }
+ typeCheck.replace(QLatin1String("%in"), QLatin1String("pyIn"));
+ processCodeSnip(typeCheck);
+ writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, typeCheck);
+}
+
+void CppGenerator::writePythonToCppConversionFunctions(QTextStream &s, const AbstractMetaType &containerType)
+{
+ const CustomConversion *customConversion = containerType.typeEntry()->customConversion();
+ if (!customConversion) {
+ //qFatal
+ return;
+ }
+ const CustomConversion::TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions();
+ if (toCppConversions.isEmpty()) {
+ //qFatal
+ return;
+ }
+ // Python to C++ conversion function.
+ QString cppTypeName = getFullTypeNameWithoutModifiers(containerType);
+ QString code = toCppConversions.constFirst()->conversion();
+ const QString line = QLatin1String("auto &cppOutRef = *reinterpret_cast<")
+ + cppTypeName + QLatin1String(" *>(cppOut);");
+ CodeSnipAbstract::prependCode(&code, line);
+ for (int i = 0; i < containerType.instantiations().count(); ++i) {
+ const AbstractMetaType &type = containerType.instantiations().at(i);
+ QString typeName = getFullTypeName(type);
+ if (type.isValue() && isValueTypeWithCopyConstructorOnly(type)) {
+ 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, QLatin1Char('*') + varName);
+ code.replace(pos, code.size() - pos, rightCode);
+ }
+ typeName.append(QLatin1String(" *"));
+ }
+ code.replace(QString::fromLatin1("%OUTTYPE_%1").arg(i), typeName);
+ }
+ code.replace(QLatin1String("%OUTTYPE"), cppTypeName);
+ code.replace(QLatin1String("%in"), QLatin1String("pyIn"));
+ code.replace(QLatin1String("%out"), QLatin1String("cppOutRef"));
+ QString typeName = fixedCppTypeName(containerType);
+ writePythonToCppFunction(s, code, typeName, typeName);
+
+ // Python to C++ convertible check function.
+ QString typeCheck = cpythonCheckFunction(containerType);
+ if (typeCheck.isEmpty())
+ typeCheck = QLatin1String("false");
+ else
+ typeCheck = QString::fromLatin1("%1pyIn)").arg(typeCheck);
+ writeIsPythonConvertibleToCppFunction(s, typeName, typeName, typeCheck);
+ s << Qt::endl;
+}
+
+void CppGenerator::writeAddPythonToCppConversion(QTextStream &s, const QString &converterVar, const QString &pythonToCppFunc, const QString &isConvertibleFunc)
+{
+ s << INDENT << "Shiboken::Conversions::addPythonToCppValueConversion(" << converterVar << ',' << Qt::endl;
+ {
+ Indentation indent(INDENT);
+ s << INDENT << pythonToCppFunc << ',' << Qt::endl;
+ s << INDENT << isConvertibleFunc;
+ }
+ s << ");\n";
+}
+
+void CppGenerator::writeNamedArgumentResolution(QTextStream &s, const AbstractMetaFunction *func, bool usePyArgs)
+{
+ const AbstractMetaArgumentList &args = OverloadData::getArgumentsWithDefaultValues(func);
+ if (args.isEmpty())
+ return;
+
+ QString pyErrString(QLatin1String("PyErr_SetString(PyExc_TypeError, \"") + fullPythonFunctionName(func)
+ + QLatin1String("(): got multiple values for keyword argument '%1'.\");"));
+
+ s << INDENT << "if (kwds) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "PyObject *keyName = nullptr;\n";
+ s << INDENT << "PyObject *value = nullptr;\n";
+ for (const AbstractMetaArgument &arg : args) {
+ const int pyArgIndex = arg.argumentIndex()
+ - OverloadData::numberOfRemovedArguments(func, arg.argumentIndex());
+ QString pyArgName = usePyArgs ? pythonArgsAt(pyArgIndex) : QLatin1String(PYTHON_ARG);
+ s << INDENT << "keyName = Py_BuildValue(\"s\",\"" << arg.name() << "\");\n";
+ s << INDENT << "if (PyDict_Contains(kwds, keyName)) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "value = PyDict_GetItem(kwds, keyName);\n";
+ s << INDENT << "if (value && " << pyArgName << ") {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << pyErrString.arg(arg.name()) << Qt::endl;
+ s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl;
+ }
+ s << INDENT << "}\n";
+ s << INDENT << "if (value) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << pyArgName << " = value;\n";
+ s << INDENT << "if (!";
+ writeTypeCheck(s, arg.type(), pyArgName, isNumber(arg.type().typeEntry()),
+ func->typeReplaced(arg.argumentIndex() + 1));
+ s << ")\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "goto " << cpythonFunctionName(func) << "_TypeError;\n";
+ }
+ }
+ s << INDENT << "}\n";
+ }
+ s << INDENT << "}\n";
+ }
+ }
+ s << INDENT << "}\n";
+}
+
+QString CppGenerator::argumentNameFromIndex(const AbstractMetaFunction *func, int argIndex, const AbstractMetaClass **wrappedClass)
+{
+ *wrappedClass = nullptr;
+ QString pyArgName;
+ if (argIndex == -1) {
+ pyArgName = QLatin1String("self");
+ *wrappedClass = func->implementingClass();
+ } else if (argIndex == 0) {
+ const auto funcType = func->type();
+ AbstractMetaType returnType = getTypeWithoutContainer(funcType);
+ if (!returnType.isVoid()) {
+ pyArgName = QLatin1String(PYTHON_RETURN_VAR);
+ *wrappedClass = AbstractMetaClass::findClass(classes(), returnType.typeEntry());
+ } else {
+ QString message = QLatin1String("Invalid Argument index (0, return value) on function modification: ")
+ + funcType.name() + QLatin1Char(' ');
+ if (const AbstractMetaClass *declaringClass = func->declaringClass())
+ message += declaringClass->name() + QLatin1String("::");
+ message += func->name() + QLatin1String("()");
+ qCWarning(lcShiboken).noquote().nospace() << message;
+ }
+ } else {
+ int realIndex = argIndex - 1 - OverloadData::numberOfRemovedArguments(func, argIndex - 1);
+ AbstractMetaType argType = getTypeWithoutContainer(func->arguments().at(realIndex).type());
+
+ if (argType) {
+ *wrappedClass = AbstractMetaClass::findClass(classes(), argType.typeEntry());
+ if (argIndex == 1
+ && !func->isConstructor()
+ && OverloadData::isSingleArgument(getFunctionGroups(func->implementingClass())[func->name()]))
+ pyArgName = QLatin1String(PYTHON_ARG);
+ else
+ pyArgName = pythonArgsAt(argIndex - 1);
+ }
+ }
+ return pyArgName;
+}
+
+static QStringList defaultExceptionHandling()
+{
+ static const QStringList result{
+ QLatin1String("} catch (const std::exception &e) {"),
+ QLatin1String(" PyErr_SetString(PyExc_RuntimeError, e.what());"),
+ QLatin1String("} catch (...) {"),
+ QLatin1String(" PyErr_SetString(PyExc_RuntimeError, \"An unknown exception was caught\");"),
+ QLatin1String("}")};
+ return result;
+}
+
+void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *func,
+ const GeneratorContext &context, int maxArgs)
+{
+ s << INDENT << "// " << func->minimalSignature() << (func->isReverseOperator() ? " [reverse operator]": "") << Qt::endl;
+ if (func->isConstructor()) {
+ const CodeSnipList &snips = func->injectedCodeSnips();
+ for (const CodeSnip &cs : snips) {
+ if (cs.position == TypeSystem::CodeSnipPositionEnd) {
+ auto klass = func->ownerClass();
+ s << INDENT << "overloadId = "
+ << klass->functions().indexOf(const_cast<AbstractMetaFunction *>(func))
+ << ";\n";
+ break;
+ }
+ }
+ }
+
+ if (func->isAbstract()) {
+ s << INDENT << "if (Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(self))) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '";
+ s << func->ownerClass()->name() << '.' << func->name() << "()' not implemented.\");\n";
+ s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl;
+ }
+ s << INDENT << "}\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++) {
+ lastArg = &func->arguments().at(i);
+ if (func->argumentRemoved(i + 1))
+ removedArgs++;
+ }
+ } else if (maxArgs != 0 && !func->arguments().isEmpty()) {
+ lastArg = &func->arguments().constLast();
+ }
+
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode, func, lastArg);
+ }
+
+ writeConversionRule(s, func, TypeSystem::NativeCode);
+
+ 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);
+ bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode,
+ arg.argumentIndex() + 1).isEmpty();
+ if (func->argumentRemoved(i + 1)) {
+ // 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() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX);
+ else if (!arg.defaultValueExpression().isEmpty())
+ userArgs.append(QLatin1String(CPP_ARG_REMOVED) + QString::number(i));
+ } else {
+ int idx = arg.argumentIndex() - removedArgs;
+ bool deRef = isValueTypeWithCopyConstructorOnly(arg.type())
+ || isObjectTypeUsedAsValueType(arg.type())
+ || (arg.type().referenceType() == LValueReference
+ && isWrapperType(arg.type()) && !isPointer(arg.type()));
+ if (hasConversionRule) {
+ userArgs.append(arg.name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX));
+ } else {
+ QString argName;
+ if (deRef)
+ argName += QLatin1Char('*');
+ argName += QLatin1String(CPP_ARG) + QString::number(idx);
+ userArgs.append(argName);
+ }
+ }
+ }
+
+ // 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 (int i = func->arguments().size() - 1; i >= maxArgs + removedArgs; i--) {
+ const AbstractMetaArgument &arg = func->arguments().at(i);
+ const bool defValModified = arg.hasModifiedDefaultValueExpression();
+ bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode,
+ arg.argumentIndex() + 1).isEmpty();
+ if (argsClear && !defValModified && !hasConversionRule)
+ continue;
+ argsClear = false;
+ otherArgsModified |= defValModified || hasConversionRule || func->argumentRemoved(i + 1);
+ if (hasConversionRule)
+ otherArgs.prepend(arg.name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX));
+ else
+ otherArgs.prepend(QLatin1String(CPP_ARG_REMOVED) + QString::number(i));
+ }
+ if (otherArgsModified)
+ userArgs << otherArgs;
+ }
+
+ bool isCtor = false;
+ QString methodCall;
+ QTextStream mc(&methodCall);
+ QString useVAddr;
+ QTextStream uva(&useVAddr);
+ if (func->isOperatorOverload() && !func->isCallOperator()) {
+ QString firstArg(QLatin1Char('('));
+ if (!func->isPointerOperator()) // no de-reference operator
+ firstArg += QLatin1Char('*');
+ firstArg += QLatin1String(CPP_SELF_VAR);
+ firstArg += QLatin1Char(')');
+ QString secondArg = QLatin1String(CPP_ARG0);
+ if (!func->isUnaryOperator() && shouldDereferenceArgumentPointer(func->arguments().constFirst())) {
+ secondArg.prepend(QLatin1String("(*"));
+ secondArg.append(QLatin1Char(')'));
+ }
+
+ 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);
+
+ if (((op == QLatin1String("++")) || (op == QLatin1String("--"))) && !func->isReverseOperator()) {
+ s << Qt::endl << INDENT << "for (int i=0; i < " << secondArg << "; i++, " << firstArg << op << ");\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());
+ QString className = context.useWrapper()
+ ? context.wrapperName() : owner->qualifiedCppName();
+
+ if (func->functionType() == AbstractMetaFunction::CopyConstructorFunction && maxArgs == 1) {
+ mc << "new ::" << className << "(*" << CPP_ARG0 << ')';
+ } else {
+ QString ctorCall = className + QLatin1Char('(') + userArgs.join(QLatin1String(", ")) + QLatin1Char(')');
+ if (usePySideExtensions() && owner->isQObject()) {
+ s << INDENT << "void *addr = PySide::nextQObjectMemoryAddr();\n";
+ uva << "if (addr) {\n";
+ {
+ Indentation indent(INDENT);
+
+ uva << INDENT << "cptr = " << "new (addr) ::"
+ << ctorCall << ";\n"
+ << INDENT
+ << "PySide::setNextQObjectMemoryAddr(0);"
+ << Qt::endl;
+ }
+ uva << INDENT << "} else {\n";
+ {
+ Indentation indent(INDENT);
+
+ uva << INDENT << "cptr = " << "new ::"
+ << ctorCall << ";\n";
+ }
+ uva << INDENT << "}\n";
+ } else {
+ mc << "new ::" << ctorCall;
+ }
+ }
+ } else {
+ QString methodCallClassName;
+ if (context.forSmartPointer())
+ methodCallClassName = context.preciseType().cppSignature();
+ else if (func->ownerClass())
+ methodCallClassName = func->ownerClass()->qualifiedCppName();
+
+ if (func->ownerClass()) {
+ if (!avoidProtectedHack() || !func->isProtected()) {
+ if (func->isStatic()) {
+ mc << "::" << methodCallClassName << "::";
+ } else {
+ const QString selfVarCast = func->ownerClass() == func->implementingClass()
+ ? QLatin1String(CPP_SELF_VAR)
+ : QLatin1String("reinterpret_cast<") + methodCallClassName
+ + QLatin1String(" *>(") + QLatin1String(CPP_SELF_VAR) + QLatin1Char(')');
+ if (func->isConstant()) {
+ if (avoidProtectedHack()) {
+ auto ownerClass = func->ownerClass();
+ mc << "const_cast<const ::";
+ if (ownerClass->hasProtectedMembers()
+ && !ownerClass->attributes().testFlag(AbstractMetaAttributes::FinalCppClass)) {
+ // PYSIDE-500: Need a special wrapper cast when inherited
+ const QString selfWrapCast = ownerClass == func->implementingClass()
+ ? QLatin1String(CPP_SELF_VAR)
+ : QLatin1String("reinterpret_cast<") + wrapperName(ownerClass)
+ + QLatin1String(" *>(") + QLatin1String(CPP_SELF_VAR) + QLatin1Char(')');
+ mc << wrapperName(ownerClass);
+ mc << " *>(" << selfWrapCast << ")->";
+ }
+ else {
+ mc << methodCallClassName;
+ mc << " *>(" << selfVarCast << ")->";
+ }
+ } else {
+ mc << "const_cast<const ::" << methodCallClassName;
+ mc << " *>(" << selfVarCast << ")->";
+ }
+ } else {
+ mc << selfVarCast << "->";
+ }
+ }
+
+ if (!func->isAbstract() && func->isVirtual())
+ mc << "::%CLASS_NAME::";
+
+ mc << func->originalName();
+ } else {
+ if (!func->isStatic()) {
+ const auto *owner = func->ownerClass();
+ const bool directInheritance = context.metaClass() == owner;
+ mc << (directInheritance ? "static_cast" : "reinterpret_cast")
+ << "<::" << wrapperName(owner) << " *>(" << CPP_SELF_VAR << ")->";
+ }
+
+ if (!func->isAbstract())
+ mc << (func->isProtected() ? wrapperName(func->ownerClass()) :
+ QLatin1String("::")
+ + methodCallClassName) << "::";
+ mc << func->originalName() << "_protected";
+ }
+ } else {
+ mc << func->originalName();
+ }
+ mc << '(' << userArgs.join(QLatin1String(", ")) << ')';
+ if (!func->isAbstract() && func->isVirtual()) {
+ mc.flush();
+ if (!avoidProtectedHack() || !func->isProtected()) {
+ QString virtualCall(methodCall);
+ QString normalCall(methodCall);
+ virtualCall.replace(QLatin1String("%CLASS_NAME"),
+ methodCallClassName);
+ normalCall.remove(QLatin1String("::%CLASS_NAME::"));
+ methodCall.clear();
+ mc << "Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(self))\n"
+ << INDENT << " ? " << virtualCall << '\n'
+ << INDENT << " : " << normalCall;
+ }
+ }
+ }
+ }
+
+ if (!injectedCodeCallsCppFunction(context, func)) {
+ const bool allowThread = func->allowThread();
+ const bool generateExceptionHandling = func->generateExceptionHandling();
+ if (generateExceptionHandling) {
+ s << INDENT << "try {\n";
+ ++INDENT.indent;
+ if (allowThread) {
+ s << INDENT << "Shiboken::ThreadStateSaver threadSaver;\n"
+ << INDENT << "threadSaver.save();\n";
+ }
+ } else if (allowThread) {
+ s << INDENT << BEGIN_ALLOW_THREADS << Qt::endl;
+ }
+ s << INDENT;
+ if (isCtor) {
+ s << (useVAddr.isEmpty() ?
+ QString::fromLatin1("cptr = %1;").arg(methodCall) : useVAddr) << Qt::endl;
+ } else if (!func->isVoid() && !func->isInplaceOperator()) {
+ bool writeReturnType = true;
+ if (avoidProtectedHack()) {
+ const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(func->type());
+ if (metaEnum) {
+ QString enumName;
+ if (metaEnum->isProtected())
+ enumName = protectedEnumSurrogateName(metaEnum);
+ else
+ enumName = func->type().cppSignature();
+ methodCall.prepend(enumName + QLatin1Char('('));
+ methodCall.append(QLatin1Char(')'));
+ s << enumName;
+ writeReturnType = false;
+ }
+ }
+ if (writeReturnType) {
+ s << func->type().cppSignature();
+ if (isObjectTypeUsedAsValueType(func->type())) {
+ s << '*';
+ methodCall.prepend(QString::fromLatin1("new %1(").arg(func->type().typeEntry()->qualifiedCppName()));
+ methodCall.append(QLatin1Char(')'));
+ }
+ }
+ s << " " << CPP_RETURN_VAR << " = ";
+ s << methodCall << ";\n";
+ } else {
+ s << methodCall << ";\n";
+ }
+
+ if (allowThread) {
+ s << INDENT << (generateExceptionHandling
+ ? "threadSaver.restore();" : END_ALLOW_THREADS) << '\n';
+ }
+
+ // Convert result
+ if (!func->conversionRule(TypeSystem::TargetLangCode, 0).isEmpty()) {
+ writeConversionRule(s, func, TypeSystem::TargetLangCode, QLatin1String(PYTHON_RETURN_VAR));
+ } else if (!isCtor && !func->isInplaceOperator() && !func->isVoid()
+ && !injectedCodeHasReturnValueAttribution(func, TypeSystem::TargetLangCode)) {
+ s << INDENT << PYTHON_RETURN_VAR << " = ";
+ if (isObjectTypeUsedAsValueType(func->type())) {
+ s << "Shiboken::Object::newObject(reinterpret_cast<SbkObjectType *>(" << cpythonTypeNameExt(func->type().typeEntry())
+ << "), " << CPP_RETURN_VAR << ", true, true)";
+ } else {
+ writeToPythonConversion(s, func->type(), func->ownerClass(), QLatin1String(CPP_RETURN_VAR));
+ }
+ s << ";\n";
+ }
+
+ if (generateExceptionHandling) { // "catch" code
+ --INDENT.indent;
+ const QStringList handlingCode = defaultExceptionHandling();
+ for (const auto &line : handlingCode)
+ s << INDENT << line << '\n';
+ }
+ }
+ }
+
+ if (func->hasInjectedCode() && !func->isConstructor())
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode, func, lastArg);
+
+ bool hasReturnPolicy = false;
+
+ // Ownership transference between C++ and Python.
+ QVector<ArgumentModification> ownership_mods;
+ // Python object reference management.
+ QVector<ArgumentModification> refcount_mods;
+ const FunctionModificationList &funcMods = func->modifications();
+ for (const FunctionModification &func_mod : funcMods) {
+ for (const ArgumentModification &arg_mod : func_mod.argument_mods) {
+ if (!arg_mod.ownerships.isEmpty() && arg_mod.ownerships.contains(TypeSystem::TargetLangCode))
+ 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 << Qt::endl << INDENT << "// Ownership transferences.\n";
+ for (const ArgumentModification &arg_mod : qAsConst(ownership_mods)) {
+ const AbstractMetaClass *wrappedClass = nullptr;
+ QString pyArgName = argumentNameFromIndex(func, arg_mod.index, &wrappedClass);
+ if (!wrappedClass) {
+ s << "#error Invalid ownership modification for argument " << arg_mod.index << '(' << pyArgName << ")\n" << Qt::endl;
+ break;
+ }
+
+ if (arg_mod.index == 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.
+ if (arg_mod.ownerships[TypeSystem::TargetLangCode] == TypeSystem::DefaultOwnership)
+ continue;
+
+ s << INDENT << "Shiboken::Object::";
+ if (arg_mod.ownerships[TypeSystem::TargetLangCode] == TypeSystem::TargetLangOwnership) {
+ s << "getOwnership(" << pyArgName << ");";
+ } else if (wrappedClass->hasVirtualDestructor()) {
+ if (arg_mod.index == 0)
+ s << "releaseOwnership(" << PYTHON_RETURN_VAR << ");";
+ else
+ s << "releaseOwnership(" << pyArgName << ");";
+ } else {
+ s << "invalidate(" << pyArgName << ");";
+ }
+ s << Qt::endl;
+ }
+
+ } else if (!refcount_mods.isEmpty()) {
+ for (const ArgumentModification &arg_mod : qAsConst(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 AbstractMetaClass *wrappedClass = nullptr;
+
+ QString pyArgName;
+ if (refCount.action == ReferenceCount::Remove) {
+ pyArgName = QLatin1String("Py_None");
+ } else {
+ pyArgName = argumentNameFromIndex(func, arg_mod.index, &wrappedClass);
+ if (pyArgName.isEmpty()) {
+ s << "#error Invalid reference count modification for argument " << arg_mod.index << Qt::endl << Qt::endl;
+ break;
+ }
+ }
+
+ if (refCount.action == ReferenceCount::Add || refCount.action == ReferenceCount::Set)
+ s << INDENT << "Shiboken::Object::keepReference(";
+ else
+ s << INDENT << "Shiboken::Object::removeReference(";
+
+ s << "reinterpret_cast<SbkObject *>(self), \"";
+ QString varName = arg_mod.referenceCounts.constFirst().varName;
+ if (varName.isEmpty())
+ varName = func->minimalSignature() + QString::number(arg_mod.index);
+
+ s << varName << "\", " << pyArgName
+ << (refCount.action == ReferenceCount::Add ? ", true" : "")
+ << ");\n";
+
+ if (arg_mod.index == 0)
+ hasReturnPolicy = true;
+ }
+ }
+ writeParentChildManagement(s, func, !hasReturnPolicy);
+}
+
+QStringList CppGenerator::getAncestorMultipleInheritance(const AbstractMetaClass *metaClass)
+{
+ QStringList result;
+ const AbstractMetaClassList &baseClases = getBaseClasses(metaClass);
+ if (!baseClases.isEmpty()) {
+ for (const AbstractMetaClass *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 AbstractMetaClass *baseClass : baseClases)
+ result.append(getAncestorMultipleInheritance(baseClass));
+ }
+ return result;
+}
+
+void CppGenerator::writeMultipleInheritanceInitializerFunction(QTextStream &s, const AbstractMetaClass *metaClass)
+{
+ QString className = metaClass->qualifiedCppName();
+ const QStringList ancestors = getAncestorMultipleInheritance(metaClass);
+ s << "static int mi_offsets[] = { ";
+ for (int i = 0; i < ancestors.size(); i++)
+ s << "-1, ";
+ s << "-1 };\n";
+ s << "int *\n";
+ s << multipleInheritanceInitializerFunctionName(metaClass) << "(const void *cptr)\n";
+ s << "{\n";
+ s << INDENT << "if (mi_offsets[0] == -1) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "std::set<int> offsets;\n";
+ s << INDENT << "const auto *class_ptr = reinterpret_cast<const " << className << " *>(cptr);\n";
+ s << INDENT << "const auto base = reinterpret_cast<uintptr_t>(class_ptr);\n";
+
+ for (const QString &ancestor : ancestors)
+ s << INDENT << "offsets.insert(int(" << ancestor << "));\n";
+
+ s << Qt::endl;
+ s << INDENT << "offsets.erase(0);\n";
+ s << Qt::endl;
+
+ s << INDENT << "std::copy(offsets.cbegin(), offsets.cend(), mi_offsets);\n";
+ }
+ s << INDENT << "}\n";
+ s << INDENT << "return mi_offsets;\n";
+ s << "}\n";
+}
+
+void CppGenerator::writeSpecialCastFunction(QTextStream &s, const AbstractMetaClass *metaClass)
+{
+ QString className = metaClass->qualifiedCppName();
+ s << "static void * " << cpythonSpecialCastFunctionName(metaClass) << "(void *obj, SbkObjectType *desiredType)\n";
+ s << "{\n";
+ s << INDENT << "auto me = reinterpret_cast< ::" << className << " *>(obj);\n";
+ bool firstClass = true;
+ const AbstractMetaClassList &allAncestors = getAllAncestors(metaClass);
+ for (const AbstractMetaClass *baseClass : allAncestors) {
+ s << INDENT << (!firstClass ? "else " : "") << "if (desiredType == reinterpret_cast<SbkObjectType *>(" << cpythonTypeNameExt(baseClass->typeEntry()) << "))\n";
+ Indentation indent(INDENT);
+ s << INDENT << "return static_cast< ::" << baseClass->qualifiedCppName() << " *>(me);\n";
+ firstClass = false;
+ }
+ s << INDENT << "return me;\n";
+ s << "}\n\n";
+}
+
+void CppGenerator::writePrimitiveConverterInitialization(QTextStream &s, const CustomConversion *customConversion)
+{
+ const TypeEntry *type = customConversion->ownerType();
+ QString converter = converterObject(type);
+ s << INDENT << "// Register converter for type '" << type->qualifiedTargetLangName() << "'.\n";
+ s << INDENT << converter << " = Shiboken::Conversions::createConverter(";
+ if (type->targetLangApiName() == type->name())
+ s << '0';
+ else if (type->targetLangApiName() == QLatin1String("PyObject"))
+ s << "&PyBaseObject_Type";
+ else
+ s << '&' << type->targetLangApiName() << "_Type";
+ QString typeName = fixedCppTypeName(type);
+ s << ", " << cppToPythonFunctionName(typeName, typeName) << ");\n";
+ s << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << type->qualifiedCppName() << "\");\n";
+ writeCustomConverterRegister(s, customConversion, converter);
+}
+
+void CppGenerator::writeEnumConverterInitialization(QTextStream &s, const AbstractMetaEnum *metaEnum)
+{
+ if (metaEnum->isPrivate() || metaEnum->isAnonymous())
+ return;
+ writeEnumConverterInitialization(s, metaEnum->typeEntry());
+}
+
+void CppGenerator::writeEnumConverterInitialization(QTextStream &s, const TypeEntry *enumType)
+{
+ if (!enumType)
+ return;
+ QString enumFlagName = enumType->isFlags() ? QLatin1String("flag") : QLatin1String("enum");
+ QString enumPythonType = cpythonTypeNameExt(enumType);
+
+ const FlagsTypeEntry *flags = nullptr;
+ if (enumType->isFlags())
+ flags = static_cast<const FlagsTypeEntry *>(enumType);
+
+ s << INDENT << "// Register converter for " << enumFlagName << " '" << enumType->qualifiedCppName() << "'.\n";
+ s << INDENT << "{\n";
+ {
+ Indentation indent(INDENT);
+ QString typeName = fixedCppTypeName(enumType);
+ s << INDENT << "SbkConverter *converter = Shiboken::Conversions::createConverter(" << enumPythonType << ',' << Qt::endl;
+ {
+ Indentation indent(INDENT);
+ s << INDENT << cppToPythonFunctionName(typeName, typeName) << ");\n";
+ }
+
+ if (flags) {
+ QString enumTypeName = fixedCppTypeName(flags->originator());
+ QString toCpp = pythonToCppFunctionName(enumTypeName, typeName);
+ QString isConv = convertibleToCppFunctionName(enumTypeName, typeName);
+ writeAddPythonToCppConversion(s, QLatin1String("converter"), toCpp, isConv);
+ }
+
+ QString toCpp = pythonToCppFunctionName(typeName, typeName);
+ QString isConv = convertibleToCppFunctionName(typeName, typeName);
+ writeAddPythonToCppConversion(s, QLatin1String("converter"), toCpp, isConv);
+
+ if (flags) {
+ QString toCpp = pythonToCppFunctionName(QLatin1String("number"), typeName);
+ QString isConv = convertibleToCppFunctionName(QLatin1String("number"), typeName);
+ writeAddPythonToCppConversion(s, QLatin1String("converter"), toCpp, isConv);
+ }
+
+ s << INDENT << "Shiboken::Enum::setTypeConverter(" << enumPythonType << ", converter);\n";
+
+ QString signature = enumType->qualifiedCppName();
+ // Replace "QFlags<Class::Option>" by "Class::Options"
+ if (flags && signature.startsWith(QLatin1String("QFlags<")) && signature.endsWith(QLatin1Char('>'))) {
+ signature.chop(1);
+ signature.remove(0, 7);
+ const int lastQualifierPos = signature.lastIndexOf(QLatin1String("::"));
+ if (lastQualifierPos != -1) {
+ signature.replace(lastQualifierPos + 2, signature.size() - lastQualifierPos - 2,
+ flags->flagsName());
+ } else {
+ signature = flags->flagsName();
+ }
+ }
+
+ while (true) {
+ s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \""
+ << signature << "\");\n";
+ const int qualifierPos = signature.indexOf(QLatin1String("::"));
+ if (qualifierPos != -1)
+ signature.remove(0, qualifierPos + 2);
+ else
+ break;
+ }
+ }
+ s << INDENT << "}\n";
+
+ if (!flags)
+ writeEnumConverterInitialization(s, static_cast<const EnumTypeEntry *>(enumType)->flags());
+}
+
+void CppGenerator::writeContainerConverterInitialization(QTextStream &s, const AbstractMetaType &type)
+{
+ QByteArray cppSignature = QMetaObject::normalizedSignature(type.cppSignature().toUtf8());
+ s << INDENT << "// Register converter for type '" << cppSignature << "'.\n";
+ QString converter = converterObject(type);
+ s << INDENT << converter << " = Shiboken::Conversions::createConverter(";
+ if (type.typeEntry()->targetLangApiName() == QLatin1String("PyObject")) {
+ s << "&PyBaseObject_Type";
+ } else {
+ QString baseName = cpythonBaseName(type.typeEntry());
+ if (baseName == QLatin1String("PySequence"))
+ baseName = QLatin1String("PyList");
+ s << '&' << baseName << "_Type";
+ }
+ QString typeName = fixedCppTypeName(type);
+ s << ", " << cppToPythonFunctionName(typeName, typeName) << ");\n";
+ QString toCpp = pythonToCppFunctionName(typeName, typeName);
+ QString isConv = convertibleToCppFunctionName(typeName, typeName);
+ s << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << cppSignature << "\");\n";
+ if (usePySideExtensions() && cppSignature.startsWith("const ") && cppSignature.endsWith("&")) {
+ cppSignature.chop(1);
+ cppSignature.remove(0, sizeof("const ") / sizeof(char) - 1);
+ s << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << cppSignature << "\");\n";
+ }
+ writeAddPythonToCppConversion(s, converterObject(type), toCpp, isConv);
+}
+
+void CppGenerator::writeSmartPointerConverterInitialization(QTextStream &s, const AbstractMetaType &type)
+{
+ const QByteArray cppSignature = type.cppSignature().toUtf8();
+ auto writeConversionRegister = [this, &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);
+ };
+
+ auto klass = AbstractMetaClass::findClass(classes(), type.instantiations().at(0).typeEntry());
+ if (!klass)
+ return;
+
+ const auto classes = getBaseClasses(klass);
+ if (classes.isEmpty())
+ return;
+
+ s << INDENT << "// Register SmartPointer converter for type '" << cppSignature << "'." << Qt::endl;
+ s << INDENT << "///////////////////////////////////////////////////////////////////////////////////////"<< Qt::endl;
+ s << Qt::endl;
+
+ for (auto k : classes) {
+ if (auto smartTargetType = findSmartPointerInstantiation(k->typeEntry()))
+ {
+ s << INDENT << "// Convert to SmartPointer derived class: [" << smartTargetType.cppSignature() << "]\n";
+ const QString converter = QLatin1String("Shiboken::Conversions::getConverter(\"%1\")").arg(smartTargetType.cppSignature());
+ writeConversionRegister(type, fixedCppTypeName(smartTargetType), converter);
+ } else {
+ s << INDENT << "// Class not found:" << type.instantiations().at(0).cppSignature();
+ }
+ }
+
+ s << INDENT << "///////////////////////////////////////////////////////////////////////////////////////"<< Qt::endl << Qt::endl;
+}
+
+void CppGenerator::writeExtendedConverterInitialization(QTextStream &s, const TypeEntry *externalType,
+ const QVector<const AbstractMetaClass *>& conversions)
+{
+ s << INDENT << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName() << '.' << Qt::endl;
+ for (const AbstractMetaClass *sourceClass : conversions) {
+ const QString converterVar = QLatin1String("reinterpret_cast<SbkObjectType *>(")
+ + cppApiVariableName(externalType->targetLangPackage()) + QLatin1Char('[')
+ + getTypeIndexVariableName(externalType) + QLatin1String("])");
+ QString sourceTypeName = fixedCppTypeName(sourceClass->typeEntry());
+ QString targetTypeName = fixedCppTypeName(externalType);
+ QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName);
+ QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName);
+ writeAddPythonToCppConversion(s, converterVar, toCpp, isConv);
+ }
+}
+
+QString CppGenerator::multipleInheritanceInitializerFunctionName(const AbstractMetaClass *metaClass)
+{
+ return cpythonBaseName(metaClass->typeEntry()) + QLatin1String("_mi_init");
+}
+
+bool CppGenerator::supportsMappingProtocol(const AbstractMetaClass *metaClass)
+{
+ for (auto it = m_mappingProtocol.cbegin(), end = m_mappingProtocol.cend(); it != end; ++it) {
+ if (metaClass->hasFunction(it.key()))
+ return true;
+ }
+
+ return false;
+}
+
+bool CppGenerator::supportsNumberProtocol(const AbstractMetaClass *metaClass)
+{
+ return metaClass->hasArithmeticOperatorOverload()
+ || metaClass->hasLogicalOperatorOverload()
+ || metaClass->hasBitwiseOperatorOverload()
+ || hasBoolCast(metaClass);
+}
+
+bool CppGenerator::supportsSequenceProtocol(const AbstractMetaClass *metaClass)
+{
+ for (auto it = m_sequenceProtocol.cbegin(), end = m_sequenceProtocol.cend(); it != end; ++it) {
+ if (metaClass->hasFunction(it.key()))
+ return true;
+ }
+
+ const ComplexTypeEntry *baseType = metaClass->typeEntry()->baseContainerType();
+ return baseType && baseType->isContainer();
+}
+
+bool CppGenerator::shouldGenerateGetSetList(const AbstractMetaClass *metaClass)
+{
+ const AbstractMetaFieldList &fields = metaClass->fields();
+ for (const AbstractMetaField *f : 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(const char *name, const QString &function) :
+ m_name(name), m_function(function) {}
+
+ const char *m_name;
+ const QString &m_function;
+};
+
+QTextStream &operator<<(QTextStream &str, const pyTypeSlotEntry &e)
+{
+ str << '{' << e.m_name << ',';
+ const int padding = qMax(0, 18 - int(strlen(e.m_name)));
+ for (int p = 0; p < padding; ++p)
+ str << ' ';
+ if (e.m_function.isEmpty())
+ str << NULL_PTR;
+ else
+ str << "reinterpret_cast<void *>(" << e.m_function << ')';
+ str << "},\n";
+ return str;
+}
+
+void CppGenerator::writeClassDefinition(QTextStream &s,
+ const AbstractMetaClass *metaClass,
+ const GeneratorContext &classContext)
+{
+ QString tp_flags;
+ QString tp_init;
+ QString tp_new;
+ QString tp_dealloc;
+ QString tp_hash;
+ QString tp_call;
+ QString cppClassName = metaClass->qualifiedCppName();
+ const QString className = chopType(cpythonTypeName(metaClass));
+ QString baseClassName;
+ AbstractMetaFunctionList ctors;
+ const AbstractMetaFunctionList &allCtors = metaClass->queryFunctions(AbstractMetaClass::Constructors);
+ for (AbstractMetaFunction *f : allCtors) {
+ if (!f->isPrivate() && !f->isModifiedRemoved() && !classContext.forSmartPointer())
+ ctors.append(f);
+ }
+
+ if (!metaClass->baseClass())
+ baseClassName = QLatin1String("reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())");
+
+ bool onlyPrivCtor = !metaClass->hasNonPrivateConstructor();
+
+ const AbstractMetaClass *qCoreApp = AbstractMetaClass::findClass(classes(), QLatin1String("QCoreApplication"));
+ const bool isQApp = qCoreApp != Q_NULLPTR && metaClass->inheritsFrom(qCoreApp);
+
+ tp_flags = QLatin1String("Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES");
+ if (metaClass->isNamespace() || metaClass->hasPrivateDestructor()) {
+ tp_dealloc = metaClass->hasPrivateDestructor() ?
+ QLatin1String("SbkDeallocWrapperWithPrivateDtor") :
+ QLatin1String("Sbk_object_dealloc /* PYSIDE-832: Prevent replacement of \"0\" with subtype_dealloc. */");
+ tp_init.clear();
+ } else {
+ QString deallocClassName = classContext.useWrapper()
+ ? classContext.wrapperName() : cppClassName;
+ if (isQApp)
+ tp_dealloc = QLatin1String("&SbkDeallocQAppWrapper");
+ else
+ tp_dealloc = QLatin1String("&SbkDeallocWrapper");
+ 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 = QLatin1String("Py_TPFLAGS_DEFAULT|Py_TPFLAGS_CHECKTYPES");
+ // 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 SbkObjectTypeTpNew.
+ if (metaClass->fullName().startsWith(QLatin1String("PySide2.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 = QLatin1String("SbkObjectTpNew");
+ }
+ else {
+ tp_new = QLatin1String("SbkDummyNew /* PYSIDE-595: Prevent replacement "
+ "of \"0\" with base->tp_new. */");
+ }
+ tp_flags.append(QLatin1String("|Py_TPFLAGS_HAVE_GC"));
+ }
+ else if (isQApp) {
+ tp_new = QLatin1String("SbkQAppTpNew"); // PYSIDE-571: need singleton app
+ }
+ else {
+ tp_new = QLatin1String("SbkObjectTpNew");
+ tp_flags.append(QLatin1String("|Py_TPFLAGS_HAVE_GC"));
+ }
+
+ QString tp_richcompare;
+ if (!metaClass->isNamespace() && metaClass->hasComparisonOperatorOverload())
+ tp_richcompare = cpythonBaseName(metaClass) + QLatin1String("_richcompare");
+
+ QString tp_getset;
+ if (shouldGenerateGetSetList(metaClass) && !classContext.forSmartPointer())
+ tp_getset = cpythonGettersSettersDefinitionName(metaClass);
+
+ // search for special functions
+ ShibokenGenerator::clearTpFuncs();
+ const AbstractMetaFunctionList &funcs = metaClass->functions();
+ for (AbstractMetaFunction *func : funcs) {
+ if (m_tpFuncs.contains(func->name()))
+ m_tpFuncs[func->name()] = cpythonFunctionName(func);
+ }
+ if (m_tpFuncs.value(QLatin1String("__repr__")).isEmpty()
+ && metaClass->hasToStringCapability()) {
+ m_tpFuncs[QLatin1String("__repr__")] = writeReprFunction(s,
+ classContext,
+ metaClass->toStringCapabilityIndirections());
+ }
+
+ // class or some ancestor has multiple inheritance
+ const AbstractMetaClass *miClass = getMultipleInheritingClass(metaClass);
+ if (miClass) {
+ if (metaClass == miClass)
+ writeMultipleInheritanceInitializerFunction(s, metaClass);
+ writeSpecialCastFunction(s, metaClass);
+ s << Qt::endl;
+ }
+
+ s << "// Class Definition -----------------------------------------------\n";
+ s << "extern \"C\" {\n";
+
+ if (!metaClass->typeEntry()->hashFunction().isEmpty())
+ tp_hash = QLatin1Char('&') + cpythonBaseName(metaClass) + QLatin1String("_HashFunc");
+
+ const AbstractMetaFunction *callOp = metaClass->findFunction(QLatin1String("operator()"));
+ if (callOp && !callOp->isModifiedRemoved())
+ tp_call = QLatin1Char('&') + cpythonFunctionName(callOp);
+
+ QString computedClassTargetFullName;
+ if (!classContext.forSmartPointer())
+ computedClassTargetFullName = getClassTargetFullName(metaClass);
+ else
+ computedClassTargetFullName = getClassTargetFullName(classContext.preciseType());
+
+ QString suffix;
+ if (isObjectType(metaClass))
+ suffix = QLatin1String(" *");
+ const QString typePtr = QLatin1String("_") + className
+ + QLatin1String("_Type");
+ s << "static SbkObjectType *" << typePtr << " = nullptr;\n";
+ s << "static SbkObjectType *" << className << "_TypeF(void)\n";
+ s << "{\n";
+ s << INDENT << "return " << typePtr << ";\n";
+ s << "}\n";
+ s << Qt::endl;
+ s << "static PyType_Slot " << className << "_slots[] = {\n";
+ s << INDENT << "{Py_tp_base, nullptr}, // inserted by introduceWrapperType\n";
+ s << INDENT << pyTypeSlotEntry("Py_tp_dealloc", tp_dealloc)
+ << INDENT << pyTypeSlotEntry("Py_tp_repr", m_tpFuncs.value(QLatin1String("__repr__")))
+ << INDENT << pyTypeSlotEntry("Py_tp_hash", tp_hash)
+ << INDENT << pyTypeSlotEntry("Py_tp_call", tp_call)
+ << INDENT << pyTypeSlotEntry("Py_tp_str", m_tpFuncs.value(QLatin1String("__str__")))
+ << INDENT << pyTypeSlotEntry("Py_tp_getattro", tp_getattro)
+ << INDENT << pyTypeSlotEntry("Py_tp_setattro", tp_setattro)
+ << INDENT << pyTypeSlotEntry("Py_tp_traverse", className + QLatin1String("_traverse"))
+ << INDENT << pyTypeSlotEntry("Py_tp_clear", className + QLatin1String("_clear"))
+ << INDENT << pyTypeSlotEntry("Py_tp_richcompare", tp_richcompare)
+ << INDENT << pyTypeSlotEntry("Py_tp_iter", m_tpFuncs.value(QLatin1String("__iter__")))
+ << INDENT << pyTypeSlotEntry("Py_tp_iternext", m_tpFuncs.value(QLatin1String("__next__")))
+ << INDENT << pyTypeSlotEntry("Py_tp_methods", className + QLatin1String("_methods"))
+ << INDENT << pyTypeSlotEntry("Py_tp_getset", tp_getset)
+ << INDENT << pyTypeSlotEntry("Py_tp_init", tp_init)
+ << INDENT << pyTypeSlotEntry("Py_tp_new", tp_new);
+ if (supportsSequenceProtocol(metaClass)) {
+ s << INDENT << "// type supports sequence protocol\n";
+ writeTypeAsSequenceDefinition(s, metaClass);
+ }
+ if (supportsMappingProtocol(metaClass)) {
+ s << INDENT << "// type supports mapping protocol\n";
+ writeTypeAsMappingDefinition(s, metaClass);
+ }
+ if (supportsNumberProtocol(metaClass)) {
+ // This one must come last. See the function itself.
+ s << INDENT << "// type supports number protocol\n";
+ writeTypeAsNumberDefinition(s, metaClass);
+ }
+ s << INDENT << "{0, " << NULL_PTR << "}\n";
+ s << "};\n";
+
+ int packageLevel = packageName().count(QLatin1Char('.')) + 1;
+ s << "static PyType_Spec " << className << "_spec = {\n";
+ s << INDENT << '"' << packageLevel << ':' << computedClassTargetFullName << "\",\n";
+ s << INDENT << "sizeof(SbkObject),\n";
+ s << INDENT << "0,\n";
+ s << INDENT << tp_flags << ",\n";
+ s << INDENT << className << "_slots\n";
+ s << "};\n";
+ s << Qt::endl;
+ s << "} //extern \"C\"" << Qt::endl;
+}
+
+void CppGenerator::writeMappingMethods(QTextStream &s,
+ const AbstractMetaClass *metaClass,
+ const GeneratorContext &context)
+{
+ for (auto it = m_mappingProtocol.cbegin(), end = m_mappingProtocol.cend(); it != end; ++it) {
+ const AbstractMetaFunction *func = metaClass->findFunction(it.key());
+ if (!func)
+ continue;
+ QString funcName = cpythonFunctionName(func);
+ QString funcArgs = it.value().first;
+ QString funcRetVal = it.value().second;
+
+ CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode);
+ s << funcRetVal << ' ' << funcName << '(' << funcArgs << ")\n{\n";
+ writeInvalidPyObjectCheck(s, QLatin1String("self"));
+
+ writeCppSelfDefinition(s, func, context);
+
+ const AbstractMetaArgument *lastArg = func->arguments().isEmpty()
+ ? nullptr : &func->arguments().constLast();
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode, func, lastArg);
+ s<< "}\n\n";
+ }
+}
+
+void CppGenerator::writeSequenceMethods(QTextStream &s,
+ const AbstractMetaClass *metaClass,
+ const GeneratorContext &context)
+{
+ bool injectedCode = false;
+
+ for (auto it = m_sequenceProtocol.cbegin(), end = m_sequenceProtocol.cend(); it != end; ++it) {
+ const AbstractMetaFunction *func = metaClass->findFunction(it.key());
+ if (!func)
+ continue;
+ injectedCode = true;
+ QString funcName = cpythonFunctionName(func);
+ QString funcArgs = it.value().first;
+ QString funcRetVal = it.value().second;
+
+ CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode);
+ s << funcRetVal << ' ' << funcName << '(' << funcArgs << ")\n{\n";
+ writeInvalidPyObjectCheck(s, QLatin1String("self"));
+
+ writeCppSelfDefinition(s, func, context);
+
+ const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast();
+ writeCodeSnips(s, snips,TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode, func, lastArg);
+ s<< "}\n\n";
+ }
+
+ if (!injectedCode)
+ writeDefaultSequenceMethods(s, context);
+}
+
+void CppGenerator::writeTypeAsSequenceDefinition(QTextStream &s, const AbstractMetaClass *metaClass)
+{
+ bool hasFunctions = false;
+ QMap<QString, QString> funcs;
+ for (auto it = m_sequenceProtocol.cbegin(), end = m_sequenceProtocol.cend(); it != end; ++it) {
+ const QString &funcName = it.key();
+ const AbstractMetaFunction *func = metaClass->findFunction(funcName);
+ funcs[funcName] = func ? cpythonFunctionName(func).prepend(QLatin1Char('&')) : QString();
+ if (!hasFunctions && func)
+ hasFunctions = true;
+ }
+
+ QString baseName = cpythonBaseName(metaClass);
+
+ //use default implementation
+ if (!hasFunctions) {
+ funcs[QLatin1String("__len__")] = baseName + QLatin1String("__len__");
+ funcs[QLatin1String("__getitem__")] = baseName + QLatin1String("__getitem__");
+ funcs[QLatin1String("__setitem__")] = baseName + QLatin1String("__setitem__");
+ }
+
+ for (QHash<QString, QString>::const_iterator it = m_sqFuncs.cbegin(), end = m_sqFuncs.cend(); it != end; ++it) {
+ const QString &sqName = it.key();
+ if (funcs[sqName].isEmpty())
+ continue;
+ s << INDENT << "{Py_" << it.value() << ", (void *)" << funcs[sqName] << "},\n";
+ }
+}
+
+void CppGenerator::writeTypeAsMappingDefinition(QTextStream &s, const AbstractMetaClass *metaClass)
+{
+ bool hasFunctions = false;
+ QMap<QString, QString> funcs;
+ for (auto it = m_mappingProtocol.cbegin(), end = m_mappingProtocol.cend(); it != end; ++it) {
+ const QString &funcName = it.key();
+ const AbstractMetaFunction *func = metaClass->findFunction(funcName);
+ funcs[funcName] = func ? cpythonFunctionName(func).prepend(QLatin1Char('&')) : QLatin1String("0");
+ if (!hasFunctions && func)
+ hasFunctions = true;
+ }
+
+ //use default implementation
+ if (!hasFunctions) {
+ funcs.insert(QLatin1String("__mlen__"), QString());
+ funcs.insert(QLatin1String("__mgetitem__"), QString());
+ funcs.insert(QLatin1String("__msetitem__"), QString());
+ }
+
+ for (auto it = m_mpFuncs.cbegin(), end = m_mpFuncs.cend(); it != end; ++it) {
+ const QString &mpName = it.key();
+ if (funcs[mpName].isEmpty())
+ continue;
+ s << INDENT << "{Py_" << it.value() << ", (void *)" << funcs[mpName] << "},\n";
+ }
+}
+
+void CppGenerator::writeTypeAsNumberDefinition(QTextStream &s, const AbstractMetaClass *metaClass)
+{
+ QMap<QString, QString> nb;
+
+ nb.insert(QLatin1String("__add__"), QString());
+ nb.insert(QLatin1String("__sub__"), QString());
+ nb.insert(QLatin1String("__mul__"), QString());
+ nb.insert(QLatin1String("__div__"), QString());
+ nb.insert(QLatin1String("__mod__"), QString());
+ nb.insert(QLatin1String("__neg__"), QString());
+ nb.insert(QLatin1String("__pos__"), QString());
+ nb.insert(QLatin1String("__invert__"), QString());
+ nb.insert(QLatin1String("__lshift__"), QString());
+ nb.insert(QLatin1String("__rshift__"), QString());
+ nb.insert(QLatin1String("__and__"), QString());
+ nb.insert(QLatin1String("__xor__"), QString());
+ nb.insert(QLatin1String("__or__"), QString());
+ nb.insert(QLatin1String("__iadd__"), QString());
+ nb.insert(QLatin1String("__isub__"), QString());
+ nb.insert(QLatin1String("__imul__"), QString());
+ nb.insert(QLatin1String("__idiv__"), QString());
+ nb.insert(QLatin1String("__imod__"), QString());
+ nb.insert(QLatin1String("__ilshift__"), QString());
+ nb.insert(QLatin1String("__irshift__"), QString());
+ nb.insert(QLatin1String("__iand__"), QString());
+ nb.insert(QLatin1String("__ixor__"), QString());
+ nb.insert(QLatin1String("__ior__"), QString());
+
+ const QVector<AbstractMetaFunctionList> opOverloads =
+ filterGroupedOperatorFunctions(metaClass,
+ AbstractMetaClass::ArithmeticOp
+ | AbstractMetaClass::LogicalOp
+ | AbstractMetaClass::BitwiseOp);
+
+ for (const AbstractMetaFunctionList &opOverload : opOverloads) {
+ const AbstractMetaFunction *rfunc = opOverload.at(0);
+ QString opName = ShibokenGenerator::pythonOperatorFunctionName(rfunc);
+ nb[opName] = cpythonFunctionName(rfunc);
+ }
+
+ QString baseName = cpythonBaseName(metaClass);
+
+ if (hasBoolCast(metaClass))
+ nb.insert(QLatin1String("bool"), baseName + QLatin1String("___nb_bool"));
+
+ for (QHash<QString, QString>::const_iterator it = m_nbFuncs.cbegin(), end = m_nbFuncs.cend(); it != end; ++it) {
+ const QString &nbName = it.key();
+ if (nb[nbName].isEmpty())
+ continue;
+
+ if (nbName == QLatin1String("bool")) {
+ s << INDENT << "{Py_nb_bool, (void *)" << nb[nbName] << "},\n";
+ } else {
+ bool excludeFromPy3K = nbName == QLatin1String("__div__") || nbName == QLatin1String("__idiv__");
+ if (!excludeFromPy3K)
+ s << INDENT << "{Py_" << it.value() << ", (void *)" << nb[nbName] << "},\n";
+ }
+ }
+ if (!nb[QLatin1String("__div__")].isEmpty())
+ s << INDENT << "{Py_nb_true_divide, (void *)" << nb[QLatin1String("__div__")] << "},\n";
+
+ if (!nb[QLatin1String("__idiv__")].isEmpty()) {
+ s << INDENT << "// This function is unused in Python 3. We reference it here.\n";
+ s << INDENT << "{0, (void *)" << nb[QLatin1String("__idiv__")] << "},\n";
+ s << INDENT << "// This list is ending at the first 0 entry.\n";
+ s << INDENT << "// Therefore, we need to put the unused functions at the very end.\n";
+ }
+}
+
+void CppGenerator::writeTpTraverseFunction(QTextStream &s, const AbstractMetaClass *metaClass)
+{
+ QString baseName = cpythonBaseName(metaClass);
+ s << "static int ";
+ s << baseName << "_traverse(PyObject *self, visitproc visit, void *arg)\n";
+ s << "{\n";
+ s << INDENT << "return reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())->tp_traverse(self, visit, arg);\n";
+ s << "}\n";
+}
+
+void CppGenerator::writeTpClearFunction(QTextStream &s, const AbstractMetaClass *metaClass)
+{
+ QString baseName = cpythonBaseName(metaClass);
+ s << "static int ";
+ s << baseName << "_clear(PyObject *self)\n";
+ s << "{\n";
+ s << INDENT << "return reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())->tp_clear(self);\n";
+ s << "}\n";
+}
+
+void CppGenerator::writeCopyFunction(QTextStream &s, const GeneratorContext &context)
+{
+ const AbstractMetaClass *metaClass = context.metaClass();
+ const QString className = chopType(cpythonTypeName(metaClass));
+ s << "static PyObject *" << className << "___copy__(PyObject *self)\n";
+ s << "{\n";
+ writeCppSelfDefinition(s, context, false, true);
+ QString conversionCode;
+ if (!context.forSmartPointer())
+ conversionCode = cpythonToPythonConversionFunction(metaClass);
+ else
+ conversionCode = cpythonToPythonConversionFunction(context.preciseType());
+
+ s << INDENT << "PyObject *" << PYTHON_RETURN_VAR << " = " << conversionCode;
+ s << CPP_SELF_VAR << ");\n";
+ writeFunctionReturnErrorCheckSection(s);
+ s << INDENT << "return " << PYTHON_RETURN_VAR << ";\n";
+ s << "}\n";
+ s << Qt::endl;
+}
+
+static inline void writeGetterFunctionStart(QTextStream &s, const QString &funcName)
+{
+ s << "static PyObject *" << funcName << "(PyObject *self, void *)\n";
+ s << "{\n";
+}
+
+void CppGenerator::writeGetterFunction(QTextStream &s,
+ const AbstractMetaField *metaField,
+ const GeneratorContext &context)
+{
+ ErrorCode errorCode(QString::fromLatin1(NULL_PTR));
+ writeGetterFunctionStart(s, cpythonGetterFunctionName(metaField));
+
+ writeCppSelfDefinition(s, context);
+
+ AbstractMetaType fieldType = metaField->type();
+ // Force use of pointer to return internal variable memory
+ bool newWrapperSameObject = !fieldType.isConstant() && isWrapperType(fieldType) && !isPointer(fieldType);
+
+ QString cppField;
+ if (avoidProtectedHack() && metaField->isProtected()) {
+ QTextStream(&cppField) << "static_cast<"
+ << context.wrapperName() << " *>("
+ << CPP_SELF_VAR << ")->" << protectedFieldGetterName(metaField) << "()";
+ } else {
+ cppField = QLatin1String(CPP_SELF_VAR) + QLatin1String("->") + metaField->name();
+ if (newWrapperSameObject) {
+ cppField.prepend(QLatin1String("&("));
+ cppField.append(QLatin1Char(')'));
+ }
+ }
+ if (isCppIntegralPrimitive(fieldType) || fieldType.isEnum()) {
+ s << INDENT << getFullTypeNameWithoutModifiers(fieldType) << " cppOut_local = " << cppField << ";\n";
+ cppField = QLatin1String("cppOut_local");
+ } else if (avoidProtectedHack() && metaField->isProtected()) {
+ s << INDENT << getFullTypeNameWithoutModifiers(fieldType);
+ if (fieldType.isContainer() || fieldType.isFlags() || fieldType.isSmartPointer()) {
+ s << " &";
+ cppField.prepend(QLatin1Char('*'));
+ } else if ((!fieldType.isConstant() && !fieldType.isEnum() && !fieldType.isPrimitive()) || fieldType.indirections() == 1) {
+ s << " *";
+ }
+ s << " fieldValue = " << cppField << ";\n";
+ cppField = QLatin1String("fieldValue");
+ }
+
+ s << INDENT << "PyObject *pyOut = {};\n";
+ if (newWrapperSameObject) {
+ // Special case colocated field with same address (first field in a struct)
+ s << INDENT << "if (reinterpret_cast<void *>("
+ << cppField
+ << ") == reinterpret_cast<void *>("
+ << CPP_SELF_VAR << ")) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "pyOut = reinterpret_cast<PyObject *>(Shiboken::Object::findColocatedChild("
+ << "reinterpret_cast<SbkObject *>(self), reinterpret_cast<SbkObjectType *>("
+ << cpythonTypeNameExt(fieldType)
+ << ")));\n";
+ s << INDENT << "if (pyOut) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "Py_IncRef(pyOut);\n"
+ << INDENT << "return pyOut;\n";
+ }
+ s << INDENT << "}\n";
+ }
+ // Check if field wrapper has already been created.
+ s << INDENT << "} else if (Shiboken::BindingManager::instance().hasWrapper(" << cppField << ")) {" << "\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper("
+ << cppField << "));" << "\n";
+ s << INDENT << "Py_IncRef(pyOut);" << "\n";
+ s << INDENT << "return pyOut;" << "\n";
+ }
+ s << INDENT << "}\n";
+ // Create and register new wrapper
+ s << INDENT << "pyOut = ";
+ s << "Shiboken::Object::newObject(reinterpret_cast<SbkObjectType *>(" << cpythonTypeNameExt(fieldType)
+ << "), " << cppField << ", false, true);\n";
+ s << INDENT << "Shiboken::Object::setParent(self, pyOut)";
+ } else {
+ s << INDENT << "pyOut = ";
+ writeToPythonConversion(s, fieldType, metaField->enclosingClass(), cppField);
+ }
+ s << ";\n";
+
+ s << INDENT << "return pyOut;\n";
+ s << "}\n";
+}
+
+// Write a getter for QPropertySpec
+void CppGenerator::writeGetterFunction(QTextStream &s, const QPropertySpec *property,
+ const GeneratorContext &context)
+{
+ ErrorCode errorCode(0);
+ writeGetterFunctionStart(s, cpythonGetterFunctionName(property, context.metaClass()));
+ writeCppSelfDefinition(s, context);
+ const QString value = QStringLiteral("value");
+ s << INDENT << "auto " << value << " = " << CPP_SELF_VAR << "->" << property->read() << "();\n"
+ << INDENT << "auto pyResult = ";
+ writeToPythonConversion(s, property->type(), context.metaClass(), value);
+ s << ";\n"
+ << INDENT << "if (PyErr_Occurred() || !pyResult) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "Py_XDECREF(pyResult);\n"
+ << INDENT << " return {};\n";
+ }
+ s << INDENT << "}\n"
+ << INDENT << "return pyResult;\n}\n\n";
+}
+
+// Write setter function preamble (type checks on "pyIn")
+void CppGenerator::writeSetterFunctionPreamble(QTextStream &s, const QString &name,
+ const QString &funcName,
+ const AbstractMetaType &type,
+ const GeneratorContext &context)
+{
+ s << "static int " << funcName << "(PyObject *self, PyObject *pyIn, void *)\n";
+ s << "{\n";
+
+ writeCppSelfDefinition(s, context);
+
+ s << INDENT << "if (pyIn == " << NULL_PTR << ") {\n" << indent(INDENT)
+ << INDENT << "PyErr_SetString(PyExc_TypeError, \"'"
+ << name << "' may not be deleted\");\n"
+ << INDENT << "return -1;\n"
+ << outdent(INDENT) << INDENT << "}\n";
+
+ s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << "{nullptr};\n";
+ s << INDENT << "if (!";
+ writeTypeCheck(s, type, QLatin1String("pyIn"), isNumber(type.typeEntry()));
+ s << ") {\n" << indent(INDENT)
+ << INDENT << "PyErr_SetString(PyExc_TypeError, \"wrong type attributed to '"
+ << name << "', '" << type.name() << "' or convertible type expected\");\n"
+ << INDENT << "return -1;\n"
+ << outdent(INDENT) << INDENT<< "}\n\n";
+}
+
+void CppGenerator::writeSetterFunction(QTextStream &s,
+ const AbstractMetaField *metaField,
+ const GeneratorContext &context)
+{
+ ErrorCode errorCode(0);
+
+ const AbstractMetaType &fieldType = metaField->type();
+ writeSetterFunctionPreamble(s, metaField->name(), cpythonSetterFunctionName(metaField),
+ fieldType, context);
+
+ QString cppField = QString::fromLatin1("%1->%2").arg(QLatin1String(CPP_SELF_VAR), metaField->name());
+ s << INDENT;
+ if (avoidProtectedHack() && metaField->isProtected()) {
+ s << getFullTypeNameWithoutModifiers(fieldType);
+ s << (fieldType.indirections() == 1 ? " *" : "") << " cppOut;\n";
+ s << INDENT << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut);\n";
+ s << INDENT << "static_cast<" << context.wrapperName()
+ << " *>(" << CPP_SELF_VAR << ")->" << protectedFieldSetterName(metaField)
+ << "(cppOut)";
+ } else if (isCppIntegralPrimitive(fieldType) || fieldType.typeEntry()->isEnum() || fieldType.typeEntry()->isFlags()) {
+ s << getFullTypeNameWithoutModifiers(fieldType) << " cppOut_local = " << cppField << ";\n";
+ s << INDENT << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut_local);\n";
+ s << INDENT << cppField << " = cppOut_local";
+ } else {
+ if (isPointerToConst(fieldType))
+ s << "const ";
+ s << getFullTypeNameWithoutModifiers(fieldType);
+ s << QString::fromLatin1(" *").repeated(fieldType.indirections()) << "& cppOut_ptr = ";
+ s << cppField << ";\n";
+ s << INDENT << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut_ptr)";
+ }
+ s << ";\n" << Qt::endl;
+
+ if (isPointerToWrapperType(fieldType)) {
+ s << INDENT << "Shiboken::Object::keepReference(reinterpret_cast<SbkObject *>(self), \"";
+ s << metaField->name() << "\", pyIn);\n";
+ }
+
+ s << INDENT << "return 0;\n";
+ s << "}\n";
+}
+
+// Write a setter for QPropertySpec
+void CppGenerator::writeSetterFunction(QTextStream &s, const QPropertySpec *property,
+ const GeneratorContext &context)
+{
+ ErrorCode errorCode(0);
+ writeSetterFunctionPreamble(s, property->name(),
+ cpythonSetterFunctionName(property, context.metaClass()),
+ property->type(), context);
+
+ s << INDENT << "auto cppOut = " << CPP_SELF_VAR << "->" << property->read() << "();\n"
+ << INDENT << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut);\n"
+ << INDENT << "if (PyErr_Occurred())\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "return -1;\n";
+ }
+ s << INDENT << CPP_SELF_VAR << "->" << property->write() << "(cppOut);\n"
+ << INDENT << "return 0;\n}\n\n";
+}
+
+void CppGenerator::writeRichCompareFunction(QTextStream &s, const GeneratorContext &context)
+{
+ const AbstractMetaClass *metaClass = context.metaClass();
+ QString baseName = cpythonBaseName(metaClass);
+ s << "static PyObject * ";
+ s << baseName << "_richcompare(PyObject *self, PyObject *" << PYTHON_ARG
+ << ", int op)\n{\n";
+ writeCppSelfDefinition(s, context, false, true);
+ writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR));
+ s << INDENT << "PyObject *" << PYTHON_RETURN_VAR << "{};\n";
+ s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ";\n";
+ writeUnusedVariableCast(s, QLatin1String(PYTHON_TO_CPP_VAR));
+ s << Qt::endl;
+
+ s << INDENT << "switch (op) {\n";
+ {
+ Indentation indent(INDENT);
+ const QVector<AbstractMetaFunctionList> &groupedFuncs = filterGroupedOperatorFunctions(metaClass, AbstractMetaClass::ComparisonOp);
+ for (const AbstractMetaFunctionList &overloads : groupedFuncs) {
+ const AbstractMetaFunction *rfunc = overloads[0];
+
+ QString operatorId = ShibokenGenerator::pythonRichCompareOperatorId(rfunc);
+ s << INDENT << "case " << operatorId << ':' << Qt::endl;
+
+ Indentation indent(INDENT);
+
+ QString op = rfunc->originalName();
+ op = op.right(op.size() - QLatin1String("operator").size());
+
+ int alternativeNumericTypes = 0;
+ for (const AbstractMetaFunction *func : overloads) {
+ if (!func->isStatic() &&
+ ShibokenGenerator::isNumber(func->arguments().at(0).type().typeEntry()))
+ alternativeNumericTypes++;
+ }
+
+ bool first = true;
+ OverloadData overloadData(overloads, this);
+ const OverloadDataList &nextOverloads = overloadData.nextOverloadData();
+ for (OverloadData *od : nextOverloads) {
+ const AbstractMetaFunction *func = od->referenceFunction();
+ if (func->isStatic())
+ continue;
+ const AbstractMetaType argType = getArgumentType(func, 1);
+ if (!argType)
+ continue;
+ if (!first) {
+ s << " else ";
+ } else {
+ first = false;
+ s << INDENT;
+ }
+ s << "if (";
+ writeTypeCheck(s, argType, QLatin1String(PYTHON_ARG), alternativeNumericTypes == 1 || isPyInt(argType));
+ s << ") {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "// " << func->signature() << Qt::endl;
+ writeArgumentConversion(s, argType, QLatin1String(CPP_ARG0),
+ QLatin1String(PYTHON_ARG), 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,
+ &func->arguments().constLast());
+ generateOperatorCode = false;
+ }
+ }
+ if (generateOperatorCode) {
+ s << INDENT;
+ if (!func->isVoid())
+ s << func->type().cppSignature() << " " << CPP_RETURN_VAR << " = ";
+ // expression
+ if (func->isPointerOperator())
+ s << '&';
+ s << CPP_SELF_VAR << ' ' << op << '(';
+ if (shouldDereferenceAbstractMetaTypePointer(argType))
+ s << '*';
+ s << CPP_ARG0 << ");\n";
+ s << INDENT << PYTHON_RETURN_VAR << " = ";
+ if (!func->isVoid())
+ writeToPythonConversion(s, func->type(), metaClass, QLatin1String(CPP_RETURN_VAR));
+ else
+ s << "Py_None;\n" << INDENT << "Py_INCREF(Py_None)";
+ s << ";\n";
+ }
+ }
+ s << INDENT << '}';
+ }
+
+ s << " else {\n";
+ if (operatorId == QLatin1String("Py_EQ") || operatorId == QLatin1String("Py_NE")) {
+ Indentation indent(INDENT);
+ s << INDENT << PYTHON_RETURN_VAR << " = "
+ << (operatorId == QLatin1String("Py_EQ") ? "Py_False" : "Py_True") << ";\n";
+ s << INDENT << "Py_INCREF(" << PYTHON_RETURN_VAR << ");\n";
+ } else {
+ Indentation indent(INDENT);
+ s << INDENT << "goto " << baseName << "_RichComparison_TypeError;\n";
+ }
+ s << INDENT<< "}\n\n";
+
+ s << INDENT << "break;\n";
+ }
+ s << INDENT << "default:\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "// PYSIDE-74: By default, we redirect to object's tp_richcompare (which is `==`, `!=`).\n";
+ s << INDENT << "return FallbackRichCompare(self, " << PYTHON_ARG << ", op);\n";
+ s << INDENT << "goto " << baseName << "_RichComparison_TypeError;\n";
+ }
+ }
+ s << INDENT<< "}\n\n";
+
+ s << INDENT << "if (" << PYTHON_RETURN_VAR << " && !PyErr_Occurred())\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "return " << PYTHON_RETURN_VAR << ";\n";
+ }
+ s << INDENT << baseName << "_RichComparison_TypeError:\n";
+ s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"operator not implemented.\");\n";
+ s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl << Qt::endl;
+ s<< "}\n\n";
+}
+
+void CppGenerator::writeMethodDefinitionEntry(QTextStream &s, const AbstractMetaFunctionList &overloads)
+{
+ Q_ASSERT(!overloads.isEmpty());
+ OverloadData overloadData(overloads, this);
+ bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(overloadData);
+ const AbstractMetaFunction *func = overloadData.referenceFunction();
+ int min = overloadData.minArgs();
+ int max = overloadData.maxArgs();
+
+ s << '"' << func->name() << "\", reinterpret_cast<PyCFunction>("
+ << cpythonFunctionName(func) << "), ";
+ if ((min == max) && (max < 2) && !usePyArgs) {
+ if (max == 0)
+ s << "METH_NOARGS";
+ else
+ s << "METH_O";
+ } else {
+ s << "METH_VARARGS";
+ if (overloadData.hasArgumentWithDefaultValue())
+ s << "|METH_KEYWORDS";
+ }
+ // METH_STATIC causes a crash when used for global functions (also from
+ // invisible namespaces).
+ auto ownerClass = func->ownerClass();
+ if (ownerClass
+ && !invisibleTopNamespaces().contains(const_cast<AbstractMetaClass *>(ownerClass))
+ && overloadData.hasStaticFunction()) {
+ s << "|METH_STATIC";
+ }
+}
+
+void CppGenerator::writeMethodDefinition(QTextStream &s, const AbstractMetaFunctionList &overloads)
+{
+ Q_ASSERT(!overloads.isEmpty());
+ const AbstractMetaFunction *func = overloads.constFirst();
+ if (m_tpFuncs.contains(func->name()))
+ return;
+
+ s << INDENT;
+ if (OverloadData::hasStaticAndInstanceFunctions(overloads)) {
+ s << cpythonMethodDefinitionName(func);
+ } else {
+ s << '{';
+ writeMethodDefinitionEntry(s, overloads);
+ s << '}';
+ }
+ s << ',' << Qt::endl;
+}
+
+void CppGenerator::writeSignatureInfo(QTextStream &s, const AbstractMetaFunctionList &overloads)
+{
+ OverloadData overloadData(overloads, this);
+ const AbstractMetaFunction *rfunc = overloadData.referenceFunction();
+ QString funcName = fullPythonFunctionName(rfunc);
+
+ int idx = overloads.length() - 1;
+ bool multiple = idx > 0;
+
+ for (const AbstractMetaFunction *f : overloads) {
+ QStringList args;
+ // PYSIDE-1328: `self`-ness cannot be computed in Python because there are mixed cases.
+ // Toplevel functions like `PySide2.QtCore.QEnum` are always self-less.
+ if (!(f->isStatic()) && f->ownerClass())
+ args << QLatin1String("self");
+ const AbstractMetaArgumentList &arguments = f->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+ auto metaType = arg.type();
+ if (auto viewOn = metaType.viewOn())
+ metaType = *viewOn;
+ QString strArg = metaType.pythonSignature();
+ if (!arg.defaultValueExpression().isEmpty()) {
+ strArg += QLatin1Char('=');
+ QString e = arg.defaultValueExpression();
+ e.replace(QLatin1String("::"), QLatin1String("."));
+ strArg += e;
+ }
+ args << arg.name() + QLatin1Char(':') + strArg;
+ }
+ // mark the multiple signatures as such, to make it easier to generate different code
+ if (multiple)
+ s << idx-- << ':';
+ s << funcName << '(' << args.join(QLatin1Char(',')) << ')';
+ if (!f->isVoid())
+ s << "->" << f->type().pythonSignature();
+ s << Qt::endl;
+ }
+}
+
+void CppGenerator::writeEnumsInitialization(QTextStream &s, AbstractMetaEnumList &enums)
+{
+ if (enums.isEmpty())
+ return;
+ s << INDENT << "// Initialization of enums.\n\n";
+ for (const AbstractMetaEnum *cppEnum : qAsConst(enums)) {
+ if (cppEnum->isPrivate())
+ continue;
+ writeEnumInitialization(s, cppEnum);
+ }
+}
+
+static QString mangleName(QString name)
+{
+ if ( name == QLatin1String("None")
+ || name == QLatin1String("False")
+ || name == QLatin1String("True"))
+ name += QLatin1Char('_');
+ return name;
+}
+
+void CppGenerator::writeEnumInitialization(QTextStream &s, const AbstractMetaEnum *cppEnum)
+{
+ const AbstractMetaClass *enclosingClass = cppEnum->targetLangEnclosingClass();
+ bool hasUpperEnclosingClass = enclosingClass && enclosingClass->targetLangEnclosingClass() != nullptr;
+ const EnumTypeEntry *enumTypeEntry = cppEnum->typeEntry();
+ QString enclosingObjectVariable;
+ if (enclosingClass)
+ enclosingObjectVariable = cpythonTypeName(enclosingClass);
+ else if (hasUpperEnclosingClass)
+ enclosingObjectVariable = QLatin1String("enclosingClass");
+ else
+ enclosingObjectVariable = QLatin1String("module");
+
+ s << INDENT << "// Initialization of ";
+ s << (cppEnum->isAnonymous() ? "anonymous enum identified by enum value" : "enum");
+ s << " '" << cppEnum->name() << "'.\n";
+
+ QString enumVarTypeObj;
+ if (!cppEnum->isAnonymous()) {
+ int packageLevel = packageName().count(QLatin1Char('.')) + 1;
+ FlagsTypeEntry *flags = enumTypeEntry->flags();
+ if (flags) {
+ // The following could probably be made nicer:
+ // We need 'flags->flagsName()' with the full module/class path.
+ QString fullPath = getClassTargetFullName(cppEnum);
+ fullPath.truncate(fullPath.lastIndexOf(QLatin1Char('.')) + 1);
+ s << INDENT << cpythonTypeNameExt(flags) << " = PySide::QFlags::create(\""
+ << packageLevel << ':' << fullPath << flags->flagsName() << "\", "
+ << cpythonEnumName(cppEnum) << "_number_slots);\n";
+ }
+
+ enumVarTypeObj = cpythonTypeNameExt(enumTypeEntry);
+
+ s << INDENT << enumVarTypeObj << " = Shiboken::Enum::";
+ s << ((enclosingClass || hasUpperEnclosingClass) ? "createScopedEnum" : "createGlobalEnum");
+ s << '(' << enclosingObjectVariable << ',' << Qt::endl;
+ {
+ Indentation indent(INDENT);
+ s << INDENT << '"' << cppEnum->name() << "\",\n";
+ s << INDENT << '"' << packageLevel << ':' << getClassTargetFullName(cppEnum) << "\",\n";
+ s << INDENT << '"' << (cppEnum->enclosingClass() ? (cppEnum->enclosingClass()->qualifiedCppName() + QLatin1String("::")) : QString());
+ s << cppEnum->name() << '"';
+ if (flags)
+ s << ',' << Qt::endl << INDENT << cpythonTypeNameExt(flags);
+ s << ");\n";
+ }
+ s << INDENT << "if (!" << cpythonTypeNameExt(cppEnum->typeEntry()) << ")\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl << Qt::endl;
+ }
+ }
+
+ const AbstractMetaEnumValueList &enumValues = cppEnum->values();
+ for (const AbstractMetaEnumValue *enumValue : enumValues) {
+ if (enumTypeEntry->isEnumValueRejected(enumValue->name()))
+ continue;
+
+ QString enumValueText;
+ if (!avoidProtectedHack() || !cppEnum->isProtected()) {
+ enumValueText = QLatin1String("(long) ");
+ if (cppEnum->enclosingClass())
+ enumValueText += cppEnum->enclosingClass()->qualifiedCppName() + QLatin1String("::");
+ // Fully qualify the value which is required for C++ 11 enum classes.
+ if (!cppEnum->isAnonymous())
+ enumValueText += cppEnum->name() + QLatin1String("::");
+ enumValueText += enumValue->name();
+ } else {
+ enumValueText += enumValue->value().toString();
+ }
+
+ switch (cppEnum->enumKind()) {
+ case AnonymousEnum:
+ if (enclosingClass || hasUpperEnclosingClass) {
+ s << INDENT << "{\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "PyObject *anonEnumItem = PyInt_FromLong(" << enumValueText << ");\n";
+ s << INDENT << "if (PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(reinterpret_cast<SbkObjectType *>(" << enclosingObjectVariable
+ << "))->tp_dict, \"" << mangleName(enumValue->name()) << "\", anonEnumItem) < 0)\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl;
+ }
+ s << INDENT << "Py_DECREF(anonEnumItem);\n";
+ }
+ s << INDENT << "}\n";
+ } else {
+ s << INDENT << "if (PyModule_AddIntConstant(module, \"" << mangleName(enumValue->name()) << "\", ";
+ s << enumValueText << ") < 0)\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl;
+ }
+ }
+ break;
+ case CEnum: {
+ s << INDENT << "if (!Shiboken::Enum::";
+ s << ((enclosingClass || hasUpperEnclosingClass) ? "createScopedEnumItem" : "createGlobalEnumItem");
+ s << '(' << enumVarTypeObj << ',' << Qt::endl;
+ Indentation indent(INDENT);
+ s << INDENT << enclosingObjectVariable << ", \"" << mangleName(enumValue->name()) << "\", ";
+ s << enumValueText << "))\n";
+ s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl;
+ }
+ break;
+ case EnumClass: {
+ s << INDENT << "if (!Shiboken::Enum::createScopedEnumItem("
+ << enumVarTypeObj << ',' << Qt::endl;
+ Indentation indent(INDENT);
+ s << INDENT << enumVarTypeObj<< ", \"" << mangleName(enumValue->name()) << "\", "
+ << enumValueText << "))\n"
+ << INDENT << returnStatement(m_currentErrorCode) << Qt::endl;
+ }
+ break;
+ }
+ }
+
+ writeEnumConverterInitialization(s, cppEnum);
+
+ s << INDENT << "// End of '" << cppEnum->name() << "' enum";
+ if (cppEnum->typeEntry()->flags())
+ s << "/flags";
+ s << '.' << Qt::endl << Qt::endl;
+}
+
+void CppGenerator::writeSignalInitialization(QTextStream &s, const AbstractMetaClass *metaClass)
+{
+ // Try to check something and print some warnings
+ const AbstractMetaFunctionList &signalFuncs = metaClass->cppSignalFunctions();
+ for (const AbstractMetaFunction *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 << INDENT << "PySide::Signal::registerSignals(" << cpythonTypeName(metaClass) << ", &::"
+ << metaClass->qualifiedCppName() << "::staticMetaObject);\n";
+}
+
+void CppGenerator::writeFlagsToLong(QTextStream &s, const AbstractMetaEnum *cppEnum)
+{
+ FlagsTypeEntry *flagsEntry = cppEnum->typeEntry()->flags();
+ if (!flagsEntry)
+ return;
+ s << "static PyObject *" << cpythonEnumName(cppEnum) << "_long(PyObject *self)\n";
+ s << "{\n";
+ s << INDENT << "int val;\n";
+ AbstractMetaType flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry);
+ s << INDENT << cpythonToCppConversionFunction(flagsType) << "self, &val);\n";
+ s << INDENT << "return Shiboken::Conversions::copyToPython(Shiboken::Conversions::PrimitiveTypeConverter<int>(), &val);\n";
+ s << "}\n";
+}
+
+void CppGenerator::writeFlagsNonZero(QTextStream &s, const AbstractMetaEnum *cppEnum)
+{
+ FlagsTypeEntry *flagsEntry = cppEnum->typeEntry()->flags();
+ if (!flagsEntry)
+ return;
+ s << "static int " << cpythonEnumName(cppEnum) << "__nonzero(PyObject *self)\n";
+ s << "{\n";
+
+ s << INDENT << "int val;\n";
+ AbstractMetaType flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry);
+ s << INDENT << cpythonToCppConversionFunction(flagsType) << "self, &val);\n";
+ s << INDENT << "return val != 0;\n";
+ s << "}\n";
+}
+
+void CppGenerator::writeFlagsMethods(QTextStream &s, const AbstractMetaEnum *cppEnum)
+{
+ writeFlagsBinaryOperator(s, cppEnum, QLatin1String("and"), QLatin1String("&"));
+ writeFlagsBinaryOperator(s, cppEnum, QLatin1String("or"), QLatin1String("|"));
+ writeFlagsBinaryOperator(s, cppEnum, QLatin1String("xor"), QLatin1String("^"));
+
+ writeFlagsUnaryOperator(s, cppEnum, QLatin1String("invert"), QLatin1String("~"));
+ writeFlagsToLong(s, cppEnum);
+ writeFlagsNonZero(s, cppEnum);
+
+ s << Qt::endl;
+}
+
+void CppGenerator::writeFlagsNumberMethodsDefinition(QTextStream &s, const AbstractMetaEnum *cppEnum)
+{
+ QString cpythonName = cpythonEnumName(cppEnum);
+
+ s << "static PyType_Slot " << cpythonName << "_number_slots[] = {\n";
+ s << INDENT << "{Py_nb_bool, reinterpret_cast<void *>(" << cpythonName << "__nonzero)},\n";
+ s << INDENT << "{Py_nb_invert, reinterpret_cast<void *>(" << cpythonName << "___invert__)},\n";
+ s << INDENT << "{Py_nb_and, reinterpret_cast<void *>(" << cpythonName << "___and__)},\n";
+ s << INDENT << "{Py_nb_xor, reinterpret_cast<void *>(" << cpythonName << "___xor__)},\n";
+ s << INDENT << "{Py_nb_or, reinterpret_cast<void *>(" << cpythonName << "___or__)},\n";
+ s << INDENT << "{Py_nb_int, reinterpret_cast<void *>(" << cpythonName << "_long)},\n";
+ s << INDENT << "{Py_nb_index, reinterpret_cast<void *>(" << cpythonName << "_long)},\n";
+ s << INDENT << "{0, " << NULL_PTR << "} // sentinel\n";
+ s << "};\n\n";
+}
+
+void CppGenerator::writeFlagsNumberMethodsDefinitions(QTextStream &s, const AbstractMetaEnumList &enums)
+{
+ for (AbstractMetaEnum *e : enums) {
+ if (!e->isAnonymous() && !e->isPrivate() && e->typeEntry()->flags()) {
+ writeFlagsMethods(s, e);
+ writeFlagsNumberMethodsDefinition(s, e);
+ s << '\n';
+ }
+ }
+}
+
+void CppGenerator::writeFlagsBinaryOperator(QTextStream &s, const AbstractMetaEnum *cppEnum,
+ const QString &pyOpName, const QString &cppOpName)
+{
+ FlagsTypeEntry *flagsEntry = cppEnum->typeEntry()->flags();
+ Q_ASSERT(flagsEntry);
+
+ s << "PyObject *" << cpythonEnumName(cppEnum) << "___" << pyOpName
+ << "__(PyObject *self, PyObject *" << PYTHON_ARG << ")\n{\n";
+
+ AbstractMetaType flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry);
+ s << INDENT << "::" << flagsEntry->originalName() << " cppResult, " << CPP_SELF_VAR << ", cppArg;\n";
+ s << INDENT << CPP_SELF_VAR << " = static_cast<::" << flagsEntry->originalName()
+ << ">(int(PyLong_AsLong(self)));\n";
+ s << INDENT << "cppArg = static_cast<" << flagsEntry->originalName() << ">(int(PyLong_AsLong("
+ << PYTHON_ARG << ")));\n\n";
+ s << INDENT << "cppResult = " << CPP_SELF_VAR << " " << cppOpName << " cppArg;\n";
+ s << INDENT << "return ";
+ writeToPythonConversion(s, flagsType, nullptr, QLatin1String("cppResult"));
+ s << ";\n";
+ s<< "}\n\n";
+}
+
+void CppGenerator::writeFlagsUnaryOperator(QTextStream &s, const AbstractMetaEnum *cppEnum,
+ const QString &pyOpName,
+ const QString &cppOpName, bool boolResult)
+{
+ FlagsTypeEntry *flagsEntry = cppEnum->typeEntry()->flags();
+ Q_ASSERT(flagsEntry);
+
+ s << "PyObject *" << cpythonEnumName(cppEnum) << "___" << pyOpName
+ << "__(PyObject *self, PyObject *" << PYTHON_ARG << ")\n{\n";
+
+ AbstractMetaType flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry);
+ s << INDENT << "::" << flagsEntry->originalName() << " " << CPP_SELF_VAR << ";\n";
+ s << INDENT << cpythonToCppConversionFunction(flagsType) << "self, &" << CPP_SELF_VAR << ");\n";
+ s << INDENT;
+ if (boolResult)
+ s << "bool";
+ else
+ s << "::" << flagsEntry->originalName();
+ s << " cppResult = " << cppOpName << CPP_SELF_VAR << ";\n";
+ s << INDENT << "return ";
+ if (boolResult)
+ s << "PyBool_FromLong(cppResult)";
+ else
+ writeToPythonConversion(s, flagsType, nullptr, QLatin1String("cppResult"));
+ s << ";\n";
+ s<< "}\n\n";
+}
+
+QString CppGenerator::getSimpleClassInitFunctionName(const AbstractMetaClass *metaClass) const
+{
+ QString initFunctionName;
+ // Disambiguate namespaces per module to allow for extending them.
+ if (metaClass->isNamespace())
+ initFunctionName += moduleName();
+ initFunctionName += metaClass->qualifiedCppName();
+ initFunctionName.replace(QLatin1String("::"), QLatin1String("_"));
+ return initFunctionName;
+}
+
+QString CppGenerator::getInitFunctionName(const GeneratorContext &context) const
+{
+ return !context.forSmartPointer()
+ ? getSimpleClassInitFunctionName(context.metaClass())
+ : getFilteredCppSignatureString(context.preciseType().cppSignature());
+}
+
+void CppGenerator::writeSignatureStrings(QTextStream &s,
+ QTextStream &signatureStream,
+ const QString &arrayName,
+ const char *comment) const
+{
+ s << "// The signatures string for the " << comment << ".\n";
+ s << "// Multiple signatures have their index \"n:\" in front.\n";
+ s << "static const char *" << arrayName << "_SignatureStrings[] = {\n";
+ QString line;
+ while (signatureStream.readLineInto(&line)) {
+ // must anything be escaped?
+ if (line.contains(QLatin1Char('"')) || line.contains(QLatin1Char('\\')))
+ s << INDENT << "R\"CPP(" << line << ")CPP\",\n";
+ else
+ s << INDENT << '"' << line << "\",\n";
+ }
+ s << INDENT << NULL_PTR << "}; // Sentinel\n\n";
+}
+
+void CppGenerator::writeClassRegister(QTextStream &s,
+ const AbstractMetaClass *metaClass,
+ const GeneratorContext &classContext,
+ QTextStream &signatureStream)
+{
+ const ComplexTypeEntry *classTypeEntry = metaClass->typeEntry();
+
+ const AbstractMetaClass *enc = metaClass->targetLangEnclosingClass();
+ QString enclosingObjectVariable = enc ? QLatin1String("enclosingClass") : QLatin1String("module");
+
+ QString pyTypeName = cpythonTypeName(metaClass);
+ QString initFunctionName = getInitFunctionName(classContext);
+
+ // PYSIDE-510: Create a signatures string for the introspection feature.
+ writeSignatureStrings(s, signatureStream, initFunctionName, "functions");
+ s << "void init_" << initFunctionName;
+ s << "(PyObject *" << enclosingObjectVariable << ")\n{\n";
+
+ // Multiple inheritance
+ QString pyTypeBasesVariable = chopType(pyTypeName) + QLatin1String("_Type_bases");
+ const AbstractMetaClassList baseClasses = getBaseClasses(metaClass);
+ if (metaClass->baseClassNames().size() > 1) {
+ s << INDENT << "PyObject *" << pyTypeBasesVariable
+ << " = PyTuple_Pack(" << baseClasses.size() << ',' << Qt::endl;
+ Indentation indent(INDENT);
+ for (int i = 0, size = baseClasses.size(); i < size; ++i) {
+ if (i)
+ s << ",\n";
+ s << INDENT << "reinterpret_cast<PyObject *>("
+ << cpythonTypeNameExt(baseClasses.at(i)->typeEntry()) << ')';
+ }
+ s << ");\n\n";
+ }
+
+ // Create type and insert it in the module or enclosing class.
+ const QString typePtr = QLatin1String("_") + chopType(pyTypeName)
+ + QLatin1String("_Type");
+
+ s << INDENT << typePtr << " = Shiboken::ObjectType::introduceWrapperType(\n";
+ {
+ Indentation indent(INDENT);
+ // 1:enclosingObject
+ s << INDENT << enclosingObjectVariable << ",\n";
+ QString typeName;
+ if (!classContext.forSmartPointer())
+ typeName = metaClass->name();
+ else
+ typeName = classContext.preciseType().cppSignature();
+
+ // 2:typeName
+ s << INDENT << "\"" << typeName << "\",\n";
+
+ // 3:originalName
+ s << INDENT << "\"";
+ if (!classContext.forSmartPointer()) {
+ s << metaClass->qualifiedCppName();
+ if (isObjectType(classTypeEntry))
+ s << '*';
+ } else {
+ s << classContext.preciseType().cppSignature();
+ }
+
+ s << "\",\n";
+ // 4:typeSpec
+ s << INDENT << '&' << chopType(pyTypeName) << "_spec,\n";
+
+ // 5:cppObjDtor
+ s << INDENT;
+ if (!metaClass->isNamespace() && !metaClass->hasPrivateDestructor()) {
+ QString dtorClassName = metaClass->qualifiedCppName();
+ if (((avoidProtectedHack() && metaClass->hasProtectedDestructor()) || classTypeEntry->isValue())
+ && classContext.useWrapper()) {
+ dtorClassName = classContext.wrapperName();
+ }
+ if (classContext.forSmartPointer())
+ dtorClassName = classContext.smartPointerWrapperName();
+
+ s << "&Shiboken::callCppDestructor< ::" << dtorClassName << " >,\n";
+ } else {
+ s << "0,\n";
+ }
+
+ // 6:baseType: Find a type that is not disabled.
+ auto base = metaClass->isNamespace()
+ ? metaClass->extendedNamespace() : metaClass->baseClass();
+ if (!metaClass->isNamespace()) {
+ for (; base != nullptr; base = base->baseClass()) {
+ const auto ct = base->typeEntry()->codeGeneration();
+ if (ct == TypeEntry::GenerateCode || ct == TypeEntry::GenerateForSubclass)
+ break;
+ }
+ }
+ if (base) {
+ s << INDENT << "reinterpret_cast<SbkObjectType *>("
+ << cpythonTypeNameExt(base->typeEntry()) << "),\n";
+ } else {
+ s << INDENT << "0,\n";
+ }
+
+ // 7:baseTypes
+ if (metaClass->baseClassNames().size() > 1)
+ s << INDENT << pyTypeBasesVariable << ',' << Qt::endl;
+ else
+ s << INDENT << "0,\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 (wrapperFlags.isEmpty())
+ s << INDENT << '0';
+ else
+ s << INDENT << wrapperFlags.join(" | ");
+ }
+ s << INDENT << ");\n";
+ s << INDENT << Qt::endl;
+
+ s << INDENT << "auto pyType = reinterpret_cast<PyTypeObject *>(" << typePtr << ");\n";
+ s << INDENT << "InitSignatureStrings(pyType, " << initFunctionName << "_SignatureStrings);\n";
+
+ if (usePySideExtensions())
+ s << INDENT << "SbkObjectType_SetPropertyStrings(reinterpret_cast<PyTypeObject *>(" << typePtr << "), "
+ << chopType(pyTypeName) << "_PropertyStrings);\n";
+
+ if (!classContext.forSmartPointer())
+ s << INDENT << cpythonTypeNameExt(classTypeEntry) << Qt::endl;
+ else
+ s << INDENT << cpythonTypeNameExt(classContext.preciseType()) << Qt::endl;
+ s << INDENT << " = reinterpret_cast<PyTypeObject *>(" << pyTypeName << ");\n";
+ s << Qt::endl;
+
+ // Register conversions for the type.
+ writeConverterRegister(s, metaClass, classContext);
+ s << Qt::endl;
+
+ // class inject-code target/beginning
+ if (!classTypeEntry->codeSnips().isEmpty()) {
+ writeClassCodeSnips(s, classTypeEntry->codeSnips(),
+ TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode,
+ classContext);
+ s << Qt::endl;
+ }
+
+ // Fill multiple inheritance data, if needed.
+ const AbstractMetaClass *miClass = getMultipleInheritingClass(metaClass);
+ if (miClass) {
+ s << INDENT << "MultipleInheritanceInitFunction func = ";
+ if (miClass == metaClass) {
+ s << multipleInheritanceInitializerFunctionName(miClass) << ";\n";
+ } else {
+ s << "Shiboken::ObjectType::getMultipleInheritanceFunction(reinterpret_cast<SbkObjectType *>(";
+ s << cpythonTypeNameExt(miClass->typeEntry()) << "));\n";
+ }
+ s << INDENT << "Shiboken::ObjectType::setMultipleInheritanceFunction(";
+ s << cpythonTypeName(metaClass) << ", func);\n";
+ s << INDENT << "Shiboken::ObjectType::setCastFunction(" << cpythonTypeName(metaClass);
+ s << ", &" << cpythonSpecialCastFunctionName(metaClass) << ");\n";
+ }
+
+ // Set typediscovery struct or fill the struct of another one
+ if (metaClass->isPolymorphic() && metaClass->baseClass()) {
+ s << INDENT << "Shiboken::ObjectType::setTypeDiscoveryFunctionV2(" << cpythonTypeName(metaClass);
+ s << ", &" << cpythonBaseName(metaClass) << "_typeDiscovery);\n\n";
+ }
+
+ AbstractMetaEnumList classEnums = metaClass->enums();
+ metaClass->getEnumsFromInvisibleNamespacesToBeGenerated(&classEnums);
+
+ ErrorCode errorCode(QString::fromLatin1(""));
+ writeEnumsInitialization(s, classEnums);
+
+ if (metaClass->hasSignals())
+ writeSignalInitialization(s, metaClass);
+
+ // Write static fields
+ const AbstractMetaFieldList &fields = metaClass->fields();
+ for (const AbstractMetaField *field : fields) {
+ if (!field->isStatic())
+ continue;
+ s << INDENT << QLatin1String("PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(") + cpythonTypeName(metaClass) + QLatin1String(")->tp_dict, \"");
+ s << field->name() << "\", ";
+ writeToPythonConversion(s, field->type(), metaClass, metaClass->qualifiedCppName() + QLatin1String("::") + field->name());
+ s << ");\n";
+ }
+ s << Qt::endl;
+
+ // class inject-code target/end
+ if (!classTypeEntry->codeSnips().isEmpty()) {
+ s << Qt::endl;
+ writeClassCodeSnips(s, classTypeEntry->codeSnips(),
+ TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode,
+ classContext);
+ }
+
+ if (usePySideExtensions()) {
+ if (avoidProtectedHack() && classContext.useWrapper())
+ s << INDENT << classContext.wrapperName() << "::pysideInitQtMetaTypes();\n";
+ else
+ writeInitQtMetaTypeFunctionBody(s, classContext);
+ }
+
+ if (usePySideExtensions() && metaClass->isQObject()) {
+ s << INDENT << "Shiboken::ObjectType::setSubTypeInitHook(" << pyTypeName << ", &PySide::initQObjectSubType);\n";
+ s << INDENT << "PySide::initDynamicMetaObject(" << pyTypeName << ", &::" << metaClass->qualifiedCppName()
+ << "::staticMetaObject, sizeof(";
+ if (shouldGenerateCppWrapper(metaClass))
+ s << wrapperName(metaClass);
+ else
+ s << "::" << metaClass->qualifiedCppName();
+ s << "));\n";
+ }
+
+ s << "}\n";
+}
+
+void CppGenerator::writeInitQtMetaTypeFunctionBody(QTextStream &s, const GeneratorContext &context) const
+{
+ const AbstractMetaClass *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();
+
+ const AbstractMetaClass *enclosingClass = metaClass->enclosingClass();
+ while (enclosingClass) {
+ if (enclosingClass->typeEntry()->generateCode())
+ nameVariants << (enclosingClass->name() + QLatin1String("::") + nameVariants.constLast());
+ enclosingClass = enclosingClass->enclosingClass();
+ }
+
+ QString className;
+ if (!context.forSmartPointer())
+ className = metaClass->qualifiedCppName();
+ else
+ className = context.preciseType().cppSignature();
+
+ if (!metaClass->isNamespace() && !metaClass->isAbstract()) {
+ // Qt metatypes are registered only on their first use, so we do this now.
+ bool canBeValue = false;
+ if (!isObjectType(metaClass)) {
+ // check if there's a empty ctor
+ const AbstractMetaFunctionList &funcs = metaClass->functions();
+ for (AbstractMetaFunction *func : funcs) {
+ if (func->isConstructor() && !func->arguments().count()) {
+ canBeValue = true;
+ break;
+ }
+ }
+ }
+
+ if (canBeValue) {
+ for (const QString &name : qAsConst(nameVariants)) {
+ if (name == QLatin1String("iterator")) {
+ qCWarning(lcShiboken).noquote().nospace()
+ << QString::fromLatin1("%1:%2 FIXME:\n"
+ " The code tried to qRegisterMetaType the unqualified name "
+ "'iterator'. This is currently fixed by a hack(ct) and needs improvement!")
+ .arg(QFile::decodeName(__FILE__)).arg(__LINE__);
+ continue;
+ }
+ s << INDENT << "qRegisterMetaType< ::" << className << " >(\"" << name << "\");\n";
+ }
+ }
+ }
+
+ for (AbstractMetaEnum *metaEnum : metaClass->enums()) {
+ if (!metaEnum->isPrivate() && !metaEnum->isAnonymous()) {
+ for (const QString &name : qAsConst(nameVariants))
+ s << INDENT << "qRegisterMetaType< ::" << metaEnum->typeEntry()->qualifiedCppName() << " >(\"" << name << "::" << metaEnum->name() << "\");\n";
+
+ if (metaEnum->typeEntry()->flags()) {
+ QString n = metaEnum->typeEntry()->flags()->originalName();
+ s << INDENT << "qRegisterMetaType< ::" << n << " >(\"" << n << "\");\n";
+ }
+ }
+ }
+}
+
+void CppGenerator::writeTypeDiscoveryFunction(QTextStream &s, const AbstractMetaClass *metaClass)
+{
+ QString polymorphicExpr = metaClass->typeEntry()->polymorphicIdValue();
+
+ s << "static void *" << cpythonBaseName(metaClass) << "_typeDiscovery(void *cptr, SbkObjectType *instanceType)\n{\n";
+
+ if (!polymorphicExpr.isEmpty()) {
+ polymorphicExpr = polymorphicExpr.replace(QLatin1String("%1"),
+ QLatin1String(" reinterpret_cast< ::")
+ + metaClass->qualifiedCppName()
+ + QLatin1String(" *>(cptr)"));
+ s << INDENT << " if (" << polymorphicExpr << ")\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "return cptr;\n";
+ }
+ } else if (metaClass->isPolymorphic()) {
+ const AbstractMetaClassList &ancestors = getAllAncestors(metaClass);
+ for (AbstractMetaClass *ancestor : ancestors) {
+ if (ancestor->baseClass())
+ continue;
+ if (ancestor->isPolymorphic()) {
+ s << INDENT << "if (instanceType == reinterpret_cast<SbkObjectType *>(Shiboken::SbkType< ::"
+ << ancestor->qualifiedCppName() << " >()))\n";
+ Indentation indent(INDENT);
+ s << INDENT << "return dynamic_cast< ::" << metaClass->qualifiedCppName()
+ << " *>(reinterpret_cast< ::"<< ancestor->qualifiedCppName() << " *>(cptr));\n";
+ } 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 << INDENT << "return {};\n";
+ s << "}\n\n";
+}
+
+QString CppGenerator::writeSmartPointerGetterCast()
+{
+ return QLatin1String("const_cast<char *>(")
+ + QLatin1String(SMART_POINTER_GETTER) + QLatin1Char(')');
+}
+
+void CppGenerator::writeSetattroDefinition(QTextStream &s, const AbstractMetaClass *metaClass) const
+{
+ s << "static int " << ShibokenGenerator::cpythonSetattroFunctionName(metaClass)
+ << "(PyObject *self, PyObject *name, PyObject *value)\n{\n";
+ if (wrapperDiagnostics()) {
+ s << INDENT << R"(std::cerr << __FUNCTION__ << ' ' << Shiboken::debugPyObject(name)
+ << ' ' << Shiboken::debugPyObject(value) << '\n';)" << '\n';
+ }
+}
+
+inline void CppGenerator::writeSetattroDefaultReturn(QTextStream &s) const
+{
+ s << INDENT << "return PyObject_GenericSetAttr(self, name, value);\n}\n\n";
+}
+
+void CppGenerator::writeSetattroFunction(QTextStream &s, AttroCheck attroCheck,
+ const GeneratorContext &context)
+{
+ Q_ASSERT(!context.forSmartPointer());
+ const AbstractMetaClass *metaClass = context.metaClass();
+ writeSetattroDefinition(s, metaClass);
+
+ // PYSIDE-1019: Switch tp_dict before doing tp_setattro.
+ if (usePySideExtensions())
+ s << INDENT << "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 << INDENT << "if (value && PyCallable_Check(value)) {\n";
+ s << INDENT << " auto plain_inst = " << cpythonWrapperCPtr(metaClass, QLatin1String("self")) << ";\n";
+ s << INDENT << " auto inst = dynamic_cast<" << context.wrapperName() << " *>(plain_inst);\n";
+ s << INDENT << " if (inst)\n";
+ s << INDENT << " inst->resetPyMethodCache();\n";
+ s << INDENT << "}\n";
+ }
+ if (attroCheck.testFlag(AttroCheckFlag::SetattroQObject)) {
+ s << INDENT << "Shiboken::AutoDecRef pp(reinterpret_cast<PyObject *>(PySide::Property::getObject(self, name)));\n";
+ s << INDENT << "if (!pp.isNull())\n";
+ Indentation indent(INDENT);
+ s << INDENT << "return PySide::Property::setValue(reinterpret_cast<PySideProperty *>(pp.object()), self, value);\n";
+ }
+
+ if (attroCheck.testFlag(AttroCheckFlag::SetattroUser)) {
+ auto func = AbstractMetaClass::queryFirstFunction(metaClass->functions(),
+ AbstractMetaClass::SetAttroFunction);
+ Q_ASSERT(func);
+ s << INDENT << "{\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "auto " << CPP_SELF_VAR << " = "
+ << cpythonWrapperCPtr(metaClass, QLatin1String("self")) << ";\n";
+ writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny,
+ TypeSystem::TargetLangCode, context);
+ }
+ s << INDENT << "}\n";
+ }
+
+ writeSetattroDefaultReturn(s);
+}
+
+void CppGenerator::writeSmartPointerSetattroFunction(QTextStream &s, const GeneratorContext &context)
+{
+ Q_ASSERT(context.forSmartPointer());
+ writeSetattroDefinition(s, context.metaClass());
+ s << INDENT << "// Try to find the 'name' attribute, by retrieving the PyObject for the corresponding C++ object held by the smart pointer.\n";
+ s << INDENT << "PyObject *rawObj = PyObject_CallMethod(self, "
+ << writeSmartPointerGetterCast() << ", 0);\n";
+ s << INDENT << "if (rawObj) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "int hasAttribute = PyObject_HasAttr(rawObj, name);\n";
+ s << INDENT << "if (hasAttribute) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "return PyObject_GenericSetAttr(rawObj, name, value);\n";
+ }
+ s << INDENT << "}\n";
+ s << INDENT << "Py_DECREF(rawObj);\n";
+ }
+ s << INDENT << "}\n";
+ writeSetattroDefaultReturn(s);
+}
+
+void CppGenerator::writeGetattroDefinition(QTextStream &s, const AbstractMetaClass *metaClass)
+{
+ s << "static PyObject *" << cpythonGetattroFunctionName(metaClass)
+ << "(PyObject *self, PyObject *name)\n{\n";
+}
+
+QString CppGenerator::qObjectGetAttroFunction() const
+{
+ static QString result;
+ if (result.isEmpty()) {
+ AbstractMetaClass *qobjectClass = AbstractMetaClass::findClass(classes(), qObjectT());
+ Q_ASSERT(qobjectClass);
+ result = QLatin1String("PySide::getMetaDataFromQObject(")
+ + cpythonWrapperCPtr(qobjectClass, QLatin1String("self"))
+ + QLatin1String(", self, name)");
+ }
+ return result;
+}
+
+void CppGenerator::writeGetattroFunction(QTextStream &s, AttroCheck attroCheck,
+ const GeneratorContext &context)
+{
+ Q_ASSERT(!context.forSmartPointer());
+ const AbstractMetaClass *metaClass = context.metaClass();
+ writeGetattroDefinition(s, metaClass);
+
+ // PYSIDE-1019: Switch tp_dict before doing tp_getattro.
+ if (usePySideExtensions())
+ s << INDENT << "PySide::Feature::Select(self);\n";
+
+ const QString getattrFunc = usePySideExtensions() && metaClass->isQObject()
+ ? qObjectGetAttroFunction() : QLatin1String("PyObject_GenericGetAttr(self, name)");
+
+ if (attroCheck.testFlag(AttroCheckFlag::GetattroOverloads)) {
+ s << INDENT << "// Search the method in the instance dict\n";
+ s << INDENT << "if (auto ob_dict = reinterpret_cast<SbkObject *>(self)->ob_dict) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "if (auto meth = PyDict_GetItem(ob_dict, name)) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "Py_INCREF(meth);\n";
+ s << INDENT << "return meth;\n";
+ }
+ s << INDENT << "}\n";
+ }
+ s << INDENT << "}\n";
+ s << INDENT << "// Search the method in the type dict\n";
+ s << INDENT << "if (Shiboken::Object::isUserType(self)) {\n";
+ {
+ Indentation indent(INDENT);
+ // PYSIDE-772: Perform optimized name mangling.
+ s << INDENT << "Shiboken::AutoDecRef tmp(_Pep_PrivateMangle(self, name));\n";
+ s << INDENT << "if (auto meth = PyDict_GetItem(Py_TYPE(self)->tp_dict, tmp))\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "return PyFunction_Check(meth) ? SBK_PyMethod_New(meth, self) : " << getattrFunc << ";\n";
+ }
+ }
+ s << INDENT << "}\n";
+
+ const AbstractMetaFunctionList &funcs = getMethodsWithBothStaticAndNonStaticMethods(metaClass);
+ for (const AbstractMetaFunction *func : funcs) {
+ QString defName = cpythonMethodDefinitionName(func);
+ s << INDENT << "static PyMethodDef non_static_" << defName << " = {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << defName << ".ml_name,\n";
+ s << INDENT << defName << ".ml_meth,\n";
+ s << INDENT << defName << ".ml_flags & (~METH_STATIC),\n";
+ s << INDENT << defName << ".ml_doc,\n";
+ }
+ s << INDENT << "};\n";
+ s << INDENT << "if (Shiboken::String::compare(name, \"" << func->name() << "\") == 0)\n";
+ Indentation indent(INDENT);
+ s << INDENT << "return PyCFunction_NewEx(&non_static_" << defName << ", self, 0);\n";
+ }
+ }
+
+ if (attroCheck.testFlag(AttroCheckFlag::GetattroUser)) {
+ auto func = AbstractMetaClass::queryFirstFunction(metaClass->functions(),
+ AbstractMetaClass::GetAttroFunction);
+ Q_ASSERT(func);
+ s << INDENT << "{\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "auto " << CPP_SELF_VAR << " = "
+ << cpythonWrapperCPtr(metaClass, QLatin1String("self")) << ";\n";
+ writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny,
+ TypeSystem::TargetLangCode, context);
+ }
+ s << INDENT << "}\n";
+ }
+
+ s << INDENT << "return " << getattrFunc << ";\n}\n\n";
+}
+
+void CppGenerator::writeSmartPointerGetattroFunction(QTextStream &s, const GeneratorContext &context)
+{
+ Q_ASSERT(context.forSmartPointer());
+ const AbstractMetaClass *metaClass = context.metaClass();
+ writeGetattroDefinition(s, metaClass);
+ s << INDENT << "PyObject *tmp = PyObject_GenericGetAttr(self, name);\n";
+ s << INDENT << "if (tmp)\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "return tmp;\n";
+ }
+ s << INDENT << "if (!PyErr_ExceptionMatches(PyExc_AttributeError))\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "return nullptr;\n";
+ }
+ s << INDENT << "PyErr_Clear();\n";
+
+ // This generates the code which dispatches access to member functions
+ // and fields from the smart pointer to its pointee.
+ s << INDENT << "// Try to find the 'name' attribute, by retrieving the PyObject for "
+ "the corresponding C++ object held by the smart pointer.\n";
+ s << INDENT << "if (auto rawObj = PyObject_CallMethod(self, "
+ << writeSmartPointerGetterCast() << ", 0)) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "if (auto attribute = PyObject_GetAttr(rawObj, name))\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "tmp = attribute;\n";
+ }
+ s << INDENT << "Py_DECREF(rawObj);\n";
+ }
+ s << INDENT << "}\n";
+ s << INDENT << "if (!tmp) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "PyTypeObject *tp = Py_TYPE(self);\n";
+ s << INDENT << "PyErr_Format(PyExc_AttributeError,\n";
+ s << INDENT << " \"'%.50s' object has no attribute '%.400s'\",\n";
+ s << INDENT << " tp->tp_name, Shiboken::String::toCString(name));\n";
+ }
+ s << INDENT << "}\n";
+ s << INDENT << "return tmp;\n}\n\n";
+}
+
+// Write declaration and invocation of the init function for the module init
+// function.
+void CppGenerator::writeInitFunc(QTextStream &declStr, QTextStream &callStr,
+ const Indentor &indent, const QString &initFunctionName,
+ const TypeEntry *enclosingEntry)
+{
+ const bool hasParent =
+ enclosingEntry && enclosingEntry->type() != TypeEntry::TypeSystemType;
+ declStr << "void init_" << initFunctionName << "(PyObject *"
+ << (hasParent ? "enclosingClass" : "module") << ");\n";
+ callStr << indent << "init_" << initFunctionName;
+ if (hasParent) {
+ callStr << "(reinterpret_cast<PyTypeObject *>("
+ << cpythonTypeNameExt(enclosingEntry) << ")->tp_dict);\n";
+ } else {
+ callStr << "(module);\n";
+ }
+}
+
+bool CppGenerator::finishGeneration()
+{
+ //Generate CPython wrapper file
+ QString classInitDecl;
+ QTextStream s_classInitDecl(&classInitDecl);
+ QString classPythonDefines;
+ QTextStream s_classPythonDefines(&classPythonDefines);
+
+ QSet<Include> includes;
+ QString globalFunctionImpl;
+ QTextStream s_globalFunctionImpl(&globalFunctionImpl);
+ QString globalFunctionDecl;
+ QTextStream s_globalFunctionDef(&globalFunctionDecl);
+ QString signaturesString;
+ QTextStream signatureStream(&signaturesString);
+
+ Indentation indentation(INDENT);
+
+ const auto functionGroups = getGlobalFunctionGroups();
+ for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) {
+ AbstractMetaFunctionList overloads;
+ for (AbstractMetaFunction *func : it.value()) {
+ if (!func->isModifiedRemoved()) {
+ overloads.append(func);
+ if (func->typeEntry())
+ includes << func->typeEntry()->include();
+ }
+ }
+
+ if (overloads.isEmpty())
+ continue;
+
+ // Dummy context to satisfy the API.
+ GeneratorContext classContext;
+ writeMethodWrapper(s_globalFunctionImpl, overloads, classContext);
+ writeSignatureInfo(signatureStream, overloads);
+ writeMethodDefinition(s_globalFunctionDef, overloads);
+ }
+
+ //this is a temporary solution before new type revison implementation
+ //We need move QMetaObject register before QObject
+ Dependencies additionalDependencies;
+ const AbstractMetaClassList &allClasses = classes();
+ if (auto qObjectClass = AbstractMetaClass::findClass(allClasses, qObjectT())) {
+ if (auto qMetaObjectClass = AbstractMetaClass::findClass(allClasses, qMetaObjectT())) {
+ Dependency dependency;
+ dependency.parent = qMetaObjectClass;
+ dependency.child = qObjectClass;
+ additionalDependencies.append(dependency);
+ }
+ }
+ const AbstractMetaClassList lst = classesTopologicalSorted(additionalDependencies);
+
+ for (const AbstractMetaClass *cls : lst){
+ if (shouldGenerate(cls)) {
+ writeInitFunc(s_classInitDecl, s_classPythonDefines, INDENT,
+ getSimpleClassInitFunctionName(cls),
+ cls->typeEntry()->targetLangEnclosingEntry());
+ }
+ }
+
+ // Initialize smart pointer types.
+ const auto &smartPtrs = instantiatedSmartPointers();
+ for (const AbstractMetaType &metaType : smartPtrs) {
+ GeneratorContext context = contextForSmartPointer(nullptr, metaType);
+ writeInitFunc(s_classInitDecl, s_classPythonDefines, INDENT,
+ getInitFunctionName(context),
+ metaType.typeEntry()->targetLangEnclosingEntry());
+ }
+
+ QString moduleFileName(outputDirectory() + QLatin1Char('/') + subDirectoryForPackage(packageName()));
+ moduleFileName += QLatin1Char('/') + moduleName().toLower() + QLatin1String("_module_wrapper.cpp");
+
+
+ verifyDirectoryFor(moduleFileName);
+ FileOut file(moduleFileName);
+
+ QTextStream &s = file.stream;
+
+ // write license comment
+ s << licenseComment() << Qt::endl;
+
+ s << "#include <sbkpython.h>\n";
+ s << "#include <shiboken.h>\n";
+ s << "#include <algorithm>\n";
+ s << "#include <signature.h>\n";
+ if (usePySideExtensions()) {
+ s << includeQDebug;
+ s << "#include <pyside.h>\n";
+ s << "#include <pysideqenum.h>\n";
+ s << "#include <feature_select.h>\n";
+ s << "#include <qapp_macro.h>\n";
+ }
+
+ s << "#include \"" << getModuleHeaderFileName() << '"' << Qt::endl << Qt::endl;
+ for (const Include &include : qAsConst(includes))
+ s << include;
+ s << Qt::endl;
+
+ // Global enums
+ AbstractMetaEnumList globalEnums = this->globalEnums();
+ for (const AbstractMetaClass *nsp : invisibleTopNamespaces())
+ nsp->getEnumsToBeGenerated(&globalEnums);
+
+ TypeDatabase *typeDb = TypeDatabase::instance();
+ const TypeSystemTypeEntry *moduleEntry = typeDb->defaultTypeSystemType();
+ Q_ASSERT(moduleEntry);
+
+ //Extra includes
+ s << Qt::endl << "// Extra includes\n";
+ QVector<Include> extraIncludes = moduleEntry->extraIncludes();
+ for (AbstractMetaEnum *cppEnum : qAsConst(globalEnums))
+ extraIncludes.append(cppEnum->typeEntry()->extraIncludes());
+ std::sort(extraIncludes.begin(), extraIncludes.end());
+ for (const Include &inc : qAsConst(extraIncludes))
+ s << inc;
+ s << Qt::endl;
+
+ s << "// Current module's type array.\n";
+ s << "PyTypeObject **" << cppApiVariableName() << " = nullptr;\n";
+
+ s << "// Current module's PyObject pointer.\n";
+ s << "PyObject *" << pythonModuleObjectName() << " = nullptr;\n";
+
+ s << "// Current module's converter array.\n";
+ s << "SbkConverter **" << convertersVariableName() << " = nullptr;\n";
+
+ const CodeSnipList snips = moduleEntry->codeSnips();
+
+ // module inject-code native/beginning
+ if (!snips.isEmpty())
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode);
+
+ // cleanup staticMetaObject attribute
+ if (usePySideExtensions()) {
+ s << "void cleanTypesAttributes(void) {\n";
+ s << INDENT << "for (int i = 0, imax = SBK_" << moduleName()
+ << "_IDX_COUNT; i < imax; i++) {\n" << indent(INDENT)
+ << INDENT << "PyObject *pyType = reinterpret_cast<PyObject *>(" << cppApiVariableName() << "[i]);\n"
+ << INDENT << "Shiboken::AutoDecRef attrName(Py_BuildValue(\"s\", \"staticMetaObject\"));\n"
+ << INDENT << "if (pyType && PyObject_HasAttr(pyType, attrName))\n" << indent(INDENT)
+ << INDENT << "PyObject_SetAttr(pyType, attrName, Py_None);\n" << outdent(INDENT)
+ << outdent(INDENT) << INDENT << "}\n" << "}\n";
+ }
+
+ s << "// Global functions ";
+ s << "------------------------------------------------------------\n";
+ s << globalFunctionImpl << Qt::endl;
+
+ s << "static PyMethodDef " << moduleName() << "_methods[] = {\n";
+ s << globalFunctionDecl;
+ s << INDENT << "{0} // Sentinel\n" << "};\n\n";
+
+ s << "// Classes initialization functions ";
+ s << "------------------------------------------------------------\n";
+ s << classInitDecl << Qt::endl;
+
+ if (!globalEnums.isEmpty()) {
+ QString converterImpl;
+ QTextStream convImpl(&converterImpl);
+
+ s << "// Enum definitions ";
+ s << "------------------------------------------------------------\n";
+ for (const AbstractMetaEnum *cppEnum : qAsConst(globalEnums)) {
+ if (cppEnum->isAnonymous() || cppEnum->isPrivate())
+ continue;
+ writeEnumConverterFunctions(s, cppEnum);
+ s << Qt::endl;
+ }
+
+ if (!converterImpl.isEmpty()) {
+ s << "// Enum converters ";
+ s << "------------------------------------------------------------\n";
+ s << "namespace Shiboken\n{\n";
+ s << converterImpl << Qt::endl;
+ s << "} // namespace Shiboken\n\n";
+ }
+
+ writeFlagsNumberMethodsDefinitions(s, globalEnums);
+ s << '\n';
+ }
+
+ const QStringList &requiredModules = typeDb->requiredTargetImports();
+ if (!requiredModules.isEmpty())
+ s << "// Required modules' type and converter arrays.\n";
+ for (const QString &requiredModule : requiredModules) {
+ s << "PyTypeObject **" << cppApiVariableName(requiredModule) << ";\n";
+ s << "SbkConverter **" << convertersVariableName(requiredModule) << ";\n";
+ }
+ s << Qt::endl;
+
+ s << "// Module initialization ";
+ s << "------------------------------------------------------------\n";
+ ExtendedConverterData extendedConverters = getExtendedConverters();
+ if (!extendedConverters.isEmpty()) {
+ s << Qt::endl << "// Extended Converters.\n\n";
+ for (ExtendedConverterData::const_iterator it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) {
+ const TypeEntry *externalType = it.key();
+ s << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName() << '.' << Qt::endl;
+ for (const AbstractMetaClass *sourceClass : it.value()) {
+ AbstractMetaType sourceType = buildAbstractMetaTypeFromAbstractMetaClass(sourceClass);
+ AbstractMetaType targetType = buildAbstractMetaTypeFromTypeEntry(externalType);
+ writePythonToCppConversionFunctions(s, sourceType, targetType);
+ }
+ }
+ }
+
+ const QVector<const CustomConversion *> &typeConversions = getPrimitiveCustomConversions();
+ if (!typeConversions.isEmpty()) {
+ s << Qt::endl << "// Primitive Type converters.\n\n";
+ for (const CustomConversion *conversion : typeConversions) {
+ s << "// C++ to Python conversion for type '" << conversion->ownerType()->qualifiedCppName() << "'.\n";
+ writeCppToPythonFunction(s, conversion);
+ writeCustomConverterFunctions(s, conversion);
+ }
+ s << Qt::endl;
+ }
+
+ const auto &containers = instantiatedContainers();
+ if (!containers.isEmpty()) {
+ s << "// Container Type converters.\n\n";
+ for (const AbstractMetaType &container : containers) {
+ s << "// C++ to Python conversion for type '" << container.cppSignature() << "'.\n";
+ writeContainerConverterFunctions(s, container);
+ }
+ s << Qt::endl;
+ }
+
+ // Implicit smart pointers conversions
+ const auto smartPointersList = instantiatedSmartPointers();
+ if (!smartPointersList.isEmpty()) {
+ s << "// SmartPointers converters.\n\n";
+ for (const AbstractMetaType &smartPointer : smartPointersList) {
+ s << "// C++ to Python conversion for type '" << smartPointer.cppSignature() << "'.\n";
+ writeSmartPointerConverterFunctions(s, smartPointer);
+ }
+ s << Qt::endl;
+ }
+
+ s << "static struct PyModuleDef moduledef = {\n";
+ s << " /* m_base */ PyModuleDef_HEAD_INIT,\n";
+ s << " /* m_name */ \"" << moduleName() << "\",\n";
+ s << " /* m_doc */ nullptr,\n";
+ s << " /* m_size */ -1,\n";
+ s << " /* m_methods */ " << moduleName() << "_methods,\n";
+ s << " /* m_reload */ nullptr,\n";
+ s << " /* m_traverse */ nullptr,\n";
+ s << " /* m_clear */ nullptr,\n";
+ s << " /* m_free */ nullptr\n";
+ s << "};\n\n";
+
+ // PYSIDE-510: Create a signatures string for the introspection feature.
+ writeSignatureStrings(s, signatureStream, moduleName(), "global functions");
+
+ s << "extern \"C\" LIBSHIBOKEN_EXPORT PyObject *PyInit_"
+ << moduleName() << "()\n{\n";
+
+ ErrorCode errorCode(QLatin1String("nullptr"));
+ // module inject-code target/beginning
+ if (!snips.isEmpty())
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode);
+
+ for (const QString &requiredModule : requiredModules) {
+ s << INDENT << "{\n" << indent(INDENT)
+ << INDENT << "Shiboken::AutoDecRef requiredModule(Shiboken::Module::import(\"" << requiredModule << "\"));\n"
+ << INDENT << "if (requiredModule.isNull())\n" << indent(INDENT)
+ << INDENT << "return nullptr;\n" << outdent(INDENT)
+ << INDENT << cppApiVariableName(requiredModule)
+ << " = Shiboken::Module::getTypes(requiredModule);\n"
+ << INDENT << convertersVariableName(requiredModule)
+ << " = Shiboken::Module::getTypeConverters(requiredModule);\n" << outdent(INDENT)
+ << INDENT << "}\n\n";
+ }
+
+ int maxTypeIndex = getMaxTypeIndex() + instantiatedSmartPointers().size();
+ if (maxTypeIndex) {
+ s << INDENT << "// Create an array of wrapper types for the current module.\n";
+ s << INDENT << "static PyTypeObject *cppApi[SBK_" << moduleName() << "_IDX_COUNT];\n";
+ s << INDENT << cppApiVariableName() << " = cppApi;\n\n";
+ }
+
+ s << INDENT << "// Create an array of primitive type converters for the current module.\n";
+ s << INDENT << "static SbkConverter *sbkConverters[SBK_" << moduleName() << "_CONVERTERS_IDX_COUNT" << "];\n";
+ s << INDENT << convertersVariableName() << " = sbkConverters;\n\n";
+
+ s << INDENT << "PyObject *module = Shiboken::Module::create(\"" << moduleName() << "\", &moduledef);\n\n";
+
+ s << INDENT << "// Make module available from global scope\n";
+ s << INDENT << pythonModuleObjectName() << " = module;\n\n";
+
+ //s << INDENT << "// Initialize converters for primitive types.\n";
+ //s << INDENT << "initConverters();\n\n";
+
+ s << INDENT << "// Initialize classes in the type system\n";
+ s << classPythonDefines;
+
+ if (!typeConversions.isEmpty()) {
+ s << Qt::endl;
+ for (const CustomConversion *conversion : typeConversions) {
+ writePrimitiveConverterInitialization(s, conversion);
+ s << Qt::endl;
+ }
+ }
+
+ if (!containers.isEmpty()) {
+ s << Qt::endl;
+ for (const AbstractMetaType &container : containers) {
+ writeContainerConverterInitialization(s, container);
+ s << Qt::endl;
+ }
+ }
+
+ if (!smartPointersList.isEmpty()) {
+ s << Qt::endl;
+ for (const AbstractMetaType &smartPointer : smartPointersList) {
+ writeSmartPointerConverterInitialization(s, smartPointer);
+ s << Qt::endl;
+ }
+ }
+
+ if (!extendedConverters.isEmpty()) {
+ s << Qt::endl;
+ for (ExtendedConverterData::const_iterator it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) {
+ writeExtendedConverterInitialization(s, it.key(), it.value());
+ s << Qt::endl;
+ }
+ }
+
+ writeEnumsInitialization(s, globalEnums);
+
+ s << INDENT << "// Register primitive types converters.\n";
+ const PrimitiveTypeEntryList &primitiveTypeList = primitiveTypes();
+ for (const PrimitiveTypeEntry *pte : primitiveTypeList) {
+ if (!pte->generateCode() || !pte->isCppPrimitive())
+ continue;
+ const TypeEntry *referencedType = pte->basicReferencedTypeEntry();
+ if (!referencedType)
+ continue;
+ QString converter = converterObject(referencedType);
+ QStringList cppSignature = pte->qualifiedCppName().split(QLatin1String("::"), Qt::SkipEmptyParts);
+ while (!cppSignature.isEmpty()) {
+ QString signature = cppSignature.join(QLatin1String("::"));
+ s << INDENT << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << signature << "\");\n";
+ cppSignature.removeFirst();
+ }
+ }
+
+ s << Qt::endl;
+ if (maxTypeIndex)
+ s << INDENT << "Shiboken::Module::registerTypes(module, " << cppApiVariableName() << ");\n";
+ s << INDENT << "Shiboken::Module::registerTypeConverters(module, " << convertersVariableName() << ");\n";
+
+ s << '\n' << INDENT << "if (PyErr_Occurred()) {\n" << indent(INDENT)
+ << INDENT << "PyErr_Print();\n"
+ << INDENT << "Py_FatalError(\"can't initialize module " << moduleName() << "\");\n"
+ << outdent(INDENT) << INDENT << "}\n";
+
+ // module inject-code target/end
+ if (!snips.isEmpty())
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode);
+
+ // module inject-code native/end
+ if (!snips.isEmpty())
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode);
+
+ if (usePySideExtensions()) {
+ for (AbstractMetaEnum *metaEnum : qAsConst(globalEnums))
+ if (!metaEnum->isAnonymous()) {
+ s << INDENT << "qRegisterMetaType< ::" << metaEnum->typeEntry()->qualifiedCppName() << " >(\"" << metaEnum->name() << "\");\n";
+ }
+
+ // cleanup staticMetaObject attribute
+ s << INDENT << "PySide::registerCleanupFunction(cleanTypesAttributes);\n\n";
+ }
+
+ // finish the rest of __signature__ initialization.
+ s << INDENT << "FinishSignatureInitialization(module, " << moduleName()
+ << "_SignatureStrings);\n"
+ << INDENT << "\nreturn module;\n}\n";
+
+ return file.done() != FileOut::Failure;
+}
+
+static ArgumentOwner getArgumentOwner(const AbstractMetaFunction *func, int argIndex)
+{
+ ArgumentOwner argOwner = func->argumentOwner(func->ownerClass(), argIndex);
+ if (argOwner.index == ArgumentOwner::InvalidIndex)
+ argOwner = func->argumentOwner(func->declaringClass(), argIndex);
+ return argOwner;
+}
+
+bool CppGenerator::writeParentChildManagement(QTextStream &s, const AbstractMetaFunction *func, int argIndex, bool useHeuristicPolicy)
+{
+ const int numArgs = func->arguments().count();
+ bool ctorHeuristicEnabled = func->isConstructor() && useCtorHeuristic() && useHeuristicPolicy;
+
+ const auto &groups = func->implementingClass()
+ ? getFunctionGroups(func->implementingClass())
+ : getGlobalFunctionGroups();
+ bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(OverloadData(groups[func->name()], this));
+
+ ArgumentOwner argOwner = getArgumentOwner(func, argIndex);
+ ArgumentOwner::Action action = argOwner.action;
+ int parentIndex = argOwner.index;
+ int childIndex = argIndex;
+ if (ctorHeuristicEnabled && argIndex > 0 && numArgs) {
+ const AbstractMetaArgument &arg = func->arguments().at(argIndex-1);
+ if (arg.name() == QLatin1String("parent") && isObjectType(arg.type())) {
+ action = ArgumentOwner::Add;
+ parentIndex = argIndex;
+ childIndex = -1;
+ }
+ }
+
+ 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 = QLatin1String("Py_None");
+ } else {
+ if (parentIndex == 0) {
+ parentVariable = QLatin1String(PYTHON_RETURN_VAR);
+ } else if (parentIndex == -1) {
+ parentVariable = QLatin1String("self");
+ } else {
+ parentVariable = usePyArgs
+ ? pythonArgsAt(parentIndex - 1) : QLatin1String(PYTHON_ARG);
+ }
+ }
+
+ if (childIndex == 0) {
+ childVariable = QLatin1String(PYTHON_RETURN_VAR);
+ } else if (childIndex == -1) {
+ childVariable = QLatin1String("self");
+ } else {
+ childVariable = usePyArgs
+ ? pythonArgsAt(childIndex - 1) : QLatin1String(PYTHON_ARG);
+ }
+
+ s << INDENT << "Shiboken::Object::setParent(" << parentVariable << ", " << childVariable << ");\n";
+ return true;
+ }
+
+ return false;
+}
+
+void CppGenerator::writeParentChildManagement(QTextStream &s, const AbstractMetaFunction *func, bool useHeuristicForReturn)
+{
+ const int numArgs = func->arguments().count();
+
+ // -1 = return value
+ // 0 = self
+ // 1..n = func. args.
+ for (int i = -1; i <= numArgs; ++i)
+ writeParentChildManagement(s, func, i, useHeuristicForReturn);
+
+ if (useHeuristicForReturn)
+ writeReturnValueHeuristics(s, func);
+}
+
+void CppGenerator::writeReturnValueHeuristics(QTextStream &s, const AbstractMetaFunction *func)
+{
+ const AbstractMetaType &type = func->type();
+ if (!useReturnValueHeuristic()
+ || !func->ownerClass()
+ || type.isVoid()
+ || func->isStatic()
+ || func->isConstructor()
+ || !func->typeReplaced(0).isEmpty()) {
+ return;
+ }
+
+ ArgumentOwner argOwner = getArgumentOwner(func, ArgumentOwner::ReturnIndex);
+ if (argOwner.action == ArgumentOwner::Invalid || argOwner.index != ArgumentOwner::ThisIndex) {
+ if (isPointerToWrapperType(type))
+ s << INDENT << "Shiboken::Object::setParent(self, " << PYTHON_RETURN_VAR << ");\n";
+ }
+}
+
+void CppGenerator::writeHashFunction(QTextStream &s, const GeneratorContext &context)
+{
+ const AbstractMetaClass *metaClass = context.metaClass();
+ const char hashType[] = "Py_hash_t";
+ s << "static " << hashType << ' ' << cpythonBaseName(metaClass)
+ << "_HashFunc(PyObject *self) {\n";
+ writeCppSelfDefinition(s, context);
+ s << INDENT << "return " << hashType << '('
+ << metaClass->typeEntry()->hashFunction() << '('
+ << (isObjectType(metaClass) ? "" : "*") << CPP_SELF_VAR << "));\n";
+ s<< "}\n\n";
+}
+
+void CppGenerator::writeDefaultSequenceMethods(QTextStream &s, const GeneratorContext &context)
+{
+ const AbstractMetaClass *metaClass = context.metaClass();
+ ErrorCode errorCode(0);
+
+ // __len__
+ s << "Py_ssize_t " << cpythonBaseName(metaClass->typeEntry())
+ << "__len__(PyObject *self)\n{\n";
+ writeCppSelfDefinition(s, context);
+ s << INDENT << "return " << CPP_SELF_VAR << "->size();\n";
+ s << "}\n";
+
+ // __getitem__
+ s << "PyObject *" << cpythonBaseName(metaClass->typeEntry())
+ << "__getitem__(PyObject *self, Py_ssize_t _i)\n{\n";
+ writeCppSelfDefinition(s, context);
+ writeIndexError(s, QLatin1String("index out of bounds"));
+
+ QString value;
+ s << INDENT << metaClass->qualifiedCppName() << "::const_iterator _item = "
+ << CPP_SELF_VAR << "->begin();\n"
+ << INDENT << "std::advance(_item, _i);\n";
+
+ const AbstractMetaTypeList &instantiations = metaClass->templateBaseClassInstantiations();
+ if (instantiations.isEmpty()) {
+ qFatal("shiboken: %s: Internal error, no instantiations of \"%s\" were found.",
+ __FUNCTION__, qPrintable(metaClass->qualifiedCppName()));
+ }
+ const AbstractMetaType &itemType = instantiations.constFirst();
+
+ s << INDENT << "return ";
+ writeToPythonConversion(s, itemType, metaClass, QLatin1String("*_item"));
+ s << ";\n";
+ s << "}\n";
+
+ // __setitem__
+ ErrorCode errorCode2(-1);
+ s << "int " << cpythonBaseName(metaClass->typeEntry())
+ << "__setitem__(PyObject *self, Py_ssize_t _i, PyObject *pyArg)\n{\n";
+ writeCppSelfDefinition(s, context);
+ writeIndexError(s, QLatin1String("list assignment index out of range"));
+
+ s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ";\n";
+ s << INDENT << "if (!";
+ writeTypeCheck(s, itemType, QLatin1String("pyArg"), isNumber(itemType.typeEntry()));
+ s << ") {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "PyErr_SetString(PyExc_TypeError, \"attributed value with wrong type, '";
+ s << itemType.name() << "' or other convertible type expected\");\n";
+ s << INDENT << "return -1;\n";
+ }
+ s << INDENT << "}\n";
+ writeArgumentConversion(s, itemType, QLatin1String("cppValue"), QLatin1String("pyArg"), metaClass);
+
+ s << INDENT << metaClass->qualifiedCppName() << "::iterator _item = "
+ << CPP_SELF_VAR << "->begin();\n"
+ << INDENT << "std::advance(_item, _i);\n"
+ << INDENT << "*_item = cppValue;\n";
+
+ s << INDENT << "return {};\n";
+ s << "}\n";
+}
+void CppGenerator::writeIndexError(QTextStream &s, const QString &errorMsg)
+{
+ s << INDENT << "if (_i < 0 || _i >= (Py_ssize_t) " << CPP_SELF_VAR << "->size()) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "PyErr_SetString(PyExc_IndexError, \"" << errorMsg << "\");\n";
+ s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl;
+ }
+ s << INDENT << "}\n";
+}
+
+QString CppGenerator::writeReprFunction(QTextStream &s,
+ const GeneratorContext &context,
+ uint indirections)
+{
+ const AbstractMetaClass *metaClass = context.metaClass();
+ QString funcName = cpythonBaseName(metaClass) + QLatin1String("__repr__");
+ s << "extern \"C\"\n{\n";
+ s << "static PyObject *" << funcName << "(PyObject *self)\n{\n";
+ writeCppSelfDefinition(s, context);
+ s << INDENT << "QBuffer buffer;\n";
+ s << INDENT << "buffer.open(QBuffer::ReadWrite);\n";
+ s << INDENT << "QDebug dbg(&buffer);\n";
+ s << INDENT << "dbg << ";
+ if (metaClass->typeEntry()->isValue() || indirections == 0)
+ s << '*';
+ s << CPP_SELF_VAR << ";\n";
+ s << INDENT << "buffer.close();\n";
+ s << INDENT << "QByteArray str = buffer.data();\n";
+ s << INDENT << "int idx = str.indexOf('(');\n";
+ s << INDENT << "if (idx >= 0)\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "str.replace(0, idx, Py_TYPE(self)->tp_name);\n";
+ }
+ s << INDENT << "str = str.trimmed();\n";
+ s << INDENT << "PyObject *mod = PyDict_GetItem(Py_TYPE(self)->tp_dict, 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 << INDENT << "if (mod && !strchr(str, '.'))\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "return Shiboken::String::fromFormat(\"<%s.%s at %p>\", Shiboken::String::toCString(mod), str.constData(), self);\n";
+ }
+ s << INDENT << "else\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "return Shiboken::String::fromFormat(\"<%s at %p>\", str.constData(), self);\n";
+ }
+ s << "}\n";
+ s << "} // extern C\n\n";
+ return funcName;
+}
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.h b/sources/shiboken6/generator/shiboken/cppgenerator.h
new file mode 100644
index 000000000..25d4b22db
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/cppgenerator.h
@@ -0,0 +1,419 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt for Python.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CPPGENERATOR_H
+#define CPPGENERATOR_H
+
+#include "shibokengenerator.h"
+
+/**
+ * The CppGenerator generate the implementations of C++ bindings classes.
+ */
+class CppGenerator : public ShibokenGenerator
+{
+public:
+ CppGenerator();
+
+ const char *name() const override { return "Source generator"; }
+
+protected:
+ QString fileNameSuffix() const override;
+ QString fileNameForContext(const GeneratorContext &context) const override;
+ QVector<AbstractMetaFunctionList> filterGroupedOperatorFunctions(const AbstractMetaClass *metaClass,
+ uint query);
+ void generateClass(QTextStream &s, const GeneratorContext &classContext) override;
+ bool finishGeneration() override;
+
+private:
+ void writeInitFunc(QTextStream &declStr, QTextStream &callStr,
+ const Indentor &indent, const QString &initFunctionName,
+ const TypeEntry *enclosingEntry = nullptr);
+ void writeCacheResetNative(QTextStream &s, const GeneratorContext &classContext);
+ void writeConstructorNative(QTextStream &s, const GeneratorContext &classContext,
+ const AbstractMetaFunction *func);
+ void writeDestructorNative(QTextStream &s, const GeneratorContext &classContext);
+
+ QString getVirtualFunctionReturnTypeName(const AbstractMetaFunction *func);
+ void writeVirtualMethodNative(QTextStream &s, const AbstractMetaFunction *func, int cacheIndex);
+ void writeVirtualMethodCppCall(QTextStream &s, const AbstractMetaFunction *func,
+ const QString &funcName, const CodeSnipList &snips,
+ const AbstractMetaArgument *lastArg, const TypeEntry *retType,
+ const QString &returnStatement);
+ QString virtualMethodReturn(QTextStream &s,
+ const AbstractMetaFunction *func,
+ const FunctionModificationList &functionModifications);
+ void writeMetaObjectMethod(QTextStream &s, const GeneratorContext &classContext);
+ void writeMetaCast(QTextStream &s, const GeneratorContext &classContext);
+
+ void writeEnumConverterFunctions(QTextStream &s, const TypeEntry *enumType);
+ void writeEnumConverterFunctions(QTextStream &s, const AbstractMetaEnum *metaEnum);
+ void writeConverterFunctions(QTextStream &s, const AbstractMetaClass *metaClass,
+ const GeneratorContext &classContext);
+ void writeCustomConverterFunctions(QTextStream &s, const CustomConversion *customConversion);
+ void writeConverterRegister(QTextStream &s, const AbstractMetaClass *metaClass,
+ const GeneratorContext &classContext);
+ void writeCustomConverterRegister(QTextStream &s, const CustomConversion *customConversion, const QString &converterVar);
+
+ void writeContainerConverterFunctions(QTextStream &s, const AbstractMetaType &containerType);
+
+ void writeSmartPointerConverterFunctions(QTextStream &s, const AbstractMetaType &smartPointerType);
+
+ void writeMethodWrapperPreamble(QTextStream &s, OverloadData &overloadData,
+ const GeneratorContext &context);
+ void writeConstructorWrapper(QTextStream &s, const AbstractMetaFunctionList &overloads,
+ const GeneratorContext &classContext);
+ void writeMethodWrapper(QTextStream &s, const AbstractMetaFunctionList &overloads,
+ const GeneratorContext &classContext);
+ void writeArgumentsInitializer(QTextStream &s, OverloadData &overloadData);
+ void writeCppSelfConversion(QTextStream &s, const GeneratorContext &context,
+ const QString &className, bool useWrapperClass);
+ void writeCppSelfDefinition(QTextStream &s,
+ const AbstractMetaFunction *func,
+ const GeneratorContext &context,
+ bool hasStaticOverload = false);
+ void writeCppSelfDefinition(QTextStream &s,
+ const GeneratorContext &context,
+ bool hasStaticOverload = false,
+ bool cppSelfAsReference = false);
+
+ void writeErrorSection(QTextStream &s, OverloadData &overloadData);
+ void writeFunctionReturnErrorCheckSection(QTextStream &s, bool hasReturnValue = true);
+
+ /// Writes the check section for the validity of wrapped C++ objects.
+ void writeInvalidPyObjectCheck(QTextStream &s, const QString &pyObj);
+
+ void writeTypeCheck(QTextStream &s, AbstractMetaType argType, const QString &argumentName,
+ bool isNumber = false, const QString &customType = QString(),
+ bool rejectNull = false);
+ void writeTypeCheck(QTextStream& s, const OverloadData *overloadData, QString argumentName);
+
+ void writeTypeDiscoveryFunction(QTextStream &s, const AbstractMetaClass *metaClass);
+
+ void writeSetattroDefinition(QTextStream &s, const AbstractMetaClass *metaClass) const;
+ void writeSetattroDefaultReturn(QTextStream &s) const;
+ void writeSmartPointerSetattroFunction(QTextStream &s, const GeneratorContext &context);
+ void writeSetattroFunction(QTextStream &s, AttroCheck attroCheck,
+ const GeneratorContext &context);
+ static void writeGetattroDefinition(QTextStream &s, const AbstractMetaClass *metaClass);
+ void writeSmartPointerGetattroFunction(QTextStream &s, const GeneratorContext &context);
+ void writeGetattroFunction(QTextStream &s, AttroCheck attroCheck,
+ const GeneratorContext &context);
+ QString writeSmartPointerGetterCast();
+ QString qObjectGetAttroFunction() const;
+
+ /**
+ * 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
+ */
+ void writeArgumentConversion(QTextStream &s, const AbstractMetaType &argType,
+ const QString &argName, const QString &pyArgName,
+ const AbstractMetaClass *context = nullptr,
+ const QString &defaultValue = QString(),
+ bool castArgumentAsUnused = false);
+
+ /**
+ * 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 argPos Argument position in the function signature.
+ * Note that the position 0 represents the return value, and the function
+ * parameters start counting on 1.
+ * \param newType It is set to true if the type returned is a new object that must be deallocated.
+ * \return The type of the argument indicated by \p argPos.
+ */
+ const AbstractMetaType getArgumentType(const AbstractMetaFunction *func, int argPos);
+
+ void writePythonToCppTypeConversion(QTextStream &s,
+ const AbstractMetaType &type,
+ const QString &pyIn,
+ const QString &cppOut,
+ const AbstractMetaClass *context = nullptr,
+ const QString &defaultValue = QString());
+
+ /// Writes the conversion rule for arguments of regular and virtual methods.
+ void writeConversionRule(QTextStream &s, const AbstractMetaFunction *func, TypeSystem::Language language);
+ /// Writes the conversion rule for the return value of a method.
+ void writeConversionRule(QTextStream &s, const AbstractMetaFunction *func, TypeSystem::Language language, const QString &outputVar);
+
+ /**
+ * 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'
+ */
+ void writeNoneReturn(QTextStream &s, const AbstractMetaFunction *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(QTextStream &s, const OverloadData &overloadData);
+ /// Recursive auxiliar method to the other writeOverloadedFunctionDecisor.
+ void writeOverloadedFunctionDecisorEngine(QTextStream &s, const OverloadData *parentOverloadData);
+
+ /// Writes calls to all the possible method/function overloads.
+ void writeFunctionCalls(QTextStream &s,
+ const OverloadData &overloadData,
+ const GeneratorContext &context);
+
+ /// Writes the call to a single function usually from a collection of overloads.
+ void writeSingleFunctionCall(QTextStream &s,
+ const OverloadData &overloadData,
+ const AbstractMetaFunction *func,
+ const GeneratorContext &context);
+
+ /// 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 CustomConversion::TargetToNativeConversion *toNative, const TypeEntry *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 CustomConversion::TargetToNativeConversion *toNative, const TypeEntry *targetType);
+
+ /// Writes a C++ to Python conversion function.
+ void writeCppToPythonFunction(QTextStream &s, const QString &code, const QString &sourceTypeName, QString targetTypeName = QString());
+ void writeCppToPythonFunction(QTextStream &s, const CustomConversion *customConversion);
+ void writeCppToPythonFunction(QTextStream &s, const AbstractMetaType &containerType);
+
+ /// Writes a Python to C++ conversion function.
+ void writePythonToCppFunction(QTextStream &s, const QString &code, const QString &sourceTypeName, const QString &targetTypeName);
+
+ /// Writes a Python to C++ convertible check function.
+ void writeIsPythonConvertibleToCppFunction(QTextStream &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(QTextStream &s,
+ const AbstractMetaType &sourceType,
+ const AbstractMetaType &targetType,
+ QString typeCheck = QString(),
+ QString conversion = QString(),
+ const QString &preConversion = QString());
+ /// Writes a pair of Python to C++ conversion and check functions for implicit conversions.
+ void writePythonToCppConversionFunctions(QTextStream &s,
+ const CustomConversion::TargetToNativeConversion *toNative,
+ const TypeEntry *targetType);
+
+ /// Writes a pair of Python to C++ conversion and check functions for instantiated container types.
+ void writePythonToCppConversionFunctions(QTextStream &s, const AbstractMetaType &containerType);
+
+ void writeAddPythonToCppConversion(QTextStream &s, const QString &converterVar, const QString &pythonToCppFunc, const QString &isConvertibleFunc);
+
+ void writeNamedArgumentResolution(QTextStream &s, const AbstractMetaFunction *func, bool usePyArgs);
+
+ /// Returns a string containing the name of an argument for the given function and argument index.
+ QString argumentNameFromIndex(const AbstractMetaFunction *func, int argIndex, const AbstractMetaClass **wrappedClass);
+ void writeMethodCall(QTextStream &s, const AbstractMetaFunction *func,
+ const GeneratorContext &context, int maxArgs = 0);
+
+ QString getInitFunctionName(const GeneratorContext &context) const;
+ QString getSimpleClassInitFunctionName(const AbstractMetaClass *metaClass) const;
+
+ void writeSignatureStrings(QTextStream &s, QTextStream &signatureStream,
+ const QString &arrayName,
+ const char *comment) const;
+ void writeClassRegister(QTextStream &s,
+ const AbstractMetaClass *metaClass,
+ const GeneratorContext &classContext,
+ QTextStream &signatureStream);
+ void writeClassDefinition(QTextStream &s,
+ const AbstractMetaClass *metaClass,
+ const GeneratorContext &classContext);
+ void writeMethodDefinitionEntry(QTextStream &s, const AbstractMetaFunctionList &overloads);
+ void writeMethodDefinition(QTextStream &s, const AbstractMetaFunctionList &overloads);
+ void writeSignatureInfo(QTextStream &s, const AbstractMetaFunctionList &overloads);
+ /// Writes the implementation of all methods part of python sequence protocol
+ void writeSequenceMethods(QTextStream &s,
+ const AbstractMetaClass *metaClass,
+ const GeneratorContext &context);
+ void writeTypeAsSequenceDefinition(QTextStream &s, const AbstractMetaClass *metaClass);
+
+ /// Writes the PyMappingMethods structure for types that supports the python mapping protocol.
+ void writeTypeAsMappingDefinition(QTextStream &s, const AbstractMetaClass *metaClass);
+ void writeMappingMethods(QTextStream &s,
+ const AbstractMetaClass *metaClass,
+ const GeneratorContext &context);
+
+ void writeTypeAsNumberDefinition(QTextStream &s, const AbstractMetaClass *metaClass);
+
+ void writeTpTraverseFunction(QTextStream &s, const AbstractMetaClass *metaClass);
+ void writeTpClearFunction(QTextStream &s, const AbstractMetaClass *metaClass);
+
+ void writeCopyFunction(QTextStream &s, const GeneratorContext &context);
+
+ void writeGetterFunction(QTextStream &s,
+ const AbstractMetaField *metaField,
+ const GeneratorContext &context);
+ void writeGetterFunction(QTextStream &s,
+ const QPropertySpec *property,
+ const GeneratorContext &context);
+ void writeSetterFunctionPreamble(QTextStream &s,
+ const QString &name,
+ const QString &funcName,
+ const AbstractMetaType &type,
+ const GeneratorContext &context);
+ void writeSetterFunction(QTextStream &s,
+ const AbstractMetaField *metaField,
+ const GeneratorContext &context);
+ void writeSetterFunction(QTextStream &s,
+ const QPropertySpec *property,
+ const GeneratorContext &context);
+
+ void writeRichCompareFunction(QTextStream &s, const GeneratorContext &context);
+
+ void writeEnumsInitialization(QTextStream &s, AbstractMetaEnumList &enums);
+ void writeEnumInitialization(QTextStream &s, const AbstractMetaEnum *metaEnum);
+
+ void writeSignalInitialization(QTextStream &s, const AbstractMetaClass *metaClass);
+
+ void writeFlagsMethods(QTextStream &s, const AbstractMetaEnum *cppEnum);
+ void writeFlagsToLong(QTextStream &s, const AbstractMetaEnum *cppEnum);
+ void writeFlagsNonZero(QTextStream &s, const AbstractMetaEnum *cppEnum);
+ void writeFlagsNumberMethodsDefinition(QTextStream &s, const AbstractMetaEnum *cppEnum);
+ void writeFlagsNumberMethodsDefinitions(QTextStream &s, const AbstractMetaEnumList &enums);
+ void writeFlagsBinaryOperator(QTextStream &s, const AbstractMetaEnum *cppEnum,
+ const QString &pyOpName, const QString &cppOpName);
+ void writeFlagsUnaryOperator(QTextStream &s, const AbstractMetaEnum *cppEnum,
+ const QString &pyOpName, const QString &cppOpName,
+ bool boolResult = false);
+
+ /// Writes the function that registers the multiple inheritance information for the classes that need it.
+ void writeMultipleInheritanceInitializerFunction(QTextStream &s, const AbstractMetaClass *metaClass);
+ /// Writes the implementation of special cast functions, used when we need to cast a class with multiple inheritance.
+ void writeSpecialCastFunction(QTextStream &s, const AbstractMetaClass *metaClass);
+
+ void writePrimitiveConverterInitialization(QTextStream &s, const CustomConversion *customConversion);
+ void writeEnumConverterInitialization(QTextStream &s, const TypeEntry *enumType);
+ void writeEnumConverterInitialization(QTextStream &s, const AbstractMetaEnum *metaEnum);
+ void writeContainerConverterInitialization(QTextStream &s, const AbstractMetaType &type);
+ void writeSmartPointerConverterInitialization(QTextStream &s, const AbstractMetaType &ype);
+ void writeExtendedConverterInitialization(QTextStream &s, const TypeEntry *externalType, const QVector<const AbstractMetaClass *>& conversions);
+
+ void writeParentChildManagement(QTextStream &s, const AbstractMetaFunction *func, bool userHeuristicForReturn);
+ bool writeParentChildManagement(QTextStream &s, const AbstractMetaFunction *func, int argIndex, bool userHeuristicPolicy);
+ void writeReturnValueHeuristics(QTextStream &s, const AbstractMetaFunction *func);
+ void writeInitQtMetaTypeFunctionBody(QTextStream &s, const GeneratorContext &context) const;
+
+ /**
+ * 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.
+ */
+ QString multipleInheritanceInitializerFunctionName(const AbstractMetaClass *metaClass);
+
+ /// Returns a list of all classes to which the given class could be cast.
+ QStringList getAncestorMultipleInheritance(const AbstractMetaClass *metaClass);
+
+ /// Returns true if the given class supports the python number protocol
+ bool supportsNumberProtocol(const AbstractMetaClass *metaClass);
+
+ /// Returns true if the given class supports the python sequence protocol
+ bool supportsSequenceProtocol(const AbstractMetaClass *metaClass);
+
+ /// Returns true if the given class supports the python mapping protocol
+ bool supportsMappingProtocol(const AbstractMetaClass *metaClass);
+
+ /// Returns true if generator should produce getters and setters for the given class.
+ bool shouldGenerateGetSetList(const AbstractMetaClass *metaClass);
+
+ void writeHashFunction(QTextStream &s, const GeneratorContext &context);
+
+ /// Write default implementations for sequence protocol
+ void writeDefaultSequenceMethods(QTextStream &s, const GeneratorContext &context);
+ /// Helper function for writeStdListWrapperMethods.
+ void writeIndexError(QTextStream &s, const QString &errorMsg);
+
+ QString writeReprFunction(QTextStream &s, const GeneratorContext &context, uint indirections);
+
+ const AbstractMetaFunction *boolCast(const AbstractMetaClass *metaClass) const;
+ bool hasBoolCast(const AbstractMetaClass *metaClass) const
+ { return boolCast(metaClass) != nullptr; }
+
+ AbstractMetaType findSmartPointerInstantiation(const TypeEntry *entry) const;
+
+ // Number protocol structure members names.
+ static QHash<QString, QString> m_nbFuncs;
+
+ // Maps special function names to function parameters and return types
+ // used by CPython API in the sequence protocol.
+ QHash<QString, QPair<QString, QString> > m_sequenceProtocol;
+ // Sequence protocol structure members names.
+ static QHash<QString, QString> m_sqFuncs;
+
+ // Maps special function names to function parameters and return types
+ // used by CPython API in the mapping protocol.
+ QHash<QString, QPair<QString, QString> > m_mappingProtocol;
+ // Mapping protocol structure members names.
+ static QHash<QString, QString> m_mpFuncs;
+
+ static QString m_currentErrorCode;
+
+ /// Helper class to set and restore the current error code.
+ class ErrorCode {
+ public:
+ explicit ErrorCode(QString errorCode) {
+ m_savedErrorCode = CppGenerator::m_currentErrorCode;
+ CppGenerator::m_currentErrorCode = errorCode;
+ }
+ explicit ErrorCode(int errorCode) {
+ m_savedErrorCode = CppGenerator::m_currentErrorCode;
+ CppGenerator::m_currentErrorCode = QString::number(errorCode);
+ }
+ ~ErrorCode() {
+ CppGenerator::m_currentErrorCode = m_savedErrorCode;
+ }
+ private:
+ QString m_savedErrorCode;
+ };
+};
+
+#endif // CPPGENERATOR_H
diff --git a/sources/shiboken6/generator/shiboken/ctypenames.h b/sources/shiboken6/generator/shiboken/ctypenames.h
new file mode 100644
index 000000000..abac261d5
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/ctypenames.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt for Python.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CTYPENAMES_H
+#define CTYPENAMES_H
+
+#include <QtCore/QString>
+
+static inline QString boolT() { return QStringLiteral("bool"); }
+static inline QString intT() { return QStringLiteral("int"); }
+static inline QString unsignedT() { return QStringLiteral("unsigned"); }
+static inline QString unsignedIntT() { return QStringLiteral("unsigned int"); }
+static inline QString longT() { return QStringLiteral("long"); }
+static inline QString unsignedLongT() { return QStringLiteral("unsigned long"); }
+static inline QString shortT() { return QStringLiteral("short"); }
+static inline QString unsignedShortT() { return QStringLiteral("unsigned short"); }
+static inline QString unsignedCharT() { return QStringLiteral("unsigned char"); }
+static inline QString longLongT() { return QStringLiteral("long long"); }
+static inline QString unsignedLongLongT() { return QStringLiteral("unsigned long long"); }
+static inline QString charT() { return QStringLiteral("char"); }
+static inline QString floatT() { return QStringLiteral("float"); }
+static inline QString doubleT() { return QStringLiteral("double"); }
+static inline QString constCharPtrT() { return QStringLiteral("const char*"); }
+
+static inline QString qByteArrayT() { return QStringLiteral("QByteArray"); }
+static inline QString qMetaObjectT() { return QStringLiteral("QMetaObject"); }
+static inline QString qObjectT() { return QStringLiteral("QObject"); }
+static inline QString qStringT() { return QStringLiteral("QString"); }
+static inline QString qVariantT() { return QStringLiteral("QVariant"); }
+
+#endif // CTYPENAMES_H
diff --git a/sources/shiboken6/generator/shiboken/headergenerator.cpp b/sources/shiboken6/generator/shiboken/headergenerator.cpp
new file mode 100644
index 000000000..d607ed643
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/headergenerator.cpp
@@ -0,0 +1,661 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt for Python.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "headergenerator.h"
+#include <abstractmetalang.h>
+#include <typedatabase.h>
+#include <reporthandler.h>
+#include <fileout.h>
+#include "parser/codemodel.h"
+
+#include <algorithm>
+
+#include <QtCore/QDir>
+#include <QtCore/QTextStream>
+#include <QtCore/QVariant>
+#include <QtCore/QDebug>
+
+QString HeaderGenerator::fileNameSuffix() const
+{
+ return QLatin1String("_wrapper.h");
+}
+
+QString HeaderGenerator::fileNameForContext(const GeneratorContext &context) const
+{
+ const AbstractMetaClass *metaClass = context.metaClass();
+ if (!context.forSmartPointer()) {
+ QString fileNameBase = metaClass->qualifiedCppName().toLower();
+ fileNameBase.replace(QLatin1String("::"), QLatin1String("_"));
+ return fileNameBase + fileNameSuffix();
+ }
+ QString fileNameBase = getFileNameBaseForSmartPointer(context.preciseType(), metaClass);
+ return fileNameBase + fileNameSuffix();
+}
+
+void HeaderGenerator::writeCopyCtor(QTextStream &s, const AbstractMetaClass *metaClass) const
+{
+ s << INDENT << wrapperName(metaClass) << "(const " << metaClass->qualifiedCppName() << "& self)";
+ s << " : " << metaClass->qualifiedCppName() << "(self)\n";
+ s << INDENT << "{\n";
+ s << INDENT << "}\n\n";
+}
+
+void HeaderGenerator::writeProtectedFieldAccessors(QTextStream &s, const AbstractMetaField *field) const
+{
+ const AbstractMetaType &metaType = field->type();
+ QString fieldType = metaType.cppSignature();
+ QString fieldName = field->enclosingClass()->qualifiedCppName() + QLatin1String("::") + field->name();
+
+ // Force use of pointer to return internal variable memory
+ bool useReference = (!metaType.isConstant() &&
+ !metaType.isEnum() &&
+ !metaType.isPrimitive() &&
+ metaType.indirections() == 0);
+
+
+ // Get function
+ s << INDENT << "inline " << fieldType
+ << (useReference ? " *" : " ")
+ << ' ' << protectedFieldGetterName(field) << "()"
+ << " { return "
+ << (useReference ? " &" : " ") << "this->" << fieldName << "; }\n";
+
+ // Set function
+ s << INDENT << "inline void " << protectedFieldSetterName(field) << '(' << fieldType << " value)"
+ << " { " << fieldName << " = value; }\n";
+}
+
+void HeaderGenerator::generateClass(QTextStream &s, const GeneratorContext &classContextIn)
+{
+ GeneratorContext classContext = classContextIn;
+ const AbstractMetaClass *metaClass = classContext.metaClass();
+ m_inheritedOverloads.clear();
+ Indentation indent(INDENT);
+
+ // write license comment
+ s << licenseComment();
+
+ QString wrapperName;
+ if (!classContext.forSmartPointer()) {
+ wrapperName = classContext.useWrapper()
+ ? classContext.wrapperName() : metaClass->qualifiedCppName();
+ } else {
+ wrapperName = classContext.smartPointerWrapperName();
+ }
+ QString outerHeaderGuard = getFilteredCppSignatureString(wrapperName).toUpper();
+ QString innerHeaderGuard;
+
+ // Header
+ s << "#ifndef SBK_" << outerHeaderGuard << "_H\n";
+ s << "#define SBK_" << outerHeaderGuard << "_H\n\n";
+
+ if (!avoidProtectedHack())
+ s << "#define protected public\n\n";
+
+ //Includes
+ auto typeEntry = metaClass->typeEntry();
+ s << typeEntry->include() << '\n';
+ if (classContext.useWrapper() && !typeEntry->extraIncludes().isEmpty()) {
+ s << "\n// Extra includes\n";
+ for (const Include &inc : typeEntry->extraIncludes())
+ s << inc.toString() << '\n';
+ }
+
+ if (classContext.useWrapper() && usePySideExtensions() && metaClass->isQObject())
+ s << "namespace PySide { class DynamicQMetaObject; }\n\n";
+
+ while (classContext.useWrapper()) {
+ if (!innerHeaderGuard.isEmpty()) {
+ s << "# ifndef SBK_" << innerHeaderGuard << "_H\n";
+ s << "# define SBK_" << innerHeaderGuard << "_H\n\n";
+ s << "// Inherited base class:\n";
+ }
+
+ // Class
+ s << "class " << wrapperName;
+ s << " : public " << metaClass->qualifiedCppName();
+
+ s << "\n{\npublic:\n";
+
+ const AbstractMetaFunctionList &funcs = filterFunctions(metaClass);
+ int maxOverrides = 0;
+ for (AbstractMetaFunction *func : funcs) {
+ if ((func->attributes() & AbstractMetaAttributes::FinalCppMethod) == 0) {
+ writeFunction(s, func);
+ // PYSIDE-803: Build a boolean cache for unused overrides.
+ if (shouldWriteVirtualMethodNative(func))
+ maxOverrides++;
+ }
+ }
+ if (!maxOverrides)
+ maxOverrides = 1;
+
+ if (avoidProtectedHack() && metaClass->hasProtectedFields()) {
+ const AbstractMetaFieldList &fields = metaClass->fields();
+ for (AbstractMetaField *field : fields) {
+ if (!field->isProtected())
+ continue;
+ writeProtectedFieldAccessors(s, field);
+ }
+ }
+
+ //destructor
+ // PYSIDE-504: When C++ 11 is used, then the destructor must always be written.
+ // See generator.h for further reference.
+ if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor() || alwaysGenerateDestructor) {
+ s << INDENT;
+ if (avoidProtectedHack() && metaClass->hasPrivateDestructor())
+ s << "// C++11: need to declare (unimplemented) destructor because "
+ "the base class destructor is private.\n";
+ s << '~' << wrapperName << "();\n";
+ }
+
+ writeClassCodeSnips(s, metaClass->typeEntry()->codeSnips(),
+ TypeSystem::CodeSnipPositionDeclaration, TypeSystem::NativeCode,
+ classContext);
+
+ if ((!avoidProtectedHack() || !metaClass->hasPrivateDestructor())
+ && usePySideExtensions() && metaClass->isQObject()) {
+ s << "public:\n";
+ s << INDENT << "int qt_metacall(QMetaObject::Call call, int id, void **args) override;\n";
+ s << INDENT << "void *qt_metacast(const char *_clname) override;\n";
+ }
+
+ if (!m_inheritedOverloads.isEmpty()) {
+ s << INDENT << "// Inherited overloads, because the using keyword sux\n";
+ writeInheritedOverloads(s);
+ m_inheritedOverloads.clear();
+ }
+
+ if (usePySideExtensions())
+ s << INDENT << "static void pysideInitQtMetaTypes();\n";
+
+ s << INDENT << "void resetPyMethodCache();\n";
+ s << "private:\n";
+ s << INDENT << "mutable bool m_PyMethodCache[" << maxOverrides << "];\n";
+
+ s << "};\n\n";
+ if (!innerHeaderGuard.isEmpty())
+ s << "# endif // SBK_" << innerHeaderGuard << "_H\n\n";
+
+ // PYSIDE-500: Use also includes for inherited wrapper classes, because
+ // without the protected hack, we sometimes need to cast inherited wrappers.
+ // But we don't use multiple include files. Instead, they are inserted as recursive
+ // headers. This keeps the file structure as simple as before the enhanced inheritance.
+ metaClass = metaClass->baseClass();
+ if (!metaClass || !avoidProtectedHack())
+ break;
+ classContext = contextForClass(metaClass);
+ if (!classContext.forSmartPointer()) {
+ wrapperName = classContext.useWrapper()
+ ? classContext.wrapperName() : metaClass->qualifiedCppName();
+ } else {
+ wrapperName = classContext.smartPointerWrapperName();
+ }
+ innerHeaderGuard = getFilteredCppSignatureString(wrapperName).toUpper();
+ }
+
+ s << "#endif // SBK_" << outerHeaderGuard << "_H\n\n";
+}
+
+void HeaderGenerator::writeFunction(QTextStream &s, const AbstractMetaFunction *func)
+{
+
+ // do not write copy ctors here.
+ if (!func->isPrivate() && func->functionType() == AbstractMetaFunction::CopyConstructorFunction) {
+ writeCopyCtor(s, func->ownerClass());
+ return;
+ }
+ if (func->isUserAdded())
+ return;
+
+ if (avoidProtectedHack() && func->isProtected() && !func->isConstructor() && !func->isOperatorOverload()) {
+ s << INDENT << "inline " << (func->isStatic() ? "static " : "");
+ s << functionSignature(func, QString(), QLatin1String("_protected"), Generator::EnumAsInts|Generator::OriginalTypeDescription)
+ << " { ";
+ if (!func->isVoid())
+ s << "return ";
+ if (!func->isAbstract())
+ s << func->ownerClass()->qualifiedCppName() << "::";
+ s << func->originalName() << '(';
+ QStringList args;
+ const AbstractMetaArgumentList &arguments = func->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+ QString argName = arg.name();
+ const TypeEntry *enumTypeEntry = nullptr;
+ if (arg.type().isFlags())
+ enumTypeEntry = static_cast<const FlagsTypeEntry *>(arg.type().typeEntry())->originator();
+ else if (arg.type().isEnum())
+ enumTypeEntry = arg.type().typeEntry();
+ if (enumTypeEntry)
+ argName = QString::fromLatin1("%1(%2)").arg(arg.type().cppSignature(), argName);
+ args << argName;
+ }
+ s << args.join(QLatin1String(", ")) << ')';
+ s << "; }\n";
+ }
+
+ // pure virtual functions need a default implementation
+ const bool notAbstract = !func->isAbstract();
+ if ((func->isPrivate() && notAbstract && !visibilityModifiedToPrivate(func))
+ || (func->isModifiedRemoved() && notAbstract))
+ return;
+
+ if (avoidProtectedHack() && func->ownerClass()->hasPrivateDestructor()
+ && (func->isAbstract() || func->isVirtual()))
+ return;
+
+ if (func->isConstructor() || func->isAbstract() || func->isVirtual()) {
+ s << INDENT;
+ Options virtualOption = Generator::OriginalTypeDescription;
+
+ const bool virtualFunc = func->isVirtual() || func->isAbstract();
+ if (!virtualFunc && !func->hasSignatureModifications())
+ virtualOption = Generator::NoOption;
+
+ s << functionSignature(func, QString(), QString(), virtualOption);
+
+ if (virtualFunc)
+ s << " override";
+ s << ";\n";
+ // Check if this method hide other methods in base classes
+ const AbstractMetaFunctionList &ownerFuncs = func->ownerClass()->functions();
+ for (const AbstractMetaFunction *f : ownerFuncs) {
+ if (f != func
+ && !f->isConstructor()
+ && !f->isPrivate()
+ && !f->isVirtual()
+ && !f->isAbstract()
+ && !f->isStatic()
+ && f->name() == func->name()) {
+ m_inheritedOverloads << f;
+ }
+ }
+
+ // TODO: when modified an abstract method ceases to be virtual but stays abstract
+ //if (func->isModifiedRemoved() && func->isAbstract()) {
+ //}
+ }
+}
+
+static void _writeTypeIndexValue(QTextStream &s, const QString &variableName,
+ int typeIndex)
+{
+ s << " ";
+ s.setFieldWidth(56);
+ s << variableName;
+ s.setFieldWidth(0);
+ s << " = " << typeIndex;
+}
+
+static inline void _writeTypeIndexValueLine(QTextStream &s,
+ const QString &variableName,
+ int typeIndex)
+{
+ _writeTypeIndexValue(s, variableName, typeIndex);
+ s << ",\n";
+}
+
+void HeaderGenerator::writeTypeIndexValueLine(QTextStream &s, const TypeEntry *typeEntry)
+{
+ if (!typeEntry || !typeEntry->generateCode())
+ return;
+ s.setFieldAlignment(QTextStream::AlignLeft);
+ const int typeIndex = typeEntry->sbkIndex();
+ _writeTypeIndexValueLine(s, getTypeIndexVariableName(typeEntry), typeIndex);
+ if (typeEntry->isComplex()) {
+ const auto *cType = static_cast<const ComplexTypeEntry *>(typeEntry);
+ if (cType->baseContainerType()) {
+ const AbstractMetaClass *metaClass = AbstractMetaClass::findClass(classes(), cType);
+ if (metaClass->templateBaseClass())
+ _writeTypeIndexValueLine(s, getTypeIndexVariableName(metaClass, true), typeIndex);
+ }
+ }
+ if (typeEntry->isEnum()) {
+ auto ete = static_cast<const EnumTypeEntry *>(typeEntry);
+ if (ete->flags())
+ writeTypeIndexValueLine(s, ete->flags());
+ }
+}
+
+void HeaderGenerator::writeTypeIndexValueLines(QTextStream &s, const AbstractMetaClass *metaClass)
+{
+ 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())
+ writeTypeIndexValueLine(s, metaEnum->typeEntry());
+ }
+ if (NamespaceTypeEntry::isVisibleScope(typeEntry))
+ writeTypeIndexValueLine(s, metaClass->typeEntry());
+}
+
+// Format the typedefs for the typedef entries to be generated
+static void formatTypeDefEntries(QTextStream &s)
+{
+ QVector<const TypedefEntry *> 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 int nameSpaceCount = components.size() - 1;
+ for (int n = 0; n < nameSpaceCount; ++n)
+ s << "namespace " << components.at(n) << " {\n";
+ s << "using " << components.constLast() << " = " << e->sourceType() << ";\n";
+ for (int n = 0; n < nameSpaceCount; ++n)
+ s << "}\n";
+ }
+ s << '\n';
+}
+
+
+bool HeaderGenerator::finishGeneration()
+{
+ // Generate the main header for this module.
+ // This header should be included by binding modules
+ // extendind on top of this one.
+ QSet<Include> includes;
+ QString macros;
+ QTextStream macrosStream(&macros);
+ QString sbkTypeFunctions;
+ QTextStream typeFunctions(&sbkTypeFunctions);
+ QString protectedEnumSurrogates;
+ QTextStream protEnumsSurrogates(&protectedEnumSurrogates);
+
+ const auto snips = TypeDatabase::instance()->defaultTypeSystemType()->codeSnips();
+ if (!snips.isEmpty()) {
+ writeCodeSnips(macrosStream, snips, TypeSystem::CodeSnipPositionDeclaration,
+ TypeSystem::TargetLangCode);
+ }
+
+ Indentation indent(INDENT);
+
+ macrosStream << "// Type indices\nenum : int {\n";
+ AbstractMetaClassList classList = classes();
+
+ std::sort(classList.begin(), classList.end(), [](AbstractMetaClass *a, AbstractMetaClass *b) {
+ return a->typeEntry()->sbkIndex() < b->typeEntry()->sbkIndex();
+ });
+
+ for (const AbstractMetaClass *metaClass : classList)
+ writeTypeIndexValueLines(macrosStream, metaClass);
+
+ for (const AbstractMetaEnum *metaEnum : globalEnums())
+ writeTypeIndexValueLine(macrosStream, metaEnum->typeEntry());
+
+ // Write the smart pointer define indexes.
+ int smartPointerCountIndex = getMaxTypeIndex();
+ int smartPointerCount = 0;
+ const QVector<AbstractMetaType> &instantiatedSmartPtrs = instantiatedSmartPointers();
+ for (const AbstractMetaType &metaType : instantiatedSmartPtrs) {
+ QString indexName = getTypeIndexVariableName(metaType);
+ _writeTypeIndexValue(macrosStream, indexName, smartPointerCountIndex);
+ macrosStream << ", // " << metaType.cppSignature() << Qt::endl;
+ // Add a the same value for const pointees (shared_ptr<const Foo>).
+ const auto ptrName = metaType.typeEntry()->entryName();
+ int pos = indexName.indexOf(ptrName, 0, Qt::CaseInsensitive);
+ if (pos >= 0) {
+ indexName.insert(pos + ptrName.size() + 1, QLatin1String("CONST"));
+ _writeTypeIndexValue(macrosStream, indexName, smartPointerCountIndex);
+ macrosStream << ", // (const)\n";
+ }
+ ++smartPointerCountIndex;
+ ++smartPointerCount;
+ }
+
+ _writeTypeIndexValue(macrosStream,
+ QLatin1String("SBK_") + moduleName() + QLatin1String("_IDX_COUNT"),
+ getMaxTypeIndex() + smartPointerCount);
+ macrosStream << "\n};\n";
+
+ macrosStream << "// This variable stores all Python types exported by this module.\n";
+ macrosStream << "extern PyTypeObject **" << cppApiVariableName() << ";\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().
+ macrosStream << "// Converter indices\nenum : int {\n";
+ const PrimitiveTypeEntryList &primitives = primitiveTypes();
+ int pCount = 0;
+ for (const PrimitiveTypeEntry *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())
+ continue;
+
+ _writeTypeIndexValueLine(macrosStream, getTypeIndexVariableName(ptype), pCount++);
+ }
+
+ const QVector<AbstractMetaType> &containers = instantiatedContainers();
+ for (const AbstractMetaType &container : containers) {
+ _writeTypeIndexValue(macrosStream, getTypeIndexVariableName(container), pCount);
+ macrosStream << ", // " << container.cppSignature() << Qt::endl;
+ pCount++;
+ }
+
+ // Because on win32 the compiler will not accept a zero length array.
+ if (pCount == 0)
+ pCount++;
+ _writeTypeIndexValue(macrosStream, QStringLiteral("SBK_%1_CONVERTERS_IDX_COUNT")
+ .arg(moduleName()), pCount);
+ macrosStream << "\n};\n";
+
+ formatTypeDefEntries(macrosStream);
+
+ // TODO-CONVERTER ------------------------------------------------------------------------------
+
+ macrosStream << "// Macros for type check\n";
+
+ if (usePySideExtensions()) {
+ typeFunctions << "QT_WARNING_PUSH\n";
+ typeFunctions << "QT_WARNING_DISABLE_DEPRECATED\n";
+ }
+ for (const AbstractMetaEnum *cppEnum : globalEnums()) {
+ if (!cppEnum->isAnonymous()) {
+ includes << cppEnum->typeEntry()->include();
+ writeSbkTypeFunction(typeFunctions, cppEnum);
+ }
+ }
+
+ for (AbstractMetaClass *metaClass : classList) {
+ if (!shouldGenerate(metaClass))
+ continue;
+
+ //Includes
+ const TypeEntry *classType = metaClass->typeEntry();
+ includes << classType->include();
+
+ for (const AbstractMetaEnum *cppEnum : metaClass->enums()) {
+ if (cppEnum->isAnonymous() || cppEnum->isPrivate())
+ continue;
+ EnumTypeEntry *enumType = cppEnum->typeEntry();
+ includes << enumType->include();
+ writeProtectedEnumSurrogate(protEnumsSurrogates, cppEnum);
+ writeSbkTypeFunction(typeFunctions, cppEnum);
+ }
+
+ if (!metaClass->isNamespace())
+ writeSbkTypeFunction(typeFunctions, metaClass);
+ }
+
+ for (const AbstractMetaType &metaType : instantiatedSmartPtrs) {
+ const TypeEntry *classType = metaType.typeEntry();
+ includes << classType->include();
+ writeSbkTypeFunction(typeFunctions, metaType);
+ }
+ if (usePySideExtensions())
+ typeFunctions << "QT_WARNING_POP\n";
+
+ QString moduleHeaderFileName(outputDirectory()
+ + QDir::separator() + subDirectoryForPackage(packageName())
+ + QDir::separator() + getModuleHeaderFileName());
+
+ QString includeShield(QLatin1String("SBK_") + moduleName().toUpper() + QLatin1String("_PYTHON_H"));
+
+ FileOut file(moduleHeaderFileName);
+ QTextStream &s = file.stream;
+ // write license comment
+ s << licenseComment() << Qt::endl << Qt::endl;
+
+ s << "#ifndef " << includeShield << Qt::endl;
+ s << "#define " << includeShield << Qt::endl << Qt::endl;
+ if (!avoidProtectedHack()) {
+ s << "//workaround to access protected functions\n";
+ s << "#define protected public\n\n";
+ }
+
+ s << "#include <sbkpython.h>\n";
+ s << "#include <sbkconverter.h>\n";
+
+ QStringList requiredTargetImports = TypeDatabase::instance()->requiredTargetImports();
+ if (!requiredTargetImports.isEmpty()) {
+ s << "// Module Includes\n";
+ for (const QString &requiredModule : qAsConst(requiredTargetImports))
+ s << "#include <" << getModuleHeaderFileName(requiredModule) << ">\n";
+ s << Qt::endl;
+ }
+
+ s << "// Bound library includes\n";
+ for (const Include &include : qAsConst(includes))
+ s << include;
+
+ if (!primitiveTypes().isEmpty()) {
+ s << "// Conversion Includes - Primitive Types\n";
+ const PrimitiveTypeEntryList &primitiveTypeList = primitiveTypes();
+ for (const PrimitiveTypeEntry *ptype : primitiveTypeList)
+ s << ptype->include();
+ s << Qt::endl;
+ }
+
+ if (!containerTypes().isEmpty()) {
+ s << "// Conversion Includes - Container Types\n";
+ const ContainerTypeEntryList &containerTypeList = containerTypes();
+ for (const ContainerTypeEntry *ctype : containerTypeList)
+ s << ctype->include();
+ s << Qt::endl;
+ }
+
+ s << macros << Qt::endl;
+
+ if (!protectedEnumSurrogates.isEmpty()) {
+ s << "// Protected enum surrogates\n";
+ s << protectedEnumSurrogates << Qt::endl;
+ }
+
+ s << "namespace Shiboken\n{\n\n";
+
+ s << "// PyType functions, to get the PyObjectType for a type T\n";
+ s << sbkTypeFunctions << Qt::endl;
+
+ s << "} // namespace Shiboken\n\n";
+
+ s << "#endif // " << includeShield << Qt::endl << Qt::endl;
+
+ return file.done() != FileOut::Failure;
+}
+
+void HeaderGenerator::writeProtectedEnumSurrogate(QTextStream &s, const AbstractMetaEnum *cppEnum)
+{
+ if (avoidProtectedHack() && cppEnum->isProtected())
+ s << "enum " << protectedEnumSurrogateName(cppEnum) << " {};\n";
+}
+
+void HeaderGenerator::writeSbkTypeFunction(QTextStream &s, const AbstractMetaEnum *cppEnum)
+{
+ QString enumName;
+ if (avoidProtectedHack() && cppEnum->isProtected()) {
+ enumName = protectedEnumSurrogateName(cppEnum);
+ } else {
+ enumName = cppEnum->name();
+ if (cppEnum->enclosingClass())
+ enumName = cppEnum->enclosingClass()->qualifiedCppName() + QLatin1String("::") + enumName;
+ }
+
+ s << "template<> inline PyTypeObject *SbkType< ::" << enumName << " >() ";
+ s << "{ return " << cpythonTypeNameExt(cppEnum->typeEntry()) << "; }\n";
+
+ FlagsTypeEntry *flag = cppEnum->typeEntry()->flags();
+ if (flag) {
+ s << "template<> inline PyTypeObject *SbkType< ::" << flag->name() << " >() "
+ << "{ return " << cpythonTypeNameExt(flag) << "; }\n";
+ }
+}
+
+void HeaderGenerator::writeSbkTypeFunction(QTextStream &s, const AbstractMetaClass *cppClass)
+{
+ s << "template<> inline PyTypeObject *SbkType< ::" << cppClass->qualifiedCppName() << " >() "
+ << "{ return reinterpret_cast<PyTypeObject *>(" << cpythonTypeNameExt(cppClass->typeEntry()) << "); }\n";
+}
+
+void HeaderGenerator::writeSbkTypeFunction(QTextStream &s, const AbstractMetaType &metaType)
+{
+ s << "template<> inline PyTypeObject *SbkType< ::" << metaType.cppSignature() << " >() "
+ << "{ return reinterpret_cast<PyTypeObject *>(" << cpythonTypeNameExt(metaType) << "); }\n";
+}
+
+void HeaderGenerator::writeInheritedOverloads(QTextStream &s)
+{
+ for (const AbstractMetaFunction *func : qAsConst(m_inheritedOverloads)) {
+ s << INDENT << "inline ";
+ s << functionSignature(func, QString(), QString(), Generator::EnumAsInts|Generator::OriginalTypeDescription)
+ << " { ";
+ if (!func->isVoid())
+ s << "return ";
+ s << func->ownerClass()->qualifiedCppName() << "::" << func->originalName() << '(';
+ QStringList args;
+ const AbstractMetaArgumentList &arguments = func->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+ QString argName = arg.name();
+ const TypeEntry *enumTypeEntry = nullptr;
+ if (arg.type().isFlags())
+ enumTypeEntry = static_cast<const FlagsTypeEntry *>(arg.type().typeEntry())->originator();
+ else if (arg.type().isEnum())
+ enumTypeEntry = arg.type().typeEntry();
+ if (enumTypeEntry)
+ argName = arg.type().cppSignature() + QLatin1Char('(') + argName + QLatin1Char(')');
+ args << argName;
+ }
+ s << args.join(QLatin1String(", ")) << ')';
+ s << "; }\n";
+ }
+}
diff --git a/sources/shiboken6/generator/shiboken/headergenerator.h b/sources/shiboken6/generator/shiboken/headergenerator.h
new file mode 100644
index 000000000..d02516b8d
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/headergenerator.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt for Python.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef HEADERGENERATOR_H
+#define HEADERGENERATOR_H
+
+#include "shibokengenerator.h"
+
+#include <QtCore/QSet>
+
+class AbstractMetaFunction;
+
+/**
+ * The HeaderGenerator generate the declarations of C++ bindings classes.
+ */
+class HeaderGenerator : public ShibokenGenerator
+{
+public:
+ OptionDescriptions options() const override { return OptionDescriptions(); }
+
+ const char *name() const override { return "Header generator"; }
+
+protected:
+ QString fileNameSuffix() const override;
+ QString fileNameForContext(const GeneratorContext &context) const override;
+ void generateClass(QTextStream &s, const GeneratorContext &classContext) override;
+ bool finishGeneration() override;
+
+private:
+ void writeCopyCtor(QTextStream &s, const AbstractMetaClass *metaClass) const;
+ void writeProtectedFieldAccessors(QTextStream &s, const AbstractMetaField *field) const;
+ void writeFunction(QTextStream &s, const AbstractMetaFunction *func);
+ void writeSbkTypeFunction(QTextStream &s, const AbstractMetaEnum *cppEnum);
+ void writeSbkTypeFunction(QTextStream &s, const AbstractMetaClass *cppClass);
+ void writeSbkTypeFunction(QTextStream &s, const AbstractMetaType &metaType);
+ void writeTypeIndexValueLine(QTextStream &s, const TypeEntry *typeEntry);
+ void writeTypeIndexValueLines(QTextStream &s, const AbstractMetaClass *metaClass);
+ void writeProtectedEnumSurrogate(QTextStream &s, const AbstractMetaEnum *cppEnum);
+ void writeInheritedOverloads(QTextStream &s);
+
+ QSet<const AbstractMetaFunction *> m_inheritedOverloads;
+};
+
+#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..53fff9f25
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/overloaddata.cpp
@@ -0,0 +1,1093 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt for Python.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <abstractmetalang.h>
+#include <reporthandler.h>
+#include <graph.h>
+#include "overloaddata.h"
+#include "ctypenames.h"
+#include "indentor.h"
+#include "shibokengenerator.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QTemporaryFile>
+
+#include <algorithm>
+
+static const TypeEntry *getReferencedTypeEntry(const TypeEntry *typeEntry)
+{
+ if (typeEntry->isPrimitive()) {
+ auto pte = dynamic_cast<const PrimitiveTypeEntry *>(typeEntry);
+ while (pte->referencedTypeEntry())
+ pte = pte->referencedTypeEntry();
+ typeEntry = pte;
+ }
+ return typeEntry;
+}
+
+static QString getTypeName(const AbstractMetaType &type)
+{
+ const TypeEntry *typeEntry = getReferencedTypeEntry(type.typeEntry());
+ QString typeName = typeEntry->name();
+ if (typeEntry->isContainer()) {
+ QStringList types;
+ for (const auto &cType : type.instantiations()) {
+ const TypeEntry *typeEntry = getReferencedTypeEntry(cType.typeEntry());
+ types << typeEntry->name();
+ }
+ typeName += QLatin1Char('<') + types.join(QLatin1Char(',')) + QLatin1String(" >");
+ }
+ return typeName;
+}
+
+static QString getTypeName(const OverloadData *ov)
+{
+ return ov->hasArgumentTypeReplace() ? ov->argumentTypeReplaced() : getTypeName(ov->argType());
+}
+
+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 (int i = 0; i < typeA.instantiations().size(); ++i) {
+ if (!typesAreEqual(typeA.instantiations().at(i), typeB.instantiations().at(i)))
+ return false;
+ }
+ return true;
+ }
+
+ return !(ShibokenGenerator::isCString(typeA) ^ ShibokenGenerator::isCString(typeB));
+ }
+ return false;
+}
+
+
+/**
+ * OverloadSortData just helps writing clearer code in the
+ * OverloadData::sortNextOverloads method.
+ */
+struct OverloadSortData
+{
+ /**
+ * Adds a typeName into the type map without associating it with
+ * a OverloadData. This is done to express type dependencies that could
+ * or could not appear in overloaded signatures not processed yet.
+ */
+ void mapType(const QString &typeName)
+ {
+ if (map.contains(typeName))
+ return;
+ map[typeName] = counter;
+ if (!reverseMap.contains(counter))
+ reverseMap[counter] = nullptr;
+ counter++;
+ }
+
+ void mapType(OverloadData *overloadData)
+ {
+ QString typeName = getTypeName(overloadData);
+ map[typeName] = counter;
+ reverseMap[counter] = overloadData;
+ counter++;
+ }
+
+ int lastProcessedItemId() { return counter - 1; }
+
+ int counter = 0;
+ QHash<QString, int> map; // typeName -> id
+ QHash<int, OverloadData *> reverseMap; // id -> OverloadData;
+};
+
+/**
+ * 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 AbstractMetaFunction *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() + QLatin1Char('<')
+ + types.join(QLatin1String(", ")) + QLatin1String(" >");
+}
+
+// overloaddata.cpp
+static QString msgCyclicDependency(const QString &funcName, const QString &graphName,
+ const AbstractMetaFunctionCList &cyclic,
+ const OverloadData::MetaFunctionList &involvedConversions)
+{
+ QString result;
+ QTextStream str(&result);
+ str << "Cyclic dependency found on overloaddata for \"" << funcName
+ << "\" method! The graph boy saved the graph at \"" << QDir::toNativeSeparators(graphName)
+ << "\". Cyclic functions:";
+ for (auto c : cyclic)
+ str << ' ' << c->signature();
+ if (const int count = involvedConversions.size()) {
+ str << " Implicit conversions (" << count << "): ";
+ for (int i = 0; i < count; ++i) {
+ if (i)
+ str << ", \"";
+ str << involvedConversions.at(i)->signature() << '"';
+ if (const AbstractMetaClass *c = involvedConversions.at(i)->implementingClass())
+ str << '(' << c->name() << ')';
+ }
+ }
+ return result;
+}
+
+static inline int overloadNumber(const OverloadData *o)
+{
+ return o->referenceFunction()->overloadNumber();
+}
+
+bool OverloadData::sortByOverloadNumberModification()
+{
+ if (std::all_of(m_nextOverloadData.cbegin(), m_nextOverloadData.cend(),
+ [](const OverloadData *o) { return overloadNumber(o) == TypeSystem::OverloadNumberDefault; })) {
+ return false;
+ }
+ std::stable_sort(m_nextOverloadData.begin(), m_nextOverloadData.end(),
+ [] (const OverloadData *o1, const OverloadData *o2) {
+ return overloadNumber(o1) < overloadNumber(o2);
+ });
+ return true;
+}
+
+/**
+ * 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 OverloadData::sortNextOverloads()
+{
+ OverloadSortData sortData;
+ bool checkPyObject = false;
+ int pyobjectIndex = 0;
+ bool checkPySequence = false;
+ int pySeqIndex = 0;
+ bool checkQString = false;
+ int qstringIndex = 0;
+ bool checkQVariant = false;
+ int qvariantIndex = 0;
+ bool checkPyBuffer = false;
+ int pyBufferIndex = 0;
+
+ // 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 (OverloadData *ov : qAsConst(m_nextOverloadData))
+ ov->sortNextOverloads();
+
+ if (m_nextOverloadData.size() <= 1 || sortByOverloadNumberModification())
+ 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.
+ for (OverloadData *ov : qAsConst(m_nextOverloadData)) {
+ sortData.mapType(ov);
+
+ const QString typeName(getTypeName(ov));
+
+ if (!checkPyObject && typeName.contains(QLatin1String("PyObject"))) {
+ checkPyObject = true;
+ pyobjectIndex = sortData.lastProcessedItemId();
+ } else if (!checkPySequence && typeName == QLatin1String("PySequence")) {
+ checkPySequence = true;
+ pySeqIndex = sortData.lastProcessedItemId();
+ } else if (!checkPyBuffer && typeName == QLatin1String("PyBuffer")) {
+ checkPyBuffer = true;
+ pyBufferIndex = sortData.lastProcessedItemId();
+ } else if (!checkQVariant && typeName == qVariantT()) {
+ checkQVariant = true;
+ qvariantIndex = sortData.lastProcessedItemId();
+ } else if (!checkQString && typeName == qStringT()) {
+ checkQString = true;
+ qstringIndex = sortData.lastProcessedItemId();
+ }
+
+ for (const auto &instantiation : ov->argType().instantiations()) {
+ // Add dependencies for type instantiation of container.
+ QString typeName = getTypeName(instantiation);
+ sortData.mapType(typeName);
+
+ // 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 : qAsConst(nonIntegerPrimitives))
+ sortData.mapType(getImplicitConversionTypeName(ov->argType(), instantiation, nullptr, primitive));
+ } else {
+ const AbstractMetaFunctionList &funcs = m_generator->implicitConversions(instantiation);
+ for (const AbstractMetaFunction *function : funcs)
+ sortData.mapType(getImplicitConversionTypeName(ov->argType(), instantiation, function));
+ }
+ }
+ }
+
+
+ // Create the graph of type dependencies based on implicit conversions.
+ Graph graph(sortData.reverseMap.count());
+ // 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()};
+
+ QList<int> foundPrimitiveTypeIds;
+ for (const auto &p : primitiveTypes) {
+ const auto it = sortData.map.constFind(p);
+ if (it != sortData.map.cend())
+ foundPrimitiveTypeIds.append(it.value());
+ }
+
+ if (checkPySequence && checkPyObject)
+ graph.addEdge(pySeqIndex, pyobjectIndex);
+
+ QStringList classesWithIntegerImplicitConversion;
+
+ MetaFunctionList involvedConversions;
+
+ for (OverloadData *ov : qAsConst(m_nextOverloadData)) {
+ const AbstractMetaType &targetType = ov->argType();
+ const QString targetTypeEntryName(getTypeName(ov));
+ int targetTypeId = sortData.map[targetTypeEntryName];
+
+ // Process implicit conversions
+ const AbstractMetaFunctionList &functions = m_generator->implicitConversions(targetType);
+ for (AbstractMetaFunction *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 (!sortData.map.contains(convertibleType))
+ continue;
+
+ int convertibleTypeId = sortData.map[convertibleType];
+
+ // If a reverse pair already exists, remove it. Probably due to the
+ // container check (This happened to QVariant and QHash)
+ graph.removeEdge(targetTypeId, convertibleTypeId);
+ graph.addEdge(convertibleTypeId, targetTypeId);
+ involvedConversions.append(function);
+ }
+
+ // Process inheritance relationships
+ if (targetType.isValue() || targetType.isObject()) {
+ const AbstractMetaClass *metaClass = AbstractMetaClass::findClass(m_generator->classes(), targetType.typeEntry());
+ const AbstractMetaClassList &ancestors = m_generator->getAllAncestors(metaClass);
+ for (const AbstractMetaClass *ancestor : ancestors) {
+ QString ancestorTypeName = ancestor->typeEntry()->name();
+ if (!sortData.map.contains(ancestorTypeName))
+ continue;
+ int ancestorTypeId = sortData.map[ancestorTypeName];
+ graph.removeEdge(ancestorTypeId, targetTypeId);
+ graph.addEdge(targetTypeId, ancestorTypeId);
+ }
+ }
+
+ // Process template instantiations
+ for (const auto &instantiation : targetType.instantiations()) {
+ if (sortData.map.contains(getTypeName(instantiation))) {
+ int convertible = sortData.map[getTypeName(instantiation)];
+
+ if (!graph.containsEdge(targetTypeId, convertible)) // Avoid cyclic dependency.
+ graph.addEdge(convertible, targetTypeId);
+
+ if (instantiation.isPrimitive() && (signedIntegerPrimitives.contains(instantiation.name()))) {
+ for (const QString &primitive : qAsConst(nonIntegerPrimitives)) {
+ QString convertibleTypeName = getImplicitConversionTypeName(ov->argType(), instantiation, nullptr, primitive);
+ if (!graph.containsEdge(targetTypeId, sortData.map[convertibleTypeName])) // Avoid cyclic dependency.
+ graph.addEdge(sortData.map[convertibleTypeName], targetTypeId);
+ }
+
+ } else {
+ const AbstractMetaFunctionList &funcs = m_generator->implicitConversions(instantiation);
+ for (const AbstractMetaFunction *function : funcs) {
+ QString convertibleTypeName = getImplicitConversionTypeName(ov->argType(), instantiation, function);
+ if (!graph.containsEdge(targetTypeId, sortData.map[convertibleTypeName])) { // Avoid cyclic dependency.
+ graph.addEdge(sortData.map[convertibleTypeName], targetTypeId);
+ involvedConversions.append(function);
+ }
+ }
+ }
+ }
+ }
+
+
+ if ((checkPySequence || checkPyObject || checkPyBuffer)
+ && !targetTypeEntryName.contains(QLatin1String("PyObject"))
+ && !targetTypeEntryName.contains(QLatin1String("PyBuffer"))
+ && !targetTypeEntryName.contains(QLatin1String("PySequence"))) {
+ if (checkPySequence) {
+ // PySequence will be checked after all more specific types, but before PyObject.
+ graph.addEdge(targetTypeId, pySeqIndex);
+ } else if (checkPyBuffer) {
+ // PySequence will be checked after all more specific types, but before PyObject.
+ graph.addEdge(targetTypeId, pyBufferIndex);
+ } else {
+ // Add dependency on PyObject, so its check is the last one (too generic).
+ graph.addEdge(targetTypeId, pyobjectIndex);
+ }
+ } else if (checkQVariant && targetTypeEntryName != qVariantT()) {
+ if (!graph.containsEdge(qvariantIndex, targetTypeId)) // Avoid cyclic dependency.
+ graph.addEdge(targetTypeId, qvariantIndex);
+ } else if (checkQString && ShibokenGenerator::isPointer(ov->argType())
+ && targetTypeEntryName != qStringT()
+ && targetTypeEntryName != qByteArrayT()
+ && (!checkPyObject || targetTypeId != pyobjectIndex)) {
+ if (!graph.containsEdge(qstringIndex, targetTypeId)) // Avoid cyclic dependency.
+ graph.addEdge(targetTypeId, qstringIndex);
+ }
+
+ if (targetType.isEnum()) {
+ // Enum values must precede primitive types.
+ for (auto id : foundPrimitiveTypeIds)
+ graph.addEdge(targetTypeId, id);
+ }
+ }
+
+ // QByteArray args need to be checked after QString args
+ if (sortData.map.contains(qStringT()) && sortData.map.contains(qByteArrayT()))
+ graph.addEdge(sortData.map.value(qStringT()), sortData.map.value(qByteArrayT()));
+
+ for (OverloadData *ov : qAsConst(m_nextOverloadData)) {
+ 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 : qAsConst(classesWithIntegerImplicitConversion))
+ graph.addEdge(sortData.map[targetTypeEntryName], sortData.map[implicitFromInt]);
+ }
+
+
+ // Special case for double(int i) (not tracked by m_generator->implicitConversions
+ for (const QString &signedIntegerName : qAsConst(signedIntegerPrimitives)) {
+ if (sortData.map.contains(signedIntegerName)) {
+ for (const QString &nonIntegerName : qAsConst(nonIntegerPrimitives)) {
+ if (sortData.map.contains(nonIntegerName))
+ graph.addEdge(sortData.map[nonIntegerName], sortData.map[signedIntegerName]);
+ }
+ }
+ }
+
+ // sort the overloads topologically based on the dependency graph.
+ const auto unmappedResult = graph.topologicalSort();
+ if (!unmappedResult.isValid()) {
+ QString funcName = referenceFunction()->name();
+ if (referenceFunction()->ownerClass())
+ funcName.prepend(referenceFunction()->ownerClass()->name() + QLatin1Char('.'));
+
+ // Dump overload graph
+ QString graphName = QDir::tempPath() + QLatin1Char('/') + funcName + QLatin1String(".dot");
+ QHash<int, QString> nodeNames;
+ for (auto it = sortData.map.cbegin(), end = sortData.map.cend(); it != end; ++it)
+ nodeNames.insert(it.value(), it.key());
+ graph.dumpDot(nodeNames, graphName);
+ AbstractMetaFunctionCList cyclic;
+ for (int c : unmappedResult.cyclic)
+ cyclic.append(sortData.reverseMap.value(c)->referenceFunction());
+ qCWarning(lcShiboken, "%s", qPrintable(msgCyclicDependency(funcName, graphName, cyclic, involvedConversions)));
+ }
+
+ m_nextOverloadData.clear();
+ for (int i : unmappedResult.result) {
+ if (!sortData.reverseMap[i])
+ continue;
+ m_nextOverloadData << sortData.reverseMap[i];
+ }
+}
+
+/**
+ * Root constructor for OverloadData
+ *
+ * This constructor receives the list of overloads for a given function and iterates generating
+ * the graph of OverloadData instances. Each OverloadData 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 AbstractMetaFunctionList &overloads, const ShibokenGenerator *generator)
+ : m_minArgs(256), m_maxArgs(0), m_argPos(-1), m_argType(nullptr),
+ m_headOverloadData(this), m_previousOverloadData(nullptr), m_generator(generator)
+{
+ for (const AbstractMetaFunction *func : overloads) {
+ m_overloads.append(func);
+ int argSize = func->arguments().size() - numberOfRemovedArguments(func);
+ if (m_minArgs > argSize)
+ m_minArgs = argSize;
+ else if (m_maxArgs < argSize)
+ m_maxArgs = argSize;
+ OverloadData *currentOverloadData = this;
+ const AbstractMetaArgumentList &arguments = func->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+ if (func->argumentRemoved(arg.argumentIndex() + 1))
+ continue;
+ currentOverloadData = currentOverloadData->addOverloadData(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();
+
+ // Fix minArgs
+ if (minArgs() > maxArgs())
+ m_headOverloadData->m_minArgs = maxArgs();
+}
+
+OverloadData::OverloadData(OverloadData *headOverloadData, const AbstractMetaFunction *func,
+ const AbstractMetaType &argType, int argPos)
+ : m_minArgs(256), m_maxArgs(0), m_argPos(argPos), m_argType(argType),
+ m_headOverloadData(headOverloadData), m_previousOverloadData(nullptr),
+ m_generator(nullptr)
+{
+ if (func)
+ this->addOverload(func);
+}
+
+void OverloadData::addOverload(const AbstractMetaFunction *func)
+{
+ int origNumArgs = func->arguments().size();
+ int removed = numberOfRemovedArguments(func);
+ int numArgs = origNumArgs - removed;
+
+ if (numArgs > m_headOverloadData->m_maxArgs)
+ m_headOverloadData->m_maxArgs = numArgs;
+
+ if (numArgs < m_headOverloadData->m_minArgs)
+ m_headOverloadData->m_minArgs = numArgs;
+
+ for (int i = 0; m_headOverloadData->m_minArgs > 0 && i < origNumArgs; i++) {
+ if (func->argumentRemoved(i + 1))
+ continue;
+ if (func->arguments().at(i).hasDefaultValueExpression()) {
+ int fixedArgIndex = i - removed;
+ if (fixedArgIndex < m_headOverloadData->m_minArgs)
+ m_headOverloadData->m_minArgs = fixedArgIndex;
+ }
+ }
+
+ m_overloads.append(func);
+}
+
+OverloadData *OverloadData::addOverloadData(const AbstractMetaFunction *func,
+ const AbstractMetaArgument &arg)
+{
+ const AbstractMetaType &argType = arg.type();
+ OverloadData *overloadData = nullptr;
+ if (!func->isOperatorOverload()) {
+ for (OverloadData *tmp : qAsConst(m_nextOverloadData)) {
+ // 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.
+ QString replacedArg = func->typeReplaced(tmp->m_argPos + 1);
+ bool argsReplaced = !replacedArg.isEmpty() || !tmp->m_argTypeReplaced.isEmpty();
+ if ((!argsReplaced && typesAreEqual(tmp->m_argType, argType))
+ || (argsReplaced && replacedArg == tmp->argumentTypeReplaced())) {
+ tmp->addOverload(func);
+ overloadData = tmp;
+ }
+ }
+ }
+
+ if (!overloadData) {
+ overloadData = new OverloadData(m_headOverloadData, func, argType, m_argPos + 1);
+ overloadData->m_previousOverloadData = this;
+ overloadData->m_generator = this->m_generator;
+ QString typeReplaced = func->typeReplaced(arg.argumentIndex() + 1);
+
+ if (!typeReplaced.isEmpty())
+ overloadData->m_argTypeReplaced = typeReplaced;
+ m_nextOverloadData.append(overloadData);
+ }
+
+ return overloadData;
+}
+
+QStringList OverloadData::returnTypes() const
+{
+ QSet<QString> retTypes;
+ for (const AbstractMetaFunction *func : m_overloads) {
+ if (!func->typeReplaced(0).isEmpty())
+ retTypes << func->typeReplaced(0);
+ else if (!func->argumentRemoved(0))
+ retTypes << func->type().cppSignature();
+ }
+ return retTypes.values();
+}
+
+bool OverloadData::hasNonVoidReturnType() const
+{
+ QStringList retTypes = returnTypes();
+ return !retTypes.contains(QLatin1String("void")) || retTypes.size() > 1;
+}
+
+bool OverloadData::hasVarargs() const
+{
+ for (const AbstractMetaFunction *func : m_overloads) {
+ AbstractMetaArgumentList args = func->arguments();
+ if (args.size() > 1 && args.constLast().type().isVarargs())
+ return true;
+ }
+ return false;
+}
+
+bool OverloadData::hasAllowThread() const
+{
+ for (const AbstractMetaFunction *func : m_overloads) {
+ if (func->allowThread())
+ return true;
+ }
+ return false;
+}
+
+bool OverloadData::hasStaticFunction(const AbstractMetaFunctionList &overloads)
+{
+ for (const AbstractMetaFunction *func : qAsConst(overloads)) {
+ if (func->isStatic())
+ return true;
+ }
+ return false;
+}
+
+bool OverloadData::hasStaticFunction() const
+{
+ for (const AbstractMetaFunction *func : m_overloads) {
+ if (func->isStatic())
+ return true;
+ }
+ return false;
+}
+
+bool OverloadData::hasInstanceFunction(const AbstractMetaFunctionList &overloads)
+{
+ for (const AbstractMetaFunction *func : qAsConst(overloads)) {
+ if (!func->isStatic())
+ return true;
+ }
+ return false;
+}
+
+bool OverloadData::hasInstanceFunction() const
+{
+ for (const AbstractMetaFunction *func : m_overloads) {
+ if (!func->isStatic())
+ return true;
+ }
+ return false;
+}
+
+bool OverloadData::hasStaticAndInstanceFunctions(const AbstractMetaFunctionList &overloads)
+{
+ return OverloadData::hasStaticFunction(overloads) && OverloadData::hasInstanceFunction(overloads);
+}
+
+bool OverloadData::hasStaticAndInstanceFunctions() const
+{
+ return OverloadData::hasStaticFunction() && OverloadData::hasInstanceFunction();
+}
+
+const AbstractMetaFunction *OverloadData::referenceFunction() const
+{
+ return m_overloads.constFirst();
+}
+
+const AbstractMetaArgument *OverloadData::argument(const AbstractMetaFunction *func) const
+{
+ if (isHeadOverloadData() || !m_overloads.contains(func))
+ return nullptr;
+
+ int argPos = 0;
+ int removed = 0;
+ for (int i = 0; argPos <= m_argPos; i++) {
+ if (func->argumentRemoved(i + 1))
+ removed++;
+ else
+ argPos++;
+ }
+
+ return &func->arguments().at(m_argPos + removed);
+}
+
+OverloadDataList OverloadData::overloadDataOnPosition(OverloadData *overloadData, int argPos) const
+{
+ OverloadDataList overloadDataList;
+ if (overloadData->argPos() == argPos) {
+ overloadDataList.append(overloadData);
+ } else if (overloadData->argPos() < argPos) {
+ const OverloadDataList &data = overloadData->nextOverloadData();
+ for (OverloadData *pd : data)
+ overloadDataList += overloadDataOnPosition(pd, argPos);
+ }
+ return overloadDataList;
+}
+
+OverloadDataList OverloadData::overloadDataOnPosition(int argPos) const
+{
+ OverloadDataList overloadDataList;
+ overloadDataList += overloadDataOnPosition(m_headOverloadData, argPos);
+ return overloadDataList;
+}
+
+bool OverloadData::nextArgumentHasDefaultValue() const
+{
+ for (OverloadData *overloadData : m_nextOverloadData) {
+ if (overloadData->getFunctionWithDefaultValue())
+ return true;
+ }
+ return false;
+}
+
+static OverloadData *_findNextArgWithDefault(OverloadData *overloadData)
+{
+ if (overloadData->getFunctionWithDefaultValue())
+ return overloadData;
+
+ OverloadData *result = nullptr;
+ const OverloadDataList &data = overloadData->nextOverloadData();
+ for (OverloadData *odata : data) {
+ OverloadData *tmp = _findNextArgWithDefault(odata);
+ if (!result || (tmp && result->argPos() > tmp->argPos()))
+ result = tmp;
+ }
+ return result;
+}
+
+OverloadData *OverloadData::findNextArgWithDefault()
+{
+ return _findNextArgWithDefault(this);
+}
+
+bool OverloadData::isFinalOccurrence(const AbstractMetaFunction *func) const
+{
+ for (const OverloadData *pd : m_nextOverloadData) {
+ if (pd->overloads().contains(func))
+ return false;
+ }
+ return true;
+}
+
+OverloadData::MetaFunctionList OverloadData::overloadsWithoutRepetition() const
+{
+ MetaFunctionList overloads = m_overloads;
+ for (const AbstractMetaFunction *func : m_overloads) {
+ if (func->minimalSignature().endsWith(QLatin1String("const")))
+ continue;
+ for (const AbstractMetaFunction *f : qAsConst(overloads)) {
+ if ((func->minimalSignature() + QLatin1String("const")) == f->minimalSignature()) {
+ overloads.removeOne(f);
+ break;
+ }
+ }
+ }
+ return overloads;
+}
+
+const AbstractMetaFunction *OverloadData::getFunctionWithDefaultValue() const
+{
+ for (const AbstractMetaFunction *func : m_overloads) {
+ int removedArgs = 0;
+ for (int i = 0; i <= m_argPos + removedArgs; i++) {
+ if (func->argumentRemoved(i + 1))
+ removedArgs++;
+ }
+ if (func->arguments().at(m_argPos + removedArgs).hasDefaultValueExpression())
+ return func;
+ }
+ return nullptr;
+}
+
+QVector<int> OverloadData::invalidArgumentLengths() const
+{
+ QSet<int> validArgLengths;
+
+ for (const AbstractMetaFunction *func : qAsConst(m_headOverloadData->m_overloads)) {
+ const AbstractMetaArgumentList args = func->arguments();
+ int offset = 0;
+ for (int i = 0; i < args.size(); ++i) {
+ if (func->argumentRemoved(i+1)) {
+ offset++;
+ } else {
+ if (args.at(i).hasDefaultValueExpression())
+ validArgLengths << i-offset;
+ }
+ }
+ validArgLengths << args.size() - offset;
+ }
+
+ QVector<int> invalidArgLengths;
+ for (int i = minArgs() + 1; i < maxArgs(); i++) {
+ if (!validArgLengths.contains(i))
+ invalidArgLengths.append(i);
+ }
+
+ return invalidArgLengths;
+}
+
+int OverloadData::numberOfRemovedArguments(const AbstractMetaFunction *func, int finalArgPos)
+{
+ int removed = 0;
+ if (finalArgPos < 0) {
+ for (int i = 0; i < func->arguments().size(); i++) {
+ if (func->argumentRemoved(i + 1))
+ removed++;
+ }
+ } else {
+ for (int i = 0; i < finalArgPos + removed; i++) {
+ if (func->argumentRemoved(i + 1))
+ removed++;
+ }
+ }
+ return removed;
+}
+
+QPair<int, int> OverloadData::getMinMaxArguments(const AbstractMetaFunctionList &overloads)
+{
+ int minArgs = 10000;
+ int maxArgs = 0;
+ for (const AbstractMetaFunction *func : overloads) {
+ int origNumArgs = func->arguments().size();
+ int removed = numberOfRemovedArguments(func);
+ int numArgs = origNumArgs - removed;
+ if (maxArgs < numArgs)
+ maxArgs = numArgs;
+ if (minArgs > numArgs)
+ minArgs = numArgs;
+ for (int j = 0; j < origNumArgs; j++) {
+ if (func->argumentRemoved(j + 1))
+ continue;
+ int fixedArgIndex = j - removed;
+ if (fixedArgIndex < minArgs && func->arguments().at(j).hasDefaultValueExpression())
+ minArgs = fixedArgIndex;
+ }
+ }
+ return {minArgs, maxArgs};
+}
+
+bool OverloadData::isSingleArgument(const AbstractMetaFunctionList &overloads)
+{
+ bool singleArgument = true;
+ for (const AbstractMetaFunction *func : overloads) {
+ if (func->arguments().size() - numberOfRemovedArguments(func) != 1) {
+ singleArgument = false;
+ break;
+ }
+ }
+ return singleArgument;
+}
+
+void OverloadData::dumpGraph(const QString &filename) const
+{
+ QFile file(filename);
+ if (file.open(QFile::WriteOnly)) {
+ QTextStream s(&file);
+ s << m_headOverloadData->dumpGraph();
+ }
+}
+
+static inline QString toHtml(QString s)
+{
+ s.replace(QLatin1Char('<'), QLatin1String("&lt;"));
+ s.replace(QLatin1Char('>'), QLatin1String("&gt;"));
+ s.replace(QLatin1Char('&'), QLatin1String("&amp;"));
+ return s;
+}
+
+QString OverloadData::dumpGraph() const
+{
+ Indentor INDENT;
+ Indentation indent(INDENT);
+ QString result;
+ QTextStream s(&result);
+ if (m_argPos == -1) {
+ const AbstractMetaFunction *rfunc = referenceFunction();
+ s << "digraph OverloadedFunction {\n";
+ s << INDENT << "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 AbstractMetaFunction *func : m_overloads) {
+ s << "f" << functionNumber(func) << " : "
+ << toHtml(func->type().cppSignature())
+ << ' ' << toHtml(func->minimalSignature()) << "\\l";
+ }
+ s << "\"];\n";
+
+ // Function box title
+ s << INDENT << '"' << 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 AbstractMetaFunction *func : m_overloads) {
+ if (func->typeReplaced(0).isEmpty())
+ continue;
+ s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << functionNumber(func);
+ s << "-type</td><td bgcolor=\"gray\" align=\"left\">";
+ s << toHtml(func->typeReplaced(0)) << "</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 AbstractMetaFunction *func : m_overloads)
+ s << 'f' << functionNumber(func) << ' ';
+ s << "</td></tr>";
+
+ s << "</table>> ];\n";
+
+ for (const OverloadData *pd : m_nextOverloadData)
+ s << INDENT << '"' << rfunc->name() << "\" -> " << pd->dumpGraph();
+
+ s << "}\n";
+ } else {
+ QString argId = QLatin1String("arg_") + QString::number(quintptr(this));
+ s << argId << ";\n";
+
+ s << INDENT << '"' << 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
+ QString type = hasArgumentTypeReplace() ? argumentTypeReplaced() : argType().cppSignature();
+ s << "<tr><td bgcolor=\"gray\" align=\"right\">type</td><td bgcolor=\"gray\" align=\"left\">";
+ s << toHtml(type) << "</td></tr>";
+ if (hasArgumentTypeReplace()) {
+ s << "<tr><td bgcolor=\"gray\" align=\"right\">orig. type</td><td bgcolor=\"gray\" align=\"left\">";
+ s << toHtml(argType().cppSignature()) << "</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 AbstractMetaFunction *func : m_overloads)
+ s << 'f' << functionNumber(func) << ' ';
+ s << "</td></tr>";
+
+ // Show default values (original and modified) for various functions
+ for (const AbstractMetaFunction *func : m_overloads) {
+ const AbstractMetaArgument *arg = argument(func);
+ if (!arg)
+ continue;
+ QString argDefault = arg->defaultValueExpression();
+ if (!argDefault.isEmpty() ||
+ argDefault != arg->originalDefaultValueExpression()) {
+ s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << functionNumber(func);
+ s << "-default</td><td bgcolor=\"gray\" align=\"left\">";
+ s << argDefault << "</td></tr>";
+ }
+ if (argDefault != arg->originalDefaultValueExpression()) {
+ s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << functionNumber(func);
+ s << "-orig-default</td><td bgcolor=\"gray\" align=\"left\">";
+ s << arg->originalDefaultValueExpression() << "</td></tr>";
+ }
+ }
+
+ s << "</table>>];\n";
+
+ for (const OverloadData *pd : m_nextOverloadData)
+ s << INDENT << argId << " -> " << pd->dumpGraph();
+ }
+ return result;
+}
+
+int OverloadData::functionNumber(const AbstractMetaFunction *func) const
+{
+ return m_headOverloadData->m_overloads.indexOf(func);
+}
+
+OverloadData::~OverloadData()
+{
+ while (!m_nextOverloadData.isEmpty())
+ delete m_nextOverloadData.takeLast();
+}
+
+bool OverloadData::hasArgumentTypeReplace() const
+{
+ return !m_argTypeReplaced.isEmpty();
+}
+
+QString OverloadData::argumentTypeReplaced() const
+{
+ return m_argTypeReplaced;
+}
+
+bool OverloadData::hasArgumentWithDefaultValue(const AbstractMetaFunctionList &overloads)
+{
+ if (OverloadData::getMinMaxArguments(overloads).second == 0)
+ return false;
+ for (const AbstractMetaFunction *func : overloads) {
+ if (hasArgumentWithDefaultValue(func))
+ return true;
+ }
+ return false;
+}
+
+bool OverloadData::hasArgumentWithDefaultValue() const
+{
+ if (maxArgs() == 0)
+ return false;
+ for (const AbstractMetaFunction *func : m_overloads) {
+ if (hasArgumentWithDefaultValue(func))
+ return true;
+ }
+ return false;
+}
+
+bool OverloadData::hasArgumentWithDefaultValue(const AbstractMetaFunction *func)
+{
+ const AbstractMetaArgumentList &arguments = func->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+ if (func->argumentRemoved(arg.argumentIndex() + 1))
+ continue;
+ if (arg.hasDefaultValueExpression())
+ return true;
+ }
+ return false;
+}
+
+AbstractMetaArgumentList OverloadData::getArgumentsWithDefaultValues(const AbstractMetaFunction *func)
+{
+ AbstractMetaArgumentList args;
+ const AbstractMetaArgumentList &arguments = func->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+ if (!arg.hasDefaultValueExpression()
+ || func->argumentRemoved(arg.argumentIndex() + 1))
+ continue;
+ args << arg;
+ }
+ return args;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+void OverloadData::formatDebug(QDebug &d) const
+{
+ const int count = m_overloads.size();
+ d << "argType=" << m_argType << ", minArgs=" << m_minArgs << ", maxArgs=" << m_maxArgs
+ << ", argPos=" << m_argPos << ", argTypeReplaced=\"" << m_argTypeReplaced
+ << "\", overloads[" << count << "]=(";
+ const int oldVerbosity = d.verbosity();
+ d.setVerbosity(3);
+ for (int i = 0; i < count; ++i) {
+ if (i)
+ d << '\n';
+ d << m_overloads.at(i);
+ }
+ d << ')';
+ d.setVerbosity(oldVerbosity);
+}
+
+QDebug operator<<(QDebug d, const OverloadData *od)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "OverloadData(";
+ if (od)
+ od->formatDebug(d);
+ else
+ d << '0';
+ 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..2507c3213
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/overloaddata.h
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt for Python.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef OVERLOADDATA_H
+#define OVERLOADDATA_H
+
+#include <abstractmetalang_typedefs.h>
+#include <QtCore/QBitArray>
+#include <QtCore/QVector>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+class ShibokenGenerator;
+
+class OverloadData;
+using OverloadDataList = QVector<OverloadData *>;
+
+class OverloadData
+{
+public:
+ using MetaFunctionList = QVector<const AbstractMetaFunction *>;
+
+ OverloadData(const AbstractMetaFunctionList &overloads, const ShibokenGenerator *generator);
+ ~OverloadData();
+
+ int minArgs() const { return m_headOverloadData->m_minArgs; }
+ int maxArgs() const { return m_headOverloadData->m_maxArgs; }
+ int argPos() const { return m_argPos; }
+
+ const AbstractMetaType &argType() const { return m_argType; }
+
+ /// Returns a string list containing all the possible return types (including void) for the current OverloadData.
+ QStringList returnTypes() const;
+
+ /// 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 allows threads when called.
+ bool hasAllowThread() 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 AbstractMetaFunctionList &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 AbstractMetaFunctionList &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 AbstractMetaFunctionList &overloads);
+
+ const AbstractMetaFunction *referenceFunction() const;
+ const AbstractMetaArgument *argument(const AbstractMetaFunction *func) const;
+ OverloadDataList overloadDataOnPosition(int argPos) const;
+
+ bool isHeadOverloadData() const { return this == m_headOverloadData; }
+
+ /// Returns the root OverloadData object that represents all the overloads.
+ OverloadData *headOverloadData() const { return m_headOverloadData; }
+
+ /// Returns the function that has a default value at the current OverloadData argument position, otherwise returns null.
+ const AbstractMetaFunction *getFunctionWithDefaultValue() const;
+
+ bool nextArgumentHasDefaultValue() const;
+ /// Returns the nearest occurrence, including this instance, of an argument with a default value.
+ OverloadData *findNextArgWithDefault();
+ bool isFinalOccurrence(const AbstractMetaFunction *func) const;
+
+ /// Returns the list of overloads removing repeated constant functions (ex.: "foo()" and "foo()const", the second is removed).
+ MetaFunctionList overloadsWithoutRepetition() const;
+ const MetaFunctionList &overloads() const { return m_overloads; }
+ OverloadDataList nextOverloadData() const { return m_nextOverloadData; }
+ OverloadData *previousOverloadData() const { return m_previousOverloadData; }
+
+ QVector<int> invalidArgumentLengths() const;
+
+ static int numberOfRemovedArguments(const AbstractMetaFunction *func, int finalArgPos = -1);
+ static QPair<int, int> getMinMaxArguments(const AbstractMetaFunctionList &overloads);
+ /// Returns true if all overloads have no more than one argument.
+ static bool isSingleArgument(const AbstractMetaFunctionList &overloads);
+
+ void dumpGraph(const QString &filename) const;
+ QString dumpGraph() const;
+
+ bool hasArgumentTypeReplace() const;
+ QString argumentTypeReplaced() const;
+
+ bool hasArgumentWithDefaultValue() const;
+ static bool hasArgumentWithDefaultValue(const AbstractMetaFunctionList &overloads);
+ static bool hasArgumentWithDefaultValue(const AbstractMetaFunction *func);
+
+ /// Returns a list of function arguments which have default values and were not removed.
+ static AbstractMetaArgumentList getArgumentsWithDefaultValues(const AbstractMetaFunction *func);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &) const;
+#endif
+
+private:
+ OverloadData(OverloadData *headOverloadData, const AbstractMetaFunction *func,
+ const AbstractMetaType &argType, int argPos);
+
+ void addOverload(const AbstractMetaFunction *func);
+ OverloadData *addOverloadData(const AbstractMetaFunction *func, const AbstractMetaArgument &arg);
+
+ void sortNextOverloads();
+ bool sortByOverloadNumberModification();
+
+ int functionNumber(const AbstractMetaFunction *func) const;
+ OverloadDataList overloadDataOnPosition(OverloadData *overloadData, int argPos) const;
+
+ int m_minArgs;
+ int m_maxArgs;
+ int m_argPos;
+ AbstractMetaType m_argType;
+ QString m_argTypeReplaced;
+ MetaFunctionList m_overloads;
+
+ OverloadData *m_headOverloadData;
+ OverloadDataList m_nextOverloadData;
+ OverloadData *m_previousOverloadData;
+ const ShibokenGenerator *m_generator;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug, const OverloadData *);
+#endif
+
+#endif // OVERLOADDATA_H
diff --git a/sources/shiboken6/generator/shiboken/shibokengenerator.cpp b/sources/shiboken6/generator/shiboken/shibokengenerator.cpp
new file mode 100644
index 000000000..5382b3a9f
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/shibokengenerator.cpp
@@ -0,0 +1,2884 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt for Python.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "shibokengenerator.h"
+#include "ctypenames.h"
+#include <abstractmetalang.h>
+#include <messages.h>
+#include "overloaddata.h"
+#include "propertyspec.h"
+#include <reporthandler.h>
+#include <typedatabase.h>
+#include <abstractmetabuilder.h>
+#include <iostream>
+
+#include <QtCore/QDir>
+#include <QtCore/QDebug>
+#include <QtCore/QRegularExpression>
+#include <limits>
+#include <memory>
+
+static const char AVOID_PROTECTED_HACK[] = "avoid-protected-hack";
+static const char PARENT_CTOR_HEURISTIC[] = "enable-parent-ctor-heuristic";
+static const char RETURN_VALUE_HEURISTIC[] = "enable-return-value-heuristic";
+static const char ENABLE_PYSIDE_EXTENSIONS[] = "enable-pyside-extensions";
+static const char DISABLE_VERBOSE_ERROR_MESSAGES[] = "disable-verbose-error-messages";
+static const char USE_ISNULL_AS_NB_NONZERO[] = "use-isnull-as-nb_nonzero";
+static const char WRAPPER_DIAGNOSTICS[] = "wrapper-diagnostics";
+
+const char *CPP_ARG = "cppArg";
+const char *CPP_ARG_REMOVED = "removed_cppArg";
+const char *CPP_RETURN_VAR = "cppResult";
+const char *CPP_SELF_VAR = "cppSelf";
+const char *NULL_PTR = "nullptr";
+const char *PYTHON_ARG = "pyArg";
+const char *PYTHON_ARGS = "pyArgs";
+const char *PYTHON_OVERRIDE_VAR = "pyOverride";
+const char *PYTHON_RETURN_VAR = "pyResult";
+const char *PYTHON_TO_CPP_VAR = "pythonToCpp";
+const char *SMART_POINTER_GETTER = "kSmartPointerGetter";
+
+const char *CONV_RULE_OUT_VAR_SUFFIX = "_out";
+const char *BEGIN_ALLOW_THREADS =
+ "PyThreadState *_save = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS";
+const char *END_ALLOW_THREADS = "PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS";
+
+//static void dumpFunction(AbstractMetaFunctionList lst);
+
+QHash<QString, QString> ShibokenGenerator::m_pythonPrimitiveTypeName = QHash<QString, QString>();
+QHash<QString, QString> ShibokenGenerator::m_pythonOperators = QHash<QString, QString>();
+QHash<QString, QString> ShibokenGenerator::m_formatUnits = QHash<QString, QString>();
+QHash<QString, QString> ShibokenGenerator::m_tpFuncs = QHash<QString, QString>();
+QStringList ShibokenGenerator::m_knownPythonTypes = QStringList();
+
+static QRegularExpression placeHolderRegex(int index)
+{
+ return QRegularExpression(QLatin1Char('%') + QString::number(index) + QStringLiteral("\\b"));
+}
+
+// Return a prefix to fully qualify value, eg:
+// resolveScopePrefix("Class::NestedClass::Enum::Value1", "Enum::Value1")
+// -> "Class::NestedClass::")
+static QString resolveScopePrefix(const QStringList &scopeList, const QString &value)
+{
+ QString name;
+ for (int i = scopeList.size() - 1 ; i >= 0; --i) {
+ const QString prefix = scopeList.at(i) + QLatin1String("::");
+ if (value.startsWith(prefix))
+ name.clear();
+ else
+ name.prepend(prefix);
+ }
+ return name;
+}
+
+static inline QStringList splitClassScope(const AbstractMetaClass *scope)
+{
+ return scope->qualifiedCppName().split(QLatin1String("::"), Qt::SkipEmptyParts);
+}
+
+static QString resolveScopePrefix(const AbstractMetaClass *scope, const QString &value)
+{
+ return scope
+ ? resolveScopePrefix(splitClassScope(scope), value)
+ : QString();
+}
+
+static QString resolveScopePrefix(const AbstractMetaEnum *metaEnum,
+ const QString &value)
+{
+ QStringList parts;
+ if (const AbstractMetaClass *scope = metaEnum->enclosingClass())
+ parts.append(splitClassScope(scope));
+ // Fully qualify the value which is required for C++ 11 enum classes.
+ if (!metaEnum->isAnonymous())
+ parts.append(metaEnum->name());
+ return resolveScopePrefix(parts, value);
+}
+
+struct GeneratorClassInfoCacheEntry
+{
+ ShibokenGenerator::FunctionGroups functionGroups;
+ bool needsGetattroFunction = false;
+};
+
+using GeneratorClassInfoCache = QHash<const AbstractMetaClass *, GeneratorClassInfoCacheEntry>;
+
+Q_GLOBAL_STATIC(GeneratorClassInfoCache, generatorClassInfoCache)
+
+ShibokenGenerator::ShibokenGenerator()
+{
+ if (m_pythonPrimitiveTypeName.isEmpty())
+ ShibokenGenerator::initPrimitiveTypesCorrespondences();
+
+ if (m_tpFuncs.isEmpty())
+ ShibokenGenerator::clearTpFuncs();
+
+ if (m_knownPythonTypes.isEmpty())
+ ShibokenGenerator::initKnownPythonTypes();
+
+ m_metaTypeFromStringCache = AbstractMetaTypeCache();
+
+ m_typeSystemConvName[TypeSystemCheckFunction] = QLatin1String("checkType");
+ m_typeSystemConvName[TypeSystemIsConvertibleFunction] = QLatin1String("isConvertible");
+ m_typeSystemConvName[TypeSystemToCppFunction] = QLatin1String("toCpp");
+ m_typeSystemConvName[TypeSystemToPythonFunction] = QLatin1String("toPython");
+
+ const char CHECKTYPE_REGEX[] = R"(%CHECKTYPE\[([^\[]*)\]\()";
+ const char ISCONVERTIBLE_REGEX[] = R"(%ISCONVERTIBLE\[([^\[]*)\]\()";
+ const char CONVERTTOPYTHON_REGEX[] = R"(%CONVERTTOPYTHON\[([^\[]*)\]\()";
+ // Capture a '*' leading the variable name into the target
+ // so that "*valuePtr = %CONVERTTOCPP..." works as expected.
+ const char CONVERTTOCPP_REGEX[] =
+ R"((\*?%?[a-zA-Z_][\w\.]*(?:\[[^\[^<^>]+\])*)(?:\s+)=(?:\s+)%CONVERTTOCPP\[([^\[]*)\]\()";
+ m_typeSystemConvRegEx[TypeSystemCheckFunction] = QRegularExpression(QLatin1String(CHECKTYPE_REGEX));
+ m_typeSystemConvRegEx[TypeSystemIsConvertibleFunction] = QRegularExpression(QLatin1String(ISCONVERTIBLE_REGEX));
+ m_typeSystemConvRegEx[TypeSystemToPythonFunction] = QRegularExpression(QLatin1String(CONVERTTOPYTHON_REGEX));
+ m_typeSystemConvRegEx[TypeSystemToCppFunction] = QRegularExpression(QLatin1String(CONVERTTOCPP_REGEX));
+}
+
+ShibokenGenerator::~ShibokenGenerator() = default;
+
+void ShibokenGenerator::clearTpFuncs()
+{
+ m_tpFuncs.insert(QLatin1String("__str__"), QString());
+ m_tpFuncs.insert(QLatin1String("__repr__"), QString());
+ m_tpFuncs.insert(QLatin1String("__iter__"), QString());
+ m_tpFuncs.insert(QLatin1String("__next__"), QString());
+}
+
+void ShibokenGenerator::initPrimitiveTypesCorrespondences()
+{
+ // Python primitive types names
+ m_pythonPrimitiveTypeName.clear();
+
+ // PyBool
+ m_pythonPrimitiveTypeName.insert(QLatin1String("bool"), QLatin1String("PyBool"));
+
+ const char *charTypes[] = {
+ "char", "signed char", "unsigned char"
+ };
+ for (const char *charType : charTypes)
+ m_pythonPrimitiveTypeName.insert(QLatin1String(charType), QStringLiteral("SbkChar"));
+
+ // PyInt
+ const char *intTypes[] = {
+ "int", "signed int", "uint", "unsigned int",
+ "short", "ushort", "signed short", "signed short int",
+ "unsigned short", "unsigned short", "unsigned short int",
+ "long"
+ };
+ for (const char *intType : intTypes)
+ m_pythonPrimitiveTypeName.insert(QLatin1String(intType), QStringLiteral("PyInt"));
+
+ // PyFloat
+ m_pythonPrimitiveTypeName.insert(doubleT(), QLatin1String("PyFloat"));
+ m_pythonPrimitiveTypeName.insert(floatT(), QLatin1String("PyFloat"));
+
+ // PyLong
+ const char *longTypes[] = {
+ "unsigned long", "signed long", "ulong", "unsigned long int",
+ "long long", "__int64",
+ "unsigned long long", "unsigned __int64", "size_t"
+ };
+ for (const char *longType : longTypes)
+ m_pythonPrimitiveTypeName.insert(QLatin1String(longType), QStringLiteral("PyLong"));
+
+ // Python operators
+ m_pythonOperators.clear();
+
+ // call operator
+ m_pythonOperators.insert(QLatin1String("operator()"), QLatin1String("call"));
+
+ // Arithmetic operators
+ m_pythonOperators.insert(QLatin1String("operator+"), QLatin1String("add"));
+ m_pythonOperators.insert(QLatin1String("operator-"), QLatin1String("sub"));
+ m_pythonOperators.insert(QLatin1String("operator*"), QLatin1String("mul"));
+ m_pythonOperators.insert(QLatin1String("operator/"), QLatin1String("div"));
+ m_pythonOperators.insert(QLatin1String("operator%"), QLatin1String("mod"));
+
+ // Inplace arithmetic operators
+ m_pythonOperators.insert(QLatin1String("operator+="), QLatin1String("iadd"));
+ m_pythonOperators.insert(QLatin1String("operator-="), QLatin1String("isub"));
+ m_pythonOperators.insert(QLatin1String("operator++"), QLatin1String("iadd"));
+ m_pythonOperators.insert(QLatin1String("operator--"), QLatin1String("isub"));
+ m_pythonOperators.insert(QLatin1String("operator*="), QLatin1String("imul"));
+ m_pythonOperators.insert(QLatin1String("operator/="), QLatin1String("idiv"));
+ m_pythonOperators.insert(QLatin1String("operator%="), QLatin1String("imod"));
+
+ // Bitwise operators
+ m_pythonOperators.insert(QLatin1String("operator&"), QLatin1String("and"));
+ m_pythonOperators.insert(QLatin1String("operator^"), QLatin1String("xor"));
+ m_pythonOperators.insert(QLatin1String("operator|"), QLatin1String("or"));
+ m_pythonOperators.insert(QLatin1String("operator<<"), QLatin1String("lshift"));
+ m_pythonOperators.insert(QLatin1String("operator>>"), QLatin1String("rshift"));
+ m_pythonOperators.insert(QLatin1String("operator~"), QLatin1String("invert"));
+
+ // Inplace bitwise operators
+ m_pythonOperators.insert(QLatin1String("operator&="), QLatin1String("iand"));
+ m_pythonOperators.insert(QLatin1String("operator^="), QLatin1String("ixor"));
+ m_pythonOperators.insert(QLatin1String("operator|="), QLatin1String("ior"));
+ m_pythonOperators.insert(QLatin1String("operator<<="), QLatin1String("ilshift"));
+ m_pythonOperators.insert(QLatin1String("operator>>="), QLatin1String("irshift"));
+
+ // Comparison operators
+ m_pythonOperators.insert(QLatin1String("operator=="), QLatin1String("eq"));
+ m_pythonOperators.insert(QLatin1String("operator!="), QLatin1String("ne"));
+ m_pythonOperators.insert(QLatin1String("operator<"), QLatin1String("lt"));
+ m_pythonOperators.insert(QLatin1String("operator>"), QLatin1String("gt"));
+ m_pythonOperators.insert(QLatin1String("operator<="), QLatin1String("le"));
+ m_pythonOperators.insert(QLatin1String("operator>="), QLatin1String("ge"));
+
+ // Initialize format units for C++->Python->C++ conversion
+ m_formatUnits.clear();
+ m_formatUnits.insert(QLatin1String("char"), QLatin1String("b"));
+ m_formatUnits.insert(QLatin1String("unsigned char"), QLatin1String("B"));
+ m_formatUnits.insert(intT(), QLatin1String("i"));
+ m_formatUnits.insert(QLatin1String("unsigned int"), QLatin1String("I"));
+ m_formatUnits.insert(shortT(), QLatin1String("h"));
+ m_formatUnits.insert(unsignedShortT(), QLatin1String("H"));
+ m_formatUnits.insert(longT(), QLatin1String("l"));
+ m_formatUnits.insert(unsignedLongLongT(), QLatin1String("k"));
+ m_formatUnits.insert(longLongT(), QLatin1String("L"));
+ m_formatUnits.insert(QLatin1String("__int64"), QLatin1String("L"));
+ m_formatUnits.insert(unsignedLongLongT(), QLatin1String("K"));
+ m_formatUnits.insert(QLatin1String("unsigned __int64"), QLatin1String("K"));
+ m_formatUnits.insert(doubleT(), QLatin1String("d"));
+ m_formatUnits.insert(floatT(), QLatin1String("f"));
+}
+
+void ShibokenGenerator::initKnownPythonTypes()
+{
+ m_knownPythonTypes.clear();
+ m_knownPythonTypes << QLatin1String("PyBool") << QLatin1String("PyInt")
+ << QLatin1String("PyFloat") << QLatin1String("PyLong") << QLatin1String("PyObject")
+ << QLatin1String("PyString") << QLatin1String("PyBuffer") << QLatin1String("PySequence")
+ << QLatin1String("PyTuple") << QLatin1String("PyList") << QLatin1String("PyDict")
+ << QLatin1String("PyObject*") << QLatin1String("PyObject *") << QLatin1String("PyTupleObject*");
+}
+
+QString ShibokenGenerator::translateTypeForWrapperMethod(const AbstractMetaType &cType,
+ const AbstractMetaClass *context,
+ Options options) const
+{
+ if (cType.isArray())
+ return translateTypeForWrapperMethod(*cType.arrayElementType(), context, options) + QLatin1String("[]");
+
+ if (avoidProtectedHack() && cType.isEnum()) {
+ const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(cType);
+ if (metaEnum && metaEnum->isProtected())
+ return protectedEnumSurrogateName(metaEnum);
+ }
+
+ return translateType(cType, context, options);
+}
+
+bool ShibokenGenerator::shouldGenerateCppWrapper(const AbstractMetaClass *metaClass) const
+{
+ if (metaClass->isNamespace()
+ || metaClass->attributes().testFlag(AbstractMetaAttributes::FinalCppClass)
+ || metaClass->typeEntry()->typeFlags().testFlag(ComplexTypeEntry::DisableWrapper)) {
+ return false;
+ }
+ bool result = metaClass->isPolymorphic() || metaClass->hasVirtualDestructor();
+ if (avoidProtectedHack()) {
+ result = result || metaClass->hasProtectedFields() || metaClass->hasProtectedDestructor();
+ if (!result && metaClass->hasProtectedFunctions()) {
+ int protectedFunctions = 0;
+ int protectedOperators = 0;
+ const AbstractMetaFunctionList &funcs = metaClass->functions();
+ for (const AbstractMetaFunction *func : funcs) {
+ if (!func->isProtected() || func->isSignal() || func->isModifiedRemoved())
+ continue;
+ if (func->isOperatorOverload())
+ protectedOperators++;
+ else
+ protectedFunctions++;
+ }
+ result = result || (protectedFunctions > protectedOperators);
+ }
+ } else {
+ result = result && !metaClass->hasPrivateDestructor();
+ }
+ return result;
+}
+
+bool ShibokenGenerator::shouldWriteVirtualMethodNative(const AbstractMetaFunction *func)
+{
+ // PYSIDE-803: Extracted this because it is used multiple times.
+ const AbstractMetaClass *metaClass = func->ownerClass();
+ return (!avoidProtectedHack() || !metaClass->hasPrivateDestructor())
+ && ((func->isVirtual() || func->isAbstract())
+ && (func->attributes() & AbstractMetaAttributes::FinalCppMethod) == 0);
+}
+
+QString ShibokenGenerator::wrapperName(const AbstractMetaClass *metaClass) const
+{
+ Q_ASSERT(shouldGenerateCppWrapper(metaClass));
+ QString result = metaClass->name();
+ if (metaClass->enclosingClass()) // is a inner class
+ result.replace(QLatin1String("::"), QLatin1String("_"));
+ return result + QLatin1String("Wrapper");
+}
+
+QString ShibokenGenerator::fullPythonClassName(const AbstractMetaClass *metaClass)
+{
+ QString fullClassName = metaClass->name();
+ const AbstractMetaClass *enclosing = metaClass->enclosingClass();
+ while (enclosing) {
+ if (NamespaceTypeEntry::isVisibleScope(enclosing->typeEntry()))
+ fullClassName.prepend(enclosing->name() + QLatin1Char('.'));
+ enclosing = enclosing->enclosingClass();
+ }
+ fullClassName.prepend(packageName() + QLatin1Char('.'));
+ return fullClassName;
+}
+
+QString ShibokenGenerator::fullPythonFunctionName(const AbstractMetaFunction *func)
+{
+ 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;
+ else
+ funcName.prepend(fullClassName + QLatin1Char('.'));
+ }
+ else {
+ funcName = packageName() + QLatin1Char('.') + func->name();
+ }
+ return funcName;
+}
+
+QString ShibokenGenerator::protectedEnumSurrogateName(const AbstractMetaEnum *metaEnum)
+{
+ return metaEnum->fullName().replace(QLatin1Char('.'), QLatin1Char('_')).replace(QLatin1String("::"), QLatin1String("_")) + QLatin1String("_Surrogate");
+}
+
+QString ShibokenGenerator::protectedFieldGetterName(const AbstractMetaField *field)
+{
+ return QStringLiteral("protected_%1_getter").arg(field->name());
+}
+
+QString ShibokenGenerator::protectedFieldSetterName(const AbstractMetaField *field)
+{
+ return QStringLiteral("protected_%1_setter").arg(field->name());
+}
+
+QString ShibokenGenerator::cpythonFunctionName(const AbstractMetaFunction *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 += QLatin1String("_Init");
+ } else {
+ result += QLatin1String("Func_");
+ if (func->isOperatorOverload())
+ result += ShibokenGenerator::pythonOperatorFunctionName(func);
+ else
+ result += func->name();
+ }
+ } else {
+ result = QLatin1String("Sbk") + moduleName() + QLatin1String("Module_") + func->name();
+ }
+
+ return result;
+}
+
+QString ShibokenGenerator::cpythonMethodDefinitionName(const AbstractMetaFunction *func)
+{
+ if (!func->ownerClass())
+ return QString();
+ return QStringLiteral("%1Method_%2").arg(cpythonBaseName(func->ownerClass()->typeEntry()), func->name());
+}
+
+QString ShibokenGenerator::cpythonGettersSettersDefinitionName(const AbstractMetaClass *metaClass)
+{
+ return cpythonBaseName(metaClass) + QLatin1String("_getsetlist");
+}
+
+QString ShibokenGenerator::cpythonSetattroFunctionName(const AbstractMetaClass *metaClass)
+{
+ return cpythonBaseName(metaClass) + QLatin1String("_setattro");
+}
+
+
+QString ShibokenGenerator::cpythonGetattroFunctionName(const AbstractMetaClass *metaClass)
+{
+ return cpythonBaseName(metaClass) + QLatin1String("_getattro");
+}
+
+QString ShibokenGenerator::cpythonGetterFunctionName(const QString &name,
+ const AbstractMetaClass *enclosingClass)
+{
+ return cpythonBaseName(enclosingClass) + QStringLiteral("_get_") + name;
+}
+
+QString ShibokenGenerator::cpythonSetterFunctionName(const QString &name,
+ const AbstractMetaClass *enclosingClass)
+{
+ return cpythonBaseName(enclosingClass) + QStringLiteral("_set_") + 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 AbstractMetaClass *metaClass)
+{
+ return cpythonGetterFunctionName(property->name(), metaClass);
+}
+
+QString ShibokenGenerator::cpythonSetterFunctionName(const QPropertySpec *property,
+ const AbstractMetaClass *metaClass)
+{
+ return cpythonSetterFunctionName(property->name(), metaClass);
+}
+
+static QString cpythonEnumFlagsName(const QString &moduleName,
+ const QString &qualifiedCppName)
+{
+ QString result = QStringLiteral("Sbk%1_%2").arg(moduleName, qualifiedCppName);
+ result.replace(QLatin1String("::"), QLatin1String("_"));
+ return result;
+}
+
+// Return the scope for fully qualifying the enumeration including trailing "::".
+static QString searchForEnumScope(const AbstractMetaClass *metaClass, const QString &value)
+{
+ if (!metaClass)
+ return QString();
+ const AbstractMetaEnumList &enums = metaClass->enums();
+ for (const AbstractMetaEnum *metaEnum : enums) {
+ if (metaEnum->findEnumValue(value))
+ return resolveScopePrefix(metaEnum, value);
+ }
+ // PYSIDE-331: We need to also search the base classes.
+ QString ret = searchForEnumScope(metaClass->enclosingClass(), value);
+ if (ret.isEmpty())
+ ret = searchForEnumScope(metaClass->baseClass(), value);
+ return ret;
+}
+
+// Handle QFlags<> for guessScopeForDefaultValue()
+QString ShibokenGenerator::guessScopeForDefaultFlagsValue(const AbstractMetaFunction *func,
+ const AbstractMetaArgument &arg,
+ const QString &value) const
+{
+ // Numeric values -> "Options(42)"
+ static const QRegularExpression numberRegEx(QStringLiteral("^\\d+$")); // Numbers to flags
+ Q_ASSERT(numberRegEx.isValid());
+ if (numberRegEx.match(value).hasMatch()) {
+ QString typeName = translateTypeForWrapperMethod(arg.type(), func->implementingClass());
+ if (arg.type().isConstant())
+ typeName.remove(0, sizeof("const ") / sizeof(char) - 1);
+ switch (arg.type().referenceType()) {
+ case NoReference:
+ break;
+ case LValueReference:
+ typeName.chop(1);
+ break;
+ case RValueReference:
+ typeName.chop(2);
+ break;
+ }
+ return typeName + QLatin1Char('(') + value + QLatin1Char(')');
+ }
+
+ // "Options(Option1 | Option2)" -> "Options(Class::Enum::Option1 | Class::Enum::Option2)"
+ static const QRegularExpression enumCombinationRegEx(QStringLiteral("^([A-Za-z_][\\w:]*)\\(([^,\\(\\)]*)\\)$")); // FlagName(EnumItem|EnumItem|...)
+ Q_ASSERT(enumCombinationRegEx.isValid());
+ const QRegularExpressionMatch match = enumCombinationRegEx.match(value);
+ if (match.hasMatch()) {
+ const QString expression = match.captured(2).trimmed();
+ if (expression.isEmpty())
+ return value;
+ const QStringList enumItems = expression.split(QLatin1Char('|'));
+ const QString scope = searchForEnumScope(func->implementingClass(),
+ enumItems.constFirst().trimmed());
+ if (scope.isEmpty())
+ return value;
+ QString result;
+ QTextStream str(&result);
+ str << match.captured(1) << '('; // Flag name
+ for (int i = 0, size = enumItems.size(); i < size; ++i) {
+ if (i)
+ str << '|';
+ str << scope << enumItems.at(i).trimmed();
+ }
+ str << ')';
+ return result;
+ }
+ // A single flag "Option1" -> "Class::Enum::Option1"
+ return searchForEnumScope(func->implementingClass(), value) + value;
+}
+
+/*
+ * This function uses some heuristics to find out the scope for a given
+ * argument default value since they must be fully qualified when used outside the class:
+ * class A {
+ * enum Enum { e1, e1 };
+ * void foo(Enum e = e1);
+ * }
+ * should be qualified to:
+ * A::Enum cppArg0 = A::Enum::e1;
+ *
+ * New situations may arise in the future and
+ * this method should be updated, do it with care.
+ */
+QString ShibokenGenerator::guessScopeForDefaultValue(const AbstractMetaFunction *func,
+ const AbstractMetaArgument &arg) const
+{
+ QString value = arg.defaultValueExpression();
+
+ if (value.isEmpty() || value == QLatin1String("{}")
+ || arg.hasModifiedDefaultValueExpression()
+ || isPointer(arg.type())) {
+ return value;
+ }
+
+ static const QRegularExpression enumValueRegEx(QStringLiteral("^([A-Za-z_]\\w*)?$"));
+ Q_ASSERT(enumValueRegEx.isValid());
+ // Do not qualify macros by class name, eg QSGGeometry(..., int t = GL_UNSIGNED_SHORT);
+ static const QRegularExpression macroRegEx(QStringLiteral("^[A-Z_][A-Z0-9_]*$"));
+ Q_ASSERT(macroRegEx.isValid());
+ if (arg.type().isPrimitive() && macroRegEx.match(value).hasMatch())
+ return value;
+
+ QString prefix;
+ if (arg.type().isEnum()) {
+ if (const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(arg.type()))
+ prefix = resolveScopePrefix(metaEnum, value);
+ } else if (arg.type().isFlags()) {
+ value = guessScopeForDefaultFlagsValue(func, arg, value);
+ } else if (arg.type().typeEntry()->isValue()) {
+ const AbstractMetaClass *metaClass = AbstractMetaClass::findClass(classes(),
+ arg.type().typeEntry());
+ if (enumValueRegEx.match(value).hasMatch() && value != QLatin1String("NULL"))
+ prefix = resolveScopePrefix(metaClass, value);
+ } else if (arg.type().isPrimitive() && arg.type().name() == intT()) {
+ if (enumValueRegEx.match(value).hasMatch() && func->implementingClass())
+ prefix = resolveScopePrefix(func->implementingClass(), value);
+ } else if (arg.type().isPrimitive()) {
+ static const QRegularExpression unknowArgumentRegEx(QStringLiteral("^(?:[A-Za-z_][\\w:]*\\()?([A-Za-z_]\\w*)(?:\\))?$")); // [PrimitiveType(] DESIREDNAME [)]
+ Q_ASSERT(unknowArgumentRegEx.isValid());
+ const QRegularExpressionMatch match = unknowArgumentRegEx.match(value);
+ if (match.hasMatch() && func->implementingClass()) {
+ const AbstractMetaFieldList &fields = func->implementingClass()->fields();
+ for (const AbstractMetaField *field : fields) {
+ if (match.captured(1).trimmed() == field->name()) {
+ QString fieldName = field->name();
+ if (field->isStatic()) {
+ prefix = resolveScopePrefix(func->implementingClass(), value);
+ fieldName.prepend(prefix);
+ prefix.clear();
+ } else {
+ fieldName.prepend(QLatin1String(CPP_SELF_VAR) + QLatin1String("->"));
+ }
+ value.replace(match.captured(1), fieldName);
+ break;
+ }
+ }
+ }
+ }
+
+ if (!prefix.isEmpty())
+ value.prepend(prefix);
+ return value;
+}
+
+QString ShibokenGenerator::cpythonEnumName(const EnumTypeEntry *enumEntry)
+{
+ QString p = enumEntry->targetLangPackage();
+ p.replace(QLatin1Char('.'), QLatin1Char('_'));
+ return cpythonEnumFlagsName(p, enumEntry->qualifiedCppName());
+}
+
+QString ShibokenGenerator::cpythonEnumName(const AbstractMetaEnum *metaEnum)
+{
+ return cpythonEnumName(metaEnum->typeEntry());
+}
+
+QString ShibokenGenerator::cpythonFlagsName(const FlagsTypeEntry *flagsEntry)
+{
+ QString p = flagsEntry->targetLangPackage();
+ p.replace(QLatin1Char('.'), QLatin1Char('_'));
+ return cpythonEnumFlagsName(p, flagsEntry->originalName());
+}
+
+QString ShibokenGenerator::cpythonFlagsName(const AbstractMetaEnum *metaEnum)
+{
+ const FlagsTypeEntry *flags = metaEnum->typeEntry()->flags();
+ if (!flags)
+ return QString();
+ return cpythonFlagsName(flags);
+}
+
+QString ShibokenGenerator::cpythonSpecialCastFunctionName(const AbstractMetaClass *metaClass)
+{
+ return cpythonBaseName(metaClass->typeEntry()) + QLatin1String("SpecialCastFunction");
+}
+
+QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaClass *metaClass,
+ const QString &argName) const
+{
+ return cpythonWrapperCPtr(metaClass->typeEntry(), argName);
+}
+
+QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaType &metaType,
+ const QString &argName) const
+{
+ if (!ShibokenGenerator::isWrapperType(metaType.typeEntry()))
+ return QString();
+ return QLatin1String("reinterpret_cast< ::") + metaType.cppSignature()
+ + QLatin1String(" *>(Shiboken::Conversions::cppPointer(") + cpythonTypeNameExt(metaType)
+ + QLatin1String(", reinterpret_cast<SbkObject *>(") + argName + QLatin1String(")))");
+}
+
+QString ShibokenGenerator::cpythonWrapperCPtr(const TypeEntry *type,
+ const QString &argName) const
+{
+ if (!ShibokenGenerator::isWrapperType(type))
+ return QString();
+ return QLatin1String("reinterpret_cast< ::") + type->qualifiedCppName()
+ + QLatin1String(" *>(Shiboken::Conversions::cppPointer(") + cpythonTypeNameExt(type)
+ + QLatin1String(", reinterpret_cast<SbkObject *>(") + argName + QLatin1String(")))");
+}
+
+void ShibokenGenerator::writeToPythonConversion(QTextStream & s, const AbstractMetaType &type,
+ const AbstractMetaClass * /* context */,
+ const QString &argumentName)
+{
+ s << cpythonToPythonConversionFunction(type) << argumentName << ')';
+}
+
+void ShibokenGenerator::writeToCppConversion(QTextStream &s, const AbstractMetaClass *metaClass,
+ const QString &inArgName, const QString &outArgName)
+{
+ s << cpythonToCppConversionFunction(metaClass) << inArgName << ", &" << outArgName << ')';
+}
+
+void ShibokenGenerator::writeToCppConversion(QTextStream &s, const AbstractMetaType &type, const AbstractMetaClass *context,
+ const QString &inArgName, const QString &outArgName)
+{
+ s << cpythonToCppConversionFunction(type, context) << inArgName << ", &" << outArgName << ')';
+}
+
+bool ShibokenGenerator::shouldRejectNullPointerArgument(const AbstractMetaFunction *func, int argIndex)
+{
+ if (argIndex < 0 || argIndex >= func->arguments().count())
+ return false;
+
+ const AbstractMetaArgument &arg = func->arguments().at(argIndex);
+ if (isValueTypeWithCopyConstructorOnly(arg.type()))
+ return true;
+
+ // Argument type is not a pointer, a None rejection should not be
+ // necessary because the type checking would handle that already.
+ if (!isPointer(arg.type()))
+ return false;
+ if (func->argumentRemoved(argIndex + 1))
+ return false;
+ const FunctionModificationList &mods = func->modifications();
+ for (const FunctionModification &funcMod : mods) {
+ for (const ArgumentModification &argMod : funcMod.argument_mods) {
+ if (argMod.index == argIndex + 1 && argMod.noNullPointers)
+ return true;
+ }
+ }
+ return false;
+}
+
+QString ShibokenGenerator::getFormatUnitString(const AbstractMetaFunction *func, bool incRef) const
+{
+ QString result;
+ const char objType = (incRef ? 'O' : 'N');
+ const AbstractMetaArgumentList &arguments = func->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+ if (func->argumentRemoved(arg.argumentIndex() + 1))
+ continue;
+
+ const auto &type = arg.type();
+ if (!func->typeReplaced(arg.argumentIndex() + 1).isEmpty()) {
+ result += QLatin1Char(objType);
+ } else if (arg.type().isObject()
+ || type.isValue()
+ || type.isValuePointer()
+ || type.isNativePointer()
+ || type.isEnum()
+ || type.isFlags()
+ || type.isContainer()
+ || type.isSmartPointer()
+ || type.referenceType() == LValueReference) {
+ result += QLatin1Char(objType);
+ } else if (type.isPrimitive()) {
+ const auto *ptype =
+ static_cast<const PrimitiveTypeEntry *>(type.typeEntry());
+ if (ptype->basicReferencedTypeEntry())
+ ptype = ptype->basicReferencedTypeEntry();
+ if (m_formatUnits.contains(ptype->name()))
+ result += m_formatUnits[ptype->name()];
+ else
+ result += QLatin1Char(objType);
+ } else if (isCString(type)) {
+ result += QLatin1Char('z');
+ } else {
+ qCWarning(lcShiboken).noquote().nospace()
+ << "Method: " << func->ownerClass()->qualifiedCppName()
+ << "::" << func->signature() << " => Arg:"
+ << arg.name() << "index: " << arg.argumentIndex()
+ << " - cannot be handled properly. Use an inject-code to fix it!";
+ result += QLatin1Char('?');
+ }
+ }
+ return result;
+}
+
+QString ShibokenGenerator::cpythonBaseName(const AbstractMetaType &type)
+{
+ if (isCString(type))
+ return QLatin1String("PyString");
+ return cpythonBaseName(type.typeEntry());
+}
+
+QString ShibokenGenerator::cpythonBaseName(const AbstractMetaClass *metaClass)
+{
+ return cpythonBaseName(metaClass->typeEntry());
+}
+
+QString ShibokenGenerator::cpythonBaseName(const TypeEntry *type)
+{
+ QString baseName;
+ if (ShibokenGenerator::isWrapperType(type) || type->isNamespace()) { // && type->referenceType() == NoReference) {
+ baseName = QLatin1String("Sbk_") + type->name();
+ } else if (type->isPrimitive()) {
+ const auto *ptype = static_cast<const PrimitiveTypeEntry *>(type);
+ while (ptype->basicReferencedTypeEntry())
+ ptype = ptype->basicReferencedTypeEntry();
+ if (ptype->targetLangApiName() == ptype->name())
+ baseName = pythonPrimitiveTypeName(ptype->name());
+ else
+ baseName = ptype->targetLangApiName();
+ } else if (type->isEnum()) {
+ baseName = cpythonEnumName(static_cast<const EnumTypeEntry *>(type));
+ } else if (type->isFlags()) {
+ baseName = cpythonFlagsName(static_cast<const FlagsTypeEntry *>(type));
+ } else if (type->isContainer()) {
+ const auto *ctype = static_cast<const ContainerTypeEntry *>(type);
+ switch (ctype->containerKind()) {
+ case ContainerTypeEntry::ListContainer:
+ case ContainerTypeEntry::StringListContainer:
+ case ContainerTypeEntry::LinkedListContainer:
+ case ContainerTypeEntry::VectorContainer:
+ case ContainerTypeEntry::StackContainer:
+ case ContainerTypeEntry::QueueContainer:
+ //baseName = "PyList";
+ //break;
+ case ContainerTypeEntry::PairContainer:
+ //baseName = "PyTuple";
+ baseName = QLatin1String("PySequence");
+ break;
+ case ContainerTypeEntry::SetContainer:
+ baseName = QLatin1String("PySet");
+ break;
+ case ContainerTypeEntry::MapContainer:
+ case ContainerTypeEntry::MultiMapContainer:
+ case ContainerTypeEntry::HashContainer:
+ case ContainerTypeEntry::MultiHashContainer:
+ baseName = QLatin1String("PyDict");
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+ } else {
+ baseName = QLatin1String("PyObject");
+ }
+ return baseName.replace(QLatin1String("::"), QLatin1String("_"));
+}
+
+QString ShibokenGenerator::cpythonTypeName(const AbstractMetaClass *metaClass)
+{
+ return cpythonTypeName(metaClass->typeEntry());
+}
+
+QString ShibokenGenerator::cpythonTypeName(const TypeEntry *type)
+{
+ return cpythonBaseName(type) + QLatin1String("_TypeF()");
+}
+
+QString ShibokenGenerator::cpythonTypeNameExt(const TypeEntry *type) const
+{
+ return cppApiVariableName(type->targetLangPackage()) + QLatin1Char('[')
+ + getTypeIndexVariableName(type) + QLatin1Char(']');
+}
+
+QString ShibokenGenerator::converterObject(const AbstractMetaType &type)
+{
+ if (isCString(type))
+ return QLatin1String("Shiboken::Conversions::PrimitiveTypeConverter<const char *>()");
+ if (isVoidPointer(type))
+ return QLatin1String("Shiboken::Conversions::PrimitiveTypeConverter<void *>()");
+ const AbstractMetaTypeList nestedArrayTypes = type.nestedArrayTypes();
+ if (!nestedArrayTypes.isEmpty() && nestedArrayTypes.constLast().isCppPrimitive()) {
+ return QStringLiteral("Shiboken::Conversions::ArrayTypeConverter<")
+ + nestedArrayTypes.constLast().minimalSignature()
+ + QLatin1String(">(") + QString::number(nestedArrayTypes.size())
+ + QLatin1Char(')');
+ }
+
+ auto typeEntry = type.typeEntry();
+ if (typeEntry->isContainer() || typeEntry->isSmartPointer()) {
+ return convertersVariableName(typeEntry->targetLangPackage())
+ + QLatin1Char('[') + getTypeIndexVariableName(type) + QLatin1Char(']');
+ }
+ return converterObject(typeEntry);
+}
+
+QString ShibokenGenerator::converterObject(const TypeEntry *type)
+{
+ if (isCppPrimitive(type))
+ return QString::fromLatin1("Shiboken::Conversions::PrimitiveTypeConverter<%1>()").arg(type->qualifiedCppName());
+ if (isWrapperType(type) || type->isEnum() || type->isFlags())
+ return QString::fromLatin1("*PepType_SGTP(%1)->converter").arg(cpythonTypeNameExt(type));
+
+ if (type->isArray()) {
+ qDebug() << "Warning: no idea how to handle the Qt5 type " << type->qualifiedCppName();
+ return QString();
+ }
+
+ /* the typedef'd primitive types case */
+ const auto *pte = dynamic_cast<const PrimitiveTypeEntry *>(type);
+ if (!pte) {
+ qDebug() << "Warning: the Qt5 primitive type is unknown" << type->qualifiedCppName();
+ return QString();
+ }
+ if (pte->basicReferencedTypeEntry())
+ pte = pte->basicReferencedTypeEntry();
+ if (pte->isPrimitive() && !pte->isCppPrimitive() && !pte->customConversion())
+ return QString::fromLatin1("Shiboken::Conversions::PrimitiveTypeConverter<%1>()").arg(pte->qualifiedCppName());
+
+ return convertersVariableName(type->targetLangPackage())
+ + QLatin1Char('[') + getTypeIndexVariableName(type) + QLatin1Char(']');
+}
+
+QString ShibokenGenerator::cpythonTypeNameExt(const AbstractMetaType &type) const
+{
+ return cppApiVariableName(type.typeEntry()->targetLangPackage()) + QLatin1Char('[')
+ + getTypeIndexVariableName(type) + QLatin1Char(']');
+}
+
+static inline QString unknownOperator() { return QStringLiteral("__UNKNOWN_OPERATOR__"); }
+
+QString ShibokenGenerator::fixedCppTypeName(const CustomConversion::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(QLatin1Char(' '));
+ typeName.replace(QLatin1Char('.'), QLatin1Char('_'));
+ typeName.replace(QLatin1Char(','), QLatin1Char('_'));
+ typeName.replace(QLatin1Char('<'), QLatin1Char('_'));
+ typeName.replace(QLatin1Char('>'), QLatin1Char('_'));
+ typeName.replace(QLatin1String("::"), QLatin1String("_"));
+ typeName.replace(QLatin1String("*"), QLatin1String("PTR"));
+ typeName.replace(QLatin1String("&"), QLatin1String("REF"));
+ return typeName;
+}
+QString ShibokenGenerator::fixedCppTypeName(const TypeEntry *type, QString typeName)
+{
+ if (typeName.isEmpty())
+ typeName = type->qualifiedCppName();
+ if (!type->generateCode()) {
+ typeName.prepend(QLatin1Char('_'));
+ typeName.prepend(type->targetLangPackage());
+ }
+ return _fixedCppTypeName(typeName);
+}
+
+QString ShibokenGenerator::pythonPrimitiveTypeName(const QString &cppTypeName)
+{
+ QString rv = ShibokenGenerator::m_pythonPrimitiveTypeName.value(cppTypeName, QString());
+ if (rv.isEmpty()) {
+ // activate this when some primitive types are missing,
+ // i.e. when shiboken itself fails to build.
+ // In general, this is valid while just called by isNumeric()
+ // used on Qt5, 2015-09-20
+ if (false) {
+ std::cerr << "primitive type not found: " << qPrintable(cppTypeName) << std::endl;
+ abort();
+ }
+ }
+ return rv;
+}
+
+QString ShibokenGenerator::pythonPrimitiveTypeName(const PrimitiveTypeEntry *type)
+{
+ while (type->basicReferencedTypeEntry())
+ type = type->basicReferencedTypeEntry();
+ return pythonPrimitiveTypeName(type->name());
+}
+
+QString ShibokenGenerator::pythonOperatorFunctionName(const QString &cppOpFuncName)
+{
+ QString value = m_pythonOperators.value(cppOpFuncName);
+ if (value.isEmpty())
+ return unknownOperator();
+ value.prepend(QLatin1String("__"));
+ value.append(QLatin1String("__"));
+ return value;
+}
+
+QString ShibokenGenerator::pythonOperatorFunctionName(const AbstractMetaFunction *func)
+{
+ QString op = pythonOperatorFunctionName(func->originalName());
+ if (op == unknownOperator())
+ qCWarning(lcShiboken).noquote().nospace() << msgUnknownOperator(func);
+ if (func->arguments().isEmpty()) {
+ if (op == QLatin1String("__sub__"))
+ op = QLatin1String("__neg__");
+ else if (op == QLatin1String("__add__"))
+ op = QLatin1String("__pos__");
+ } 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, QLatin1Char('r'));
+ }
+ return op;
+}
+
+QString ShibokenGenerator::pythonRichCompareOperatorId(const QString &cppOpFuncName)
+{
+ return QLatin1String("Py_") + m_pythonOperators.value(cppOpFuncName).toUpper();
+}
+
+QString ShibokenGenerator::pythonRichCompareOperatorId(const AbstractMetaFunction *func)
+{
+ return pythonRichCompareOperatorId(func->originalName());
+}
+
+bool ShibokenGenerator::isNumber(const QString &cpythonApiName)
+{
+ return cpythonApiName == QLatin1String("PyInt")
+ || cpythonApiName == QLatin1String("PyFloat")
+ || cpythonApiName == QLatin1String("PyLong")
+ || cpythonApiName == QLatin1String("PyBool");
+}
+
+bool ShibokenGenerator::isNumber(const TypeEntry *type)
+{
+ if (!type->isPrimitive())
+ return false;
+ return isNumber(pythonPrimitiveTypeName(static_cast<const PrimitiveTypeEntry *>(type)));
+}
+
+bool ShibokenGenerator::isNumber(const AbstractMetaType &type)
+{
+ return isNumber(type.typeEntry());
+}
+
+bool ShibokenGenerator::isPyInt(const TypeEntry *type)
+{
+ if (!type->isPrimitive())
+ return false;
+ return pythonPrimitiveTypeName(static_cast<const PrimitiveTypeEntry *>(type))
+ == QLatin1String("PyInt");
+}
+
+bool ShibokenGenerator::isPyInt(const AbstractMetaType &type)
+{
+ return isPyInt(type.typeEntry());
+}
+
+bool ShibokenGenerator::isWrapperType(const TypeEntry *type)
+{
+ if (type->isComplex())
+ return ShibokenGenerator::isWrapperType(static_cast<const ComplexTypeEntry *>(type));
+ return type->isObject() || type->isValue() || type->isSmartPointer();
+}
+bool ShibokenGenerator::isWrapperType(const ComplexTypeEntry *type)
+{
+ return isObjectType(type) || type->isValue() || type->isSmartPointer();
+}
+bool ShibokenGenerator::isWrapperType(const AbstractMetaType &metaType)
+{
+ return isObjectType(metaType)
+ || metaType.typeEntry()->isValue()
+ || metaType.typeEntry()->isSmartPointer();
+}
+
+bool ShibokenGenerator::isPointerToWrapperType(const AbstractMetaType &type)
+{
+ return (isObjectType(type) && type.indirections() == 1) || type.isValuePointer();
+}
+
+bool ShibokenGenerator::isObjectTypeUsedAsValueType(const AbstractMetaType &type)
+{
+ return type.typeEntry()->isObject() && type.referenceType() == NoReference && type.indirections() == 0;
+}
+
+bool ShibokenGenerator::isValueTypeWithCopyConstructorOnly(const AbstractMetaClass *metaClass)
+{
+ if (!metaClass || !metaClass->typeEntry()->isValue())
+ return false;
+ if (metaClass->attributes().testFlag(AbstractMetaAttributes::HasRejectedDefaultConstructor))
+ return false;
+ const AbstractMetaFunctionList ctors =
+ metaClass->queryFunctions(AbstractMetaClass::Constructors);
+ bool copyConstructorFound = false;
+ for (auto ctor : ctors) {
+ switch (ctor->functionType()) {
+ case AbstractMetaFunction::ConstructorFunction:
+ return false;
+ case AbstractMetaFunction::CopyConstructorFunction:
+ copyConstructorFound = true;
+ break;
+ case AbstractMetaFunction::MoveConstructorFunction:
+ break;
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+ }
+ return copyConstructorFound;
+}
+
+bool ShibokenGenerator::isValueTypeWithCopyConstructorOnly(const TypeEntry *type) const
+{
+ if (!type || !type->isValue())
+ return false;
+ return isValueTypeWithCopyConstructorOnly(AbstractMetaClass::findClass(classes(), type));
+}
+
+bool ShibokenGenerator::isValueTypeWithCopyConstructorOnly(const AbstractMetaType &type) const
+{
+ return type.typeEntry()->isValue()
+ && isValueTypeWithCopyConstructorOnly(type.typeEntry());
+}
+
+bool ShibokenGenerator::isUserPrimitive(const TypeEntry *type)
+{
+ if (!type->isPrimitive())
+ return false;
+ const auto *trueType = static_cast<const PrimitiveTypeEntry *>(type);
+ if (trueType->basicReferencedTypeEntry())
+ trueType = trueType->basicReferencedTypeEntry();
+ return trueType->isPrimitive() && !trueType->isCppPrimitive()
+ && trueType->qualifiedCppName() != QLatin1String("std::string");
+}
+
+bool ShibokenGenerator::isUserPrimitive(const AbstractMetaType &type)
+{
+ if (type.indirections() != 0)
+ return false;
+ return isUserPrimitive(type.typeEntry());
+}
+
+bool ShibokenGenerator::isCppPrimitive(const TypeEntry *type)
+{
+ if (type->isCppPrimitive())
+ return true;
+ if (!type->isPrimitive())
+ return false;
+ const auto *trueType = static_cast<const PrimitiveTypeEntry *>(type);
+ if (trueType->basicReferencedTypeEntry())
+ trueType = trueType->basicReferencedTypeEntry();
+ return trueType->qualifiedCppName() == QLatin1String("std::string");
+}
+
+bool ShibokenGenerator::isCppPrimitive(const AbstractMetaType &type)
+{
+ if (isCString(type) || isVoidPointer(type))
+ return true;
+ if (type.indirections() != 0)
+ return false;
+ return isCppPrimitive(type.typeEntry());
+}
+
+bool ShibokenGenerator::shouldDereferenceArgumentPointer(const AbstractMetaArgument &arg)
+{
+ return shouldDereferenceAbstractMetaTypePointer(arg.type());
+}
+
+bool ShibokenGenerator::shouldDereferenceAbstractMetaTypePointer(const AbstractMetaType &metaType)
+{
+ return metaType.referenceType() == LValueReference && isWrapperType(metaType) && !isPointer(metaType);
+}
+
+bool ShibokenGenerator::visibilityModifiedToPrivate(const AbstractMetaFunction *func)
+{
+ const FunctionModificationList &mods = func->modifications();
+ for (const FunctionModification &mod : mods) {
+ if (mod.modifiers & Modification::Private)
+ return true;
+ }
+ return false;
+}
+
+bool ShibokenGenerator::isNullPtr(const QString &value)
+{
+ return value == QLatin1String("0") || value == QLatin1String("nullptr")
+ || value == QLatin1String("NULLPTR") || value == QLatin1String("{}");
+}
+
+QString ShibokenGenerator::cpythonCheckFunction(AbstractMetaType metaType, bool genericNumberType)
+{
+ QString customCheck;
+ if (metaType.typeEntry()->isCustom()) {
+ AbstractMetaType type;
+ customCheck = guessCPythonCheckFunction(metaType.typeEntry()->name(), &type);
+ if (type)
+ metaType = type;
+ if (!customCheck.isEmpty())
+ return customCheck;
+ }
+
+ if (isCppPrimitive(metaType)) {
+ if (isCString(metaType))
+ return QLatin1String("Shiboken::String::check");
+ if (isVoidPointer(metaType))
+ return QLatin1String("PyObject_Check");
+ return cpythonCheckFunction(metaType.typeEntry(), genericNumberType);
+ }
+ auto typeEntry = metaType.typeEntry();
+ if (typeEntry->isContainer()) {
+ QString typeCheck = QLatin1String("Shiboken::Conversions::");
+ ContainerTypeEntry::ContainerKind type =
+ static_cast<const ContainerTypeEntry *>(typeEntry)->containerKind();
+ if (type == ContainerTypeEntry::ListContainer
+ || type == ContainerTypeEntry::StringListContainer
+ || type == ContainerTypeEntry::LinkedListContainer
+ || type == ContainerTypeEntry::VectorContainer
+ || type == ContainerTypeEntry::StackContainer
+ || type == ContainerTypeEntry::SetContainer
+ || type == ContainerTypeEntry::QueueContainer) {
+ const AbstractMetaType &type = metaType.instantiations().constFirst();
+ if (isPointerToWrapperType(type)) {
+ typeCheck += QString::fromLatin1("checkSequenceTypes(%1, ").arg(cpythonTypeNameExt(type));
+ } else if (isWrapperType(type)) {
+ typeCheck += QLatin1String("convertibleSequenceTypes(reinterpret_cast<SbkObjectType *>(");
+ typeCheck += cpythonTypeNameExt(type);
+ typeCheck += QLatin1String("), ");
+ } else {
+ typeCheck += QString::fromLatin1("convertibleSequenceTypes(%1, ").arg(converterObject(type));
+ }
+ } else if (type == ContainerTypeEntry::MapContainer
+ || type == ContainerTypeEntry::MultiMapContainer
+ || type == ContainerTypeEntry::HashContainer
+ || type == ContainerTypeEntry::MultiHashContainer
+ || type == ContainerTypeEntry::PairContainer) {
+ QString pyType = (type == ContainerTypeEntry::PairContainer) ? QLatin1String("Pair") : QLatin1String("Dict");
+ const AbstractMetaType &firstType = metaType.instantiations().constFirst();
+ const AbstractMetaType &secondType = metaType.instantiations().constLast();
+ if (isPointerToWrapperType(firstType) && isPointerToWrapperType(secondType)) {
+ typeCheck += QString::fromLatin1("check%1Types(%2, %3, ")
+ .arg(pyType, cpythonTypeNameExt(firstType), cpythonTypeNameExt(secondType));
+ } else {
+ typeCheck += QString::fromLatin1("convertible%1Types(%2, %3, %4, %5, ")
+ .arg(pyType, converterObject(firstType),
+ isPointerToWrapperType(firstType) ? QLatin1String("true") : QLatin1String("false"),
+ converterObject(secondType),
+ isPointerToWrapperType(secondType) ? QLatin1String("true") : QLatin1String("false"));
+ }
+ }
+ return typeCheck;
+ }
+ return cpythonCheckFunction(typeEntry, genericNumberType);
+}
+
+QString ShibokenGenerator::cpythonCheckFunction(const TypeEntry *type, bool genericNumberType)
+{
+ QString customCheck;
+ if (type->isCustom()) {
+ AbstractMetaType metaType;
+ customCheck = guessCPythonCheckFunction(type->name(), &metaType);
+ if (metaType) {
+ const QString result = cpythonCheckFunction(metaType, genericNumberType);
+ return result;
+ }
+ return customCheck;
+ }
+
+ if (type->isEnum() || type->isFlags() || isWrapperType(type))
+ return QString::fromLatin1("SbkObject_TypeCheck(%1, ").arg(cpythonTypeNameExt(type));
+ if (isCppPrimitive(type)) {
+ return pythonPrimitiveTypeName(static_cast<const PrimitiveTypeEntry *>(type))
+ + QLatin1String("_Check");
+ }
+ QString typeCheck;
+ if (type->targetLangApiName() == type->name())
+ typeCheck = cpythonIsConvertibleFunction(type);
+ else if (type->targetLangApiName() == QLatin1String("PyUnicode"))
+ typeCheck = QLatin1String("Shiboken::String::check");
+ else
+ typeCheck = type->targetLangApiName() + QLatin1String("_Check");
+ return typeCheck;
+}
+
+QString ShibokenGenerator::guessCPythonCheckFunction(const QString &type, AbstractMetaType *metaType)
+{
+ *metaType = {};
+ // PYSIDE-795: We abuse PySequence for iterables.
+ // This part handles the overrides in the XML files.
+ if (type == QLatin1String("PySequence"))
+ return QLatin1String("Shiboken::String::checkIterable");
+
+ if (type == QLatin1String("PyTypeObject"))
+ return QLatin1String("PyType_Check");
+
+ if (type == QLatin1String("PyBuffer"))
+ return QLatin1String("Shiboken::Buffer::checkType");
+
+ if (type == QLatin1String("str"))
+ return QLatin1String("Shiboken::String::check");
+
+ *metaType = buildAbstractMetaTypeFromString(type);
+ if (*metaType && !metaType->typeEntry()->isCustom())
+ return QString();
+
+ return type + QLatin1String("_Check");
+}
+
+QString ShibokenGenerator::cpythonIsConvertibleFunction(const TypeEntry *type,
+ bool /* genericNumberType */,
+ bool /* checkExact */)
+{
+ if (isWrapperType(type)) {
+ QString result = QLatin1String("Shiboken::Conversions::");
+ result += (type->isValue() && !isValueTypeWithCopyConstructorOnly(type))
+ ? QLatin1String("isPythonToCppValueConvertible")
+ : QLatin1String("isPythonToCppPointerConvertible");
+ result += QLatin1String("(reinterpret_cast<SbkObjectType *>(")
+ + cpythonTypeNameExt(type) + QLatin1String("), ");
+ return result;
+ }
+ return QString::fromLatin1("Shiboken::Conversions::isPythonToCppConvertible(%1, ")
+ .arg(converterObject(type));
+}
+QString ShibokenGenerator::cpythonIsConvertibleFunction(AbstractMetaType metaType,
+ bool /* genericNumberType */)
+{
+ QString customCheck;
+ if (metaType.typeEntry()->isCustom()) {
+ AbstractMetaType type;
+ customCheck = guessCPythonCheckFunction(metaType.typeEntry()->name(), &type);
+ if (type)
+ metaType = type;
+ if (!customCheck.isEmpty())
+ return customCheck;
+ }
+
+ QString result = QLatin1String("Shiboken::Conversions::");
+ if (isWrapperType(metaType)) {
+ if (isPointer(metaType) || isValueTypeWithCopyConstructorOnly(metaType))
+ result += QLatin1String("isPythonToCppPointerConvertible");
+ else if (metaType.referenceType() == LValueReference)
+ result += QLatin1String("isPythonToCppReferenceConvertible");
+ else
+ result += QLatin1String("isPythonToCppValueConvertible");
+ result += QLatin1String("(reinterpret_cast<SbkObjectType *>(")
+ + cpythonTypeNameExt(metaType) + QLatin1String("), ");
+ return result;
+ }
+ result += QLatin1String("isPythonToCppConvertible(") + 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 += QLatin1String(", ") + QString::number(dim1)
+ + QLatin1String(", ") + QString::number(dim2);
+ }
+ result += QLatin1String(", ");
+ return result;
+}
+
+QString ShibokenGenerator::cpythonIsConvertibleFunction(const AbstractMetaArgument &metaArg, bool genericNumberType)
+{
+ return cpythonIsConvertibleFunction(metaArg.type(), genericNumberType);
+}
+
+QString ShibokenGenerator::cpythonToCppConversionFunction(const AbstractMetaClass *metaClass)
+{
+ return QLatin1String("Shiboken::Conversions::pythonToCppPointer(reinterpret_cast<SbkObjectType *>(")
+ + cpythonTypeNameExt(metaClass->typeEntry()) + QLatin1String("), ");
+}
+
+QString ShibokenGenerator::cpythonToCppConversionFunction(const AbstractMetaType &type,
+ const AbstractMetaClass * /* context */)
+{
+ if (isWrapperType(type)) {
+ return QLatin1String("Shiboken::Conversions::pythonToCpp")
+ + (isPointer(type) ? QLatin1String("Pointer") : QLatin1String("Copy"))
+ + QLatin1String("(reinterpret_cast<SbkObjectType *>(")
+ + cpythonTypeNameExt(type) + QLatin1String("), ");
+ }
+ return QStringLiteral("Shiboken::Conversions::pythonToCppCopy(%1, ")
+ .arg(converterObject(type));
+}
+
+QString ShibokenGenerator::cpythonToPythonConversionFunction(const AbstractMetaType &type,
+ const AbstractMetaClass * /* context */)
+{
+ if (isWrapperType(type)) {
+ QString conversion;
+ if (type.referenceType() == LValueReference && !(type.isValue() && type.isConstant()) && !isPointer(type))
+ conversion = QLatin1String("reference");
+ else if (type.isValue() || type.isSmartPointer())
+ conversion = QLatin1String("copy");
+ else
+ conversion = QLatin1String("pointer");
+ QString result = QLatin1String("Shiboken::Conversions::") + conversion
+ + QLatin1String("ToPython(reinterpret_cast<SbkObjectType *>(")
+ + cpythonTypeNameExt(type) + QLatin1String("), ");
+ if (conversion != QLatin1String("pointer"))
+ result += QLatin1Char('&');
+ return result;
+ }
+ return QStringLiteral("Shiboken::Conversions::copyToPython(%1, %2")
+ .arg(converterObject(type),
+ (isCString(type) || isVoidPointer(type)) ? QString() : QLatin1String("&"));
+}
+
+QString ShibokenGenerator::cpythonToPythonConversionFunction(const AbstractMetaClass *metaClass)
+{
+ return cpythonToPythonConversionFunction(metaClass->typeEntry());
+}
+
+QString ShibokenGenerator::cpythonToPythonConversionFunction(const TypeEntry *type)
+{
+ if (isWrapperType(type)) {
+ const QString conversion = type->isValue() ? QLatin1String("copy") : QLatin1String("pointer");
+ QString result = QLatin1String("Shiboken::Conversions::") + conversion
+ + QLatin1String("ToPython(reinterpret_cast<SbkObjectType *>(") + cpythonTypeNameExt(type)
+ + QLatin1String("), ");
+ if (conversion != QLatin1String("pointer"))
+ result += QLatin1Char('&');
+ return result;
+ }
+
+ return QStringLiteral("Shiboken::Conversions::copyToPython(%1, &").arg(converterObject(type));
+}
+
+QString ShibokenGenerator::argumentString(const AbstractMetaFunction *func,
+ const AbstractMetaArgument &argument,
+ Options options) const
+{
+ QString modified_type;
+ if (!(options & OriginalTypeDescription))
+ modified_type = func->typeReplaced(argument.argumentIndex() + 1);
+ QString arg;
+
+ if (modified_type.isEmpty())
+ arg = translateType(argument.type(), func->implementingClass(), options);
+ else
+ arg = modified_type.replace(QLatin1Char('$'), QLatin1Char('.'));
+
+ if (!(options & Generator::SkipName)) {
+ // "int a", "int a[]"
+ const int arrayPos = arg.indexOf(QLatin1Char('['));
+ if (arrayPos != -1)
+ arg.insert(arrayPos, QLatin1Char(' ') + argument.name());
+ else
+ arg.append(QLatin1Char(' ') + argument.name());
+ }
+
+ if ((options & Generator::SkipDefaultValues) != Generator::SkipDefaultValues &&
+ !argument.originalDefaultValueExpression().isEmpty())
+ {
+ QString default_value = argument.originalDefaultValueExpression();
+ if (default_value == QLatin1String("NULL"))
+ default_value = QLatin1String(NULL_PTR);
+
+ //WORKAROUND: fix this please
+ if (default_value.startsWith(QLatin1String("new ")))
+ default_value.remove(0, 4);
+
+ arg += QLatin1String(" = ") + default_value;
+ }
+
+ return arg;
+}
+
+void ShibokenGenerator::writeArgument(QTextStream &s,
+ const AbstractMetaFunction *func,
+ const AbstractMetaArgument &argument,
+ Options options) const
+{
+ s << argumentString(func, argument, options);
+}
+
+void ShibokenGenerator::writeFunctionArguments(QTextStream &s,
+ const AbstractMetaFunction *func,
+ Options options) const
+{
+ AbstractMetaArgumentList arguments = func->arguments();
+
+ if (options & Generator::WriteSelf) {
+ s << func->implementingClass()->name() << '&';
+ if (!(options & SkipName))
+ s << " self";
+ }
+
+ int argUsed = 0;
+ for (int i = 0; i < arguments.size(); ++i) {
+ if ((options & Generator::SkipRemovedArguments) && func->argumentRemoved(i+1))
+ continue;
+
+ if ((options & Generator::WriteSelf) || argUsed != 0)
+ s << ", ";
+ writeArgument(s, func, arguments[i], options);
+ argUsed++;
+ }
+}
+
+GeneratorContext ShibokenGenerator::contextForClass(const AbstractMetaClass *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 AbstractMetaFunction *func, Options options) const
+{
+ QString modifiedReturnType = QString(func->typeReplaced(0));
+ if (!modifiedReturnType.isEmpty() && !(options & OriginalTypeDescription))
+ return modifiedReturnType;
+ return translateType(func->type(), func->implementingClass(), options);
+}
+
+QString ShibokenGenerator::functionSignature(const AbstractMetaFunction *func,
+ const QString &prepend,
+ const QString &append,
+ Options options,
+ int /* argCount */) const
+{
+ QString result;
+ QTextStream s(&result);
+ // The actual function
+ if (!(func->isEmptyFunction() ||
+ func->isNormal() ||
+ func->isSignal())) {
+ options |= Generator::SkipReturnType;
+ } else {
+ s << functionReturnType(func, options) << ' ';
+ }
+
+ // name
+ QString name(func->originalName());
+ if (func->isConstructor())
+ name = wrapperName(func->ownerClass());
+
+ s << prepend << name << append << '(';
+ writeFunctionArguments(s, func, options);
+ s << ')';
+
+ if (func->isConstant() && !(options & Generator::ExcludeMethodConst))
+ s << " const";
+
+ if (func->exceptionSpecification() == ExceptionSpecification::NoExcept)
+ s << " noexcept";
+
+ return result;
+}
+
+void ShibokenGenerator::writeArgumentNames(QTextStream &s,
+ const AbstractMetaFunction *func,
+ Options options) const
+{
+ const AbstractMetaArgumentList arguments = func->arguments();
+ int argCount = 0;
+ for (auto argument : arguments) {
+ const int index = argument.argumentIndex() + 1;
+ if ((options & Generator::SkipRemovedArguments) && (func->argumentRemoved(index)))
+ continue;
+
+ s << ((argCount > 0) ? ", " : "") << argument.name();
+
+ if (((options & Generator::VirtualCall) == 0)
+ && (!func->conversionRule(TypeSystem::NativeCode, index).isEmpty()
+ || !func->conversionRule(TypeSystem::TargetLangCode, index).isEmpty())
+ && !func->isConstructor()) {
+ s << CONV_RULE_OUT_VAR_SUFFIX;
+ }
+
+ argCount++;
+ }
+}
+
+void ShibokenGenerator::writeFunctionCall(QTextStream &s,
+ const AbstractMetaFunction *func,
+ Options options) const
+{
+ if (!(options & Generator::SkipName))
+ s << (func->isConstructor() ? func->ownerClass()->qualifiedCppName() : func->originalName());
+ s << '(';
+ writeArgumentNames(s, func, options);
+ s << ')';
+}
+
+void ShibokenGenerator::writeUnusedVariableCast(QTextStream &s, const QString &variableName)
+{
+ s << INDENT << "SBK_UNUSED(" << variableName<< ")\n";
+}
+
+static bool filterFunction(const AbstractMetaFunction *func, bool avoidProtectedHack)
+{
+ switch (func->functionType()) {
+ case AbstractMetaFunction::DestructorFunction:
+ case AbstractMetaFunction::SignalFunction:
+ case AbstractMetaFunction::GetAttroFunction:
+ case AbstractMetaFunction::SetAttroFunction:
+ return false;
+ default:
+ break;
+ }
+ if (func->usesRValueReferences())
+ return false;
+ if (func->isModifiedRemoved() && !func->isAbstract()
+ && (!avoidProtectedHack || !func->isProtected())) {
+ return false;
+ }
+ return true;
+}
+
+AbstractMetaFunctionList ShibokenGenerator::filterFunctions(const AbstractMetaClass *metaClass)
+{
+ AbstractMetaFunctionList result;
+ const AbstractMetaFunctionList &funcs = metaClass->functions();
+ result.reserve(funcs.size());
+ for (AbstractMetaFunction *func : funcs) {
+ if (filterFunction(func, avoidProtectedHack()))
+ result.append(func);
+ }
+ return result;
+}
+
+ShibokenGenerator::ExtendedConverterData ShibokenGenerator::getExtendedConverters() const
+{
+ ExtendedConverterData extConvs;
+ for (const AbstractMetaClass *metaClass : classes()) {
+ // Use only the classes for the current module.
+ if (!shouldGenerate(metaClass))
+ continue;
+ const AbstractMetaFunctionList &overloads = metaClass->operatorOverloads(AbstractMetaClass::ConversionOp);
+ for (AbstractMetaFunction *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 TypeEntry *convType = convOp->type().typeEntry();
+ if (convType->generateCode() || !convType->isValue()
+ || convOp->isModifiedRemoved())
+ continue;
+ extConvs[convType].append(convOp->ownerClass());
+ }
+ }
+ return extConvs;
+}
+
+QVector<const CustomConversion *> ShibokenGenerator::getPrimitiveCustomConversions()
+{
+ QVector<const CustomConversion *> conversions;
+ const PrimitiveTypeEntryList &primitiveTypeList = primitiveTypes();
+ for (const PrimitiveTypeEntry *type : primitiveTypeList) {
+ if (!shouldGenerateTypeEntry(type) || !isUserPrimitive(type) || !type->customConversion())
+ continue;
+
+ 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");
+ int pos = str.indexOf(funcCall);
+ if (pos == -1)
+ return QString();
+ pos = pos + funcCall.size();
+ while (str.at(pos) == QLatin1Char(' ') || str.at(pos) == QLatin1Char('\t'))
+ ++pos;
+ if (str.at(pos) == QLatin1Char('('))
+ ++pos;
+ int begin = pos;
+ int counter = 1;
+ while (counter != 0) {
+ if (str.at(pos) == QLatin1Char('('))
+ ++counter;
+ else if (str.at(pos) == QLatin1Char(')'))
+ --counter;
+ ++pos;
+ }
+ return str.mid(begin, pos-begin-1);
+}
+
+QString ShibokenGenerator::getCodeSnippets(const CodeSnipList &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language)
+{
+ QString code;
+ QTextStream c(&code);
+ for (const CodeSnip &snip : codeSnips) {
+ if ((position != TypeSystem::CodeSnipPositionAny && snip.position != position) || !(snip.language & language))
+ continue;
+ QString snipCode;
+ QTextStream sc(&snipCode);
+ formatCode(sc, snip.code(), INDENT);
+ c << snipCode;
+ }
+ return code;
+}
+
+void ShibokenGenerator::processClassCodeSnip(QString &code, const GeneratorContext &context)
+{
+ auto metaClass = context.metaClass();
+ // Replace template variable by the Python Type object
+ // for the class context in which the variable is used.
+ code.replace(QLatin1String("%PYTHONTYPEOBJECT"),
+ cpythonTypeName(metaClass) + QLatin1String("->type"));
+ const QString className = context.useWrapper()
+ ? context.wrapperName() : metaClass->qualifiedCppName();
+ code.replace(QLatin1String("%TYPE"), className);
+ code.replace(QLatin1String("%CPPTYPE"), metaClass->name());
+
+ processCodeSnip(code);
+}
+
+void ShibokenGenerator::processCodeSnip(QString &code)
+{
+ // replace "toPython" converters
+ replaceConvertToPythonTypeSystemVariable(code);
+
+ // replace "toCpp" converters
+ replaceConvertToCppTypeSystemVariable(code);
+
+ // replace "isConvertible" check
+ replaceIsConvertibleToCppTypeSystemVariable(code);
+
+ // replace "checkType" check
+ replaceTypeCheckTypeSystemVariable(code);
+}
+
+ShibokenGenerator::ArgumentVarReplacementList ShibokenGenerator::getArgumentReplacement(const AbstractMetaFunction *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 (int i = 0; i < func->arguments().size(); ++i) {
+ const AbstractMetaArgument &arg = func->arguments().at(i);
+ QString argValue;
+ if (language == TypeSystem::TargetLangCode) {
+ bool hasConversionRule = !func->conversionRule(convLang, i+1).isEmpty();
+ const bool argRemoved = func->argumentRemoved(i+1);
+ if (argRemoved)
+ ++removed;
+ if (argRemoved && hasConversionRule)
+ argValue = arg.name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX);
+ else if (argRemoved || (lastArg && arg.argumentIndex() > lastArg->argumentIndex()))
+ argValue = QLatin1String(CPP_ARG_REMOVED) + QString::number(i);
+ if (!argRemoved && argValue.isEmpty()) {
+ int argPos = i - removed;
+ AbstractMetaType type = arg.type();
+ QString typeReplaced = func->typeReplaced(arg.argumentIndex() + 1);
+ if (!typeReplaced.isEmpty()) {
+ AbstractMetaType builtType = buildAbstractMetaTypeFromString(typeReplaced);
+ if (builtType)
+ type = builtType;
+ }
+ if (type.typeEntry()->isCustom()) {
+ argValue = usePyArgs
+ ? pythonArgsAt(argPos) : QLatin1String(PYTHON_ARG);
+ } else {
+ argValue = hasConversionRule
+ ? arg.name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX)
+ : QLatin1String(CPP_ARG) + QString::number(argPos);
+ if (isWrapperType(type)) {
+ if (type.referenceType() == LValueReference && !isPointer(type))
+ argValue.prepend(QLatin1Char('*'));
+ }
+ }
+ }
+ } else {
+ argValue = arg.name();
+ }
+ if (!argValue.isEmpty())
+ argReplacements << ArgumentVarReplacementPair(arg, argValue);
+
+ }
+ return argReplacements;
+}
+
+void ShibokenGenerator::writeClassCodeSnips(QTextStream &s,
+ const CodeSnipList &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language,
+ const GeneratorContext &context)
+{
+ QString code = getCodeSnippets(codeSnips, position, language);
+ if (code.isEmpty())
+ return;
+ processClassCodeSnip(code, context);
+ s << INDENT << "// Begin code injection\n";
+ s << code;
+ s << INDENT << "// End of code injection\n\n";
+}
+
+void ShibokenGenerator::writeCodeSnips(QTextStream &s,
+ const CodeSnipList &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language)
+{
+ QString code = getCodeSnippets(codeSnips, position, language);
+ if (code.isEmpty())
+ return;
+ processCodeSnip(code);
+ s << INDENT << "// Begin code injection\n";
+ s << code;
+ s << INDENT << "// End of code injection\n\n";
+}
+
+void ShibokenGenerator::writeCodeSnips(QTextStream &s,
+ const CodeSnipList &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language,
+ const AbstractMetaFunction *func,
+ const AbstractMetaArgument *lastArg)
+{
+ QString code = getCodeSnippets(codeSnips, position, language);
+ if (code.isEmpty())
+ return;
+
+ // Calculate the real number of arguments.
+ int argsRemoved = 0;
+ for (int i = 0; i < func->arguments().size(); i++) {
+ if (func->argumentRemoved(i+1))
+ argsRemoved++;
+ }
+
+ const auto &groups = func->implementingClass()
+ ? getFunctionGroups(func->implementingClass())
+ : getGlobalFunctionGroups();
+ OverloadData od(groups[func->name()], this);
+ bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(od);
+
+ // Replace %PYARG_# variables.
+ code.replace(QLatin1String("%PYARG_0"), QLatin1String(PYTHON_RETURN_VAR));
+
+ static const QRegularExpression pyArgsRegex(QStringLiteral("%PYARG_(\\d+)"));
+ Q_ASSERT(pyArgsRegex.isValid());
+ if (language == TypeSystem::TargetLangCode) {
+ if (usePyArgs) {
+ code.replace(pyArgsRegex, QLatin1String(PYTHON_ARGS) + QLatin1String("[\\1-1]"));
+ } else {
+ static const QRegularExpression pyArgsRegexCheck(QStringLiteral("%PYARG_([2-9]+)"));
+ Q_ASSERT(pyArgsRegexCheck.isValid());
+ const QRegularExpressionMatch match = pyArgsRegexCheck.match(code);
+ if (match.hasMatch()) {
+ qCWarning(lcShiboken).noquote().nospace()
+ << msgWrongIndex("%PYARG", match.captured(1), func);
+ return;
+ }
+ code.replace(QLatin1String("%PYARG_1"), QLatin1String(PYTHON_ARG));
+ }
+ } else {
+ // Replaces the simplest case of attribution to a
+ // Python argument on the binding virtual method.
+ static const QRegularExpression pyArgsAttributionRegex(QStringLiteral("%PYARG_(\\d+)\\s*=[^=]\\s*([^;]+)"));
+ Q_ASSERT(pyArgsAttributionRegex.isValid());
+ code.replace(pyArgsAttributionRegex, QLatin1String("PyTuple_SET_ITEM(")
+ + QLatin1String(PYTHON_ARGS) + QLatin1String(", \\1-1, \\2)"));
+ code.replace(pyArgsRegex, QLatin1String("PyTuple_GET_ITEM(")
+ + QLatin1String(PYTHON_ARGS) + QLatin1String(", \\1-1)"));
+ }
+
+ // Replace %ARG#_TYPE variables.
+ const AbstractMetaArgumentList &arguments = func->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+ QString argTypeVar = QStringLiteral("%ARG%1_TYPE").arg(arg.argumentIndex() + 1);
+ QString argTypeVal = arg.type().cppSignature();
+ code.replace(argTypeVar, argTypeVal);
+ }
+
+ static const QRegularExpression cppArgTypeRegexCheck(QStringLiteral("%ARG(\\d+)_TYPE"));
+ 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);
+ }
+
+ // Replace template variable for return variable name.
+ if (func->isConstructor()) {
+ code.replace(QLatin1String("%0."), QLatin1String("cptr->"));
+ code.replace(QLatin1String("%0"), QLatin1String("cptr"));
+ } else if (!func->isVoid()) {
+ QString returnValueOp = isPointerToWrapperType(func->type())
+ ? QLatin1String("%1->") : QLatin1String("%1.");
+ if (ShibokenGenerator::isWrapperType(func->type()))
+ code.replace(QLatin1String("%0."), returnValueOp.arg(QLatin1String(CPP_RETURN_VAR)));
+ code.replace(QLatin1String("%0"), QLatin1String(CPP_RETURN_VAR));
+ }
+
+ // Replace template variable for self Python object.
+ QString pySelf = language == TypeSystem::NativeCode
+ ? QLatin1String("pySelf") : QLatin1String("self");
+ code.replace(QLatin1String("%PYSELF"), pySelf);
+
+ // Replace template variable for a pointer to C++ of this object.
+ if (func->implementingClass()) {
+ QString replacement = func->isStatic() ? QLatin1String("%1::") : QLatin1String("%1->");
+ QString cppSelf;
+ if (func->isStatic())
+ cppSelf = func->ownerClass()->qualifiedCppName();
+ else if (language == TypeSystem::NativeCode)
+ cppSelf = QLatin1String("this");
+ else
+ cppSelf = QLatin1String(CPP_SELF_VAR);
+
+ // On comparison operator CPP_SELF_VAR is always a reference.
+ if (func->isComparisonOperator())
+ replacement = QLatin1String("%1.");
+
+ if (func->isVirtual() && !func->isAbstract() && (!avoidProtectedHack() || !func->isProtected())) {
+ QString methodCallArgs = getArgumentsFromMethodCall(code);
+ if (!methodCallArgs.isEmpty()) {
+ const QString pattern = QStringLiteral("%CPPSELF.%FUNCTION_NAME(%1)").arg(methodCallArgs);
+ if (func->name() == QLatin1String("metaObject")) {
+ QString wrapperClassName = wrapperName(func->ownerClass());
+ QString cppSelfVar = avoidProtectedHack()
+ ? QLatin1String("%CPPSELF")
+ : QStringLiteral("reinterpret_cast<%1 *>(%CPPSELF)").arg(wrapperClassName);
+ code.replace(pattern,
+ QString::fromLatin1("(Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(%1))"
+ " ? %2->::%3::%FUNCTION_NAME(%4)"
+ " : %CPPSELF.%FUNCTION_NAME(%4))").arg(pySelf, cppSelfVar, wrapperClassName, methodCallArgs));
+ } else {
+ code.replace(pattern,
+ QString::fromLatin1("(Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(%1))"
+ " ? %CPPSELF->::%TYPE::%FUNCTION_NAME(%2)"
+ " : %CPPSELF.%FUNCTION_NAME(%2))").arg(pySelf, methodCallArgs));
+ }
+ }
+ }
+
+ code.replace(QLatin1String("%CPPSELF."), replacement.arg(cppSelf));
+ code.replace(QLatin1String("%CPPSELF"), cppSelf);
+
+ if (code.indexOf(QLatin1String("%BEGIN_ALLOW_THREADS")) > -1) {
+ if (code.count(QLatin1String("%BEGIN_ALLOW_THREADS")) == code.count(QLatin1String("%END_ALLOW_THREADS"))) {
+ code.replace(QLatin1String("%BEGIN_ALLOW_THREADS"), QLatin1String(BEGIN_ALLOW_THREADS));
+ code.replace(QLatin1String("%END_ALLOW_THREADS"), QLatin1String(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(QLatin1String("%PYTHONTYPEOBJECT"),
+ cpythonTypeName(func->implementingClass()) + QLatin1String("->type"));
+ } else {
+ code.replace(QLatin1String("%PYTHONTYPEOBJECT."), pySelf + QLatin1String("->ob_type->"));
+ code.replace(QLatin1String("%PYTHONTYPEOBJECT"), pySelf + QLatin1String("->ob_type"));
+ }
+ }
+
+ // 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(QLatin1String(CPP_ARG_REMOVED)))
+ continue;
+ args << pair.second;
+ }
+ code.replace(QLatin1String("%ARGUMENT_NAMES"), args.join(QLatin1String(", ")));
+
+ for (const ArgumentVarReplacementPair &pair : argReplacements) {
+ const AbstractMetaArgument &arg = pair.first;
+ int idx = arg.argumentIndex() + 1;
+ AbstractMetaType type = arg.type();
+ QString typeReplaced = func->typeReplaced(arg.argumentIndex() + 1);
+ if (!typeReplaced.isEmpty()) {
+ AbstractMetaType builtType = buildAbstractMetaTypeFromString(typeReplaced);
+ if (builtType)
+ type = builtType;
+ }
+ if (isWrapperType(type)) {
+ QString replacement = pair.second;
+ if (type.referenceType() == LValueReference && !isPointer(type))
+ replacement.remove(0, 1);
+ if (type.referenceType() == LValueReference || isPointer(type))
+ code.replace(QString::fromLatin1("%%1.").arg(idx), replacement + QLatin1String("->"));
+ }
+ code.replace(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(QLatin1String("%PYTHON_ARGUMENTS"), QLatin1String(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(QLatin1String("%PYTHON_METHOD_OVERRIDE"), QLatin1String(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 hasProtectedOverload = false;
+ if (func->isUserAdded()) {
+ const AbstractMetaFunctionList &funcs = getFunctionOverloads(func->ownerClass(), func->name());
+ for (const AbstractMetaFunction *f : funcs)
+ hasProtectedOverload |= f->isProtected();
+ }
+
+ if (func->isProtected() || hasProtectedOverload) {
+ code.replace(QLatin1String("%TYPE::%FUNCTION_NAME"),
+ QStringLiteral("%1::%2_protected")
+ .arg(wrapperName(func->ownerClass()), func->originalName()));
+ code.replace(QLatin1String("%FUNCTION_NAME"),
+ func->originalName() + QLatin1String("_protected"));
+ }
+ }
+
+ if (func->isConstructor() && shouldGenerateCppWrapper(func->ownerClass()))
+ code.replace(QLatin1String("%TYPE"), wrapperName(func->ownerClass()));
+
+ if (func->ownerClass())
+ code.replace(QLatin1String("%CPPTYPE"), func->ownerClass()->name());
+
+ replaceTemplateVariables(code, func);
+
+ processCodeSnip(code);
+ s << INDENT << "// Begin code injection\n";
+ s << code;
+ s << INDENT << "// 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(QStringLiteral("^\\s*\\*?\\s*[A-Za-z_][A-Za-z_0-9.]*\\s*(?:\\[[^\\[]+\\])*$"));
+ 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(QLatin1String("::")))
+ normalized.remove(0, 2);
+ QString suffix;
+ while (normalized.endsWith(QLatin1Char('*')) || normalized.endsWith(QLatin1Char('&'))) {
+ suffix.prepend(normalized.at(normalized.count() - 1));
+ normalized.chop(1);
+ normalized = normalized.trimmed();
+ }
+ const QString result = normalized + QLatin1Char(' ') + 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.count()) {
+ 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)
+ qFatal("Unbalanced parenthesis on type system converter variable call.");
+ return arg;
+}
+using StringPair = QPair<QString, QString>;
+
+void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVariable converterVariable, QString &code)
+{
+ QVector<StringPair> replacements;
+ QRegularExpressionMatchIterator rit = m_typeSystemConvRegEx[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 AbstractMetaType conversionType = buildAbstractMetaTypeFromString(conversionTypeName, &message);
+ if (!conversionType) {
+ qFatal("%s", qPrintable(msgCannotFindType(conversionTypeName,
+ m_typeSystemConvName[converterVariable],
+ message)));
+ }
+ QString conversion;
+ QTextStream c(&conversion);
+ switch (converterVariable) {
+ case TypeSystemToCppFunction: {
+ int end = match.capturedStart();
+ int start = end;
+ while (start > 0 && code.at(start) != QLatin1Char('\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()) {
+ const QString conversionSignature = conversionType.cppSignature();
+ if (varType != QLatin1String("auto") && varType != conversionSignature)
+ qFatal("%s", qPrintable(msgConversionTypesDiffer(varType, conversionSignature)));
+ c << getFullTypeName(conversionType) << ' ' << varName;
+ writeMinimalConstructorExpression(c, conversionType);
+ c << ";\n";
+ Indentation indent(INDENT);
+ c << INDENT;
+ }
+ c << cpythonToCppConversionFunction(conversionType);
+ QString prefix;
+ if (varName.startsWith(QLatin1Char('*'))) {
+ varName.remove(0, 1);
+ varName = varName.trimmed();
+ } else {
+ prefix = QLatin1Char('&');
+ }
+ QString arg = getConverterTypeSystemVariableArgument(code, match.capturedEnd());
+ conversionString += arg;
+ c << arg << ", " << prefix << '(' << varName << ')';
+ break;
+ }
+ case TypeSystemCheckFunction:
+ conversion = cpythonCheckFunction(conversionType);
+ if (conversionType.typeEntry()->isPrimitive()
+ && (conversionType.typeEntry()->name() == QLatin1String("PyObject")
+ || !conversion.endsWith(QLatin1Char(' ')))) {
+ c << '(';
+ 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)) {
+ qFatal("Only variables are acceptable as argument to %%CONVERTTOPYTHON type system variable on code snippet: '%s'",
+ qPrintable(code));
+ }
+ if (conversion.contains(QLatin1String("%in"))) {
+ conversion.prepend(QLatin1Char('('));
+ conversion.replace(QLatin1String("%in"), arg);
+ } else {
+ c << arg;
+ }
+ }
+ }
+ replacements.append(qMakePair(conversionString, conversion));
+ }
+ for (const StringPair &rep : qAsConst(replacements))
+ code.replace(rep.first, rep.second);
+}
+
+bool ShibokenGenerator::injectedCodeUsesPySelf(const AbstractMetaFunction *func)
+{
+ CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::NativeCode);
+ for (const CodeSnip &snip : qAsConst(snips)) {
+ if (snip.code().contains(QLatin1String("%PYSELF")))
+ return true;
+ }
+ return false;
+}
+
+bool ShibokenGenerator::injectedCodeCallsCppFunction(const GeneratorContext &context,
+ const AbstractMetaFunction *func)
+{
+ QString funcCall = func->originalName() + QLatin1Char('(');
+ QString wrappedCtorCall;
+ if (func->isConstructor()) {
+ funcCall.prepend(QLatin1String("new "));
+ const auto owner = func->ownerClass();
+ const QString className = context.useWrapper()
+ ? context.wrapperName() : owner->qualifiedCppName();
+ wrappedCtorCall = QLatin1String("new ") + className + QLatin1Char('(');
+ }
+ CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode);
+ for (const CodeSnip &snip : qAsConst(snips)) {
+ if (snip.code().contains(QLatin1String("%FUNCTION_NAME(")) || snip.code().contains(funcCall)
+ || (func->isConstructor()
+ && ((func->ownerClass()->isPolymorphic() && snip.code().contains(wrappedCtorCall))
+ || snip.code().contains(QLatin1String("new %TYPE("))))
+ )
+ return true;
+ }
+ return false;
+}
+
+bool ShibokenGenerator::injectedCodeCallsPythonOverride(const AbstractMetaFunction *func)
+{
+ static const QRegularExpression overrideCallRegexCheck(QStringLiteral("PyObject_Call\\s*\\(\\s*%PYTHON_METHOD_OVERRIDE\\s*,"));
+ Q_ASSERT(overrideCallRegexCheck.isValid());
+ CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::NativeCode);
+ for (const CodeSnip &snip : qAsConst(snips)) {
+ if (snip.code().contains(overrideCallRegexCheck))
+ return true;
+ }
+ return false;
+}
+
+bool ShibokenGenerator::injectedCodeHasReturnValueAttribution(const AbstractMetaFunction *func, TypeSystem::Language language)
+{
+ static const QRegularExpression retValAttributionRegexCheck_native(QStringLiteral("%0\\s*=[^=]\\s*.+"));
+ Q_ASSERT(retValAttributionRegexCheck_native.isValid());
+ static const QRegularExpression retValAttributionRegexCheck_target(QStringLiteral("%PYARG_0\\s*=[^=]\\s*.+"));
+ Q_ASSERT(retValAttributionRegexCheck_target.isValid());
+ CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, language);
+ for (const CodeSnip &snip : qAsConst(snips)) {
+ if (language == TypeSystem::TargetLangCode) {
+ if (snip.code().contains(retValAttributionRegexCheck_target))
+ return true;
+ } else {
+ if (snip.code().contains(retValAttributionRegexCheck_native))
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ShibokenGenerator::injectedCodeUsesArgument(const AbstractMetaFunction *func, int argumentIndex)
+{
+ CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny);
+ const QRegularExpression argRegEx = placeHolderRegex(argumentIndex + 1);
+ for (const CodeSnip &snip : qAsConst(snips)) {
+ QString code = snip.code();
+ if (code.contains(QLatin1String("%ARGUMENT_NAMES")) || code.contains(argRegEx))
+ return true;
+ }
+ return false;
+}
+
+bool ShibokenGenerator::useOverrideCaching(const AbstractMetaClass *metaClass)
+{
+ return metaClass->isPolymorphic();
+}
+
+ShibokenGenerator::AttroCheck ShibokenGenerator::checkAttroFunctionNeeds(const AbstractMetaClass *metaClass) const
+{
+ AttroCheck result;
+ if (metaClass->typeEntry()->isSmartPointer()) {
+ result |= AttroCheckFlag::GetattroSmartPointer | AttroCheckFlag::SetattroSmartPointer;
+ } else {
+ if (getGeneratorClassInfo(metaClass).needsGetattroFunction)
+ result |= AttroCheckFlag::GetattroOverloads;
+ if (metaClass->queryFirstFunction(metaClass->functions(),
+ AbstractMetaClass::GetAttroFunction)) {
+ result |= AttroCheckFlag::GetattroUser;
+ }
+ if (usePySideExtensions() && metaClass->qualifiedCppName() == qObjectT())
+ result |= AttroCheckFlag::SetattroQObject;
+ if (useOverrideCaching(metaClass))
+ result |= AttroCheckFlag::SetattroMethodOverride;
+ if (metaClass->queryFirstFunction(metaClass->functions(),
+ AbstractMetaClass::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)
+ && metaClass->isQObject()) {
+ result |= AttroCheckFlag::SetattroQObject;
+ }
+ }
+ return result;
+}
+
+bool ShibokenGenerator::classNeedsGetattroFunctionImpl(const AbstractMetaClass *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) {
+ AbstractMetaFunctionList overloads;
+ for (AbstractMetaFunction *func : qAsConst(it.value())) {
+ if (func->isAssignmentOperator() || func->isCastOperator() || 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;
+}
+
+AbstractMetaFunctionList ShibokenGenerator::getMethodsWithBothStaticAndNonStaticMethods(const AbstractMetaClass *metaClass)
+{
+ AbstractMetaFunctionList methods;
+ if (metaClass) {
+ const auto &functionGroups = getFunctionGroups(metaClass);
+ for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) {
+ AbstractMetaFunctionList overloads;
+ for (AbstractMetaFunction *func : qAsConst(it.value())) {
+ if (func->isAssignmentOperator() || func->isCastOperator() || 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;
+}
+
+AbstractMetaClassList ShibokenGenerator::getBaseClasses(const AbstractMetaClass *metaClass) const
+{
+ AbstractMetaClassList baseClasses;
+ if (metaClass) {
+ QStringList baseClassNames(metaClass->baseClassNames());
+ const QString defaultSuperclass = metaClass->typeEntry()->defaultSuperclass();
+ if (!defaultSuperclass.isEmpty()) {
+ int index = baseClassNames.indexOf(defaultSuperclass);
+ if (index >= 0)
+ baseClassNames.move(index, 0);
+ }
+ for (const QString &parent : baseClassNames) {
+ AbstractMetaClass *clazz = AbstractMetaClass::findClass(classes(), parent);
+ if (clazz)
+ baseClasses << clazz;
+ }
+ }
+ return baseClasses;
+}
+
+const AbstractMetaClass *ShibokenGenerator::getMultipleInheritingClass(const AbstractMetaClass *metaClass)
+{
+ if (!metaClass || metaClass->baseClassNames().isEmpty())
+ return nullptr;
+ if (metaClass->baseClassNames().size() > 1)
+ return metaClass;
+ return getMultipleInheritingClass(metaClass->baseClass());
+}
+
+AbstractMetaClassList ShibokenGenerator::getAllAncestors(const AbstractMetaClass *metaClass) const
+{
+ AbstractMetaClassList result;
+ if (metaClass) {
+ AbstractMetaClassList baseClasses = getBaseClasses(metaClass);
+ for (AbstractMetaClass *base : qAsConst(baseClasses)) {
+ result.append(base);
+ result.append(getAllAncestors(base));
+ }
+ }
+ return result;
+}
+
+QString ShibokenGenerator::getModuleHeaderFileName(const QString &moduleName) const
+{
+ return moduleCppPrefix(moduleName).toLower() + QLatin1String("_python.h");
+}
+
+bool ShibokenGenerator::isCopyable(const AbstractMetaClass *metaClass)
+
+{
+ if (metaClass->isNamespace() || isObjectType(metaClass))
+ return false;
+ if (metaClass->typeEntry()->copyable() == ComplexTypeEntry::Unknown)
+ return metaClass->hasCloneOperator();
+
+ return metaClass->typeEntry()->copyable() == ComplexTypeEntry::CopyableSet;
+}
+
+AbstractMetaType ShibokenGenerator::buildAbstractMetaTypeFromString(QString typeSignature,
+ QString *errorMessage)
+{
+ typeSignature = typeSignature.trimmed();
+ if (typeSignature.startsWith(QLatin1String("::")))
+ typeSignature.remove(0, 2);
+
+ auto it = m_metaTypeFromStringCache.find(typeSignature);
+ if (it == m_metaTypeFromStringCache.end()) {
+ AbstractMetaType metaType =
+ AbstractMetaBuilder::translateType(typeSignature, nullptr, {}, errorMessage);
+ if (Q_UNLIKELY(!metaType)) {
+ if (errorMessage)
+ errorMessage->prepend(msgCannotBuildMetaType(typeSignature));
+ return {};
+ }
+ it = m_metaTypeFromStringCache.insert(typeSignature, metaType);
+ }
+ return it.value();
+}
+
+AbstractMetaType ShibokenGenerator::buildAbstractMetaTypeFromTypeEntry(const TypeEntry *typeEntry)
+{
+ QString typeName = typeEntry->qualifiedCppName();
+ if (typeName.startsWith(QLatin1String("::")))
+ typeName.remove(0, 2);
+ if (m_metaTypeFromStringCache.contains(typeName))
+ return m_metaTypeFromStringCache.value(typeName);
+ AbstractMetaType metaType(typeEntry);
+ metaType.clearIndirections();
+ metaType.setReferenceType(NoReference);
+ metaType.setConstant(false);
+ metaType.decideUsagePattern();
+ m_metaTypeFromStringCache.insert(typeName, metaType);
+ return metaType;
+}
+AbstractMetaType ShibokenGenerator::buildAbstractMetaTypeFromAbstractMetaClass(const AbstractMetaClass *metaClass)
+{
+ return ShibokenGenerator::buildAbstractMetaTypeFromTypeEntry(metaClass->typeEntry());
+}
+
+/*
+static void dumpFunction(AbstractMetaFunctionList lst)
+{
+ qDebug() << "DUMP FUNCTIONS: ";
+ for (AbstractMetaFunction *func : qAsConst(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 AbstractMetaFunction *func)
+{
+ switch (func->functionType()) {
+ case AbstractMetaFunction::DestructorFunction:
+ case AbstractMetaFunction::SignalFunction:
+ case AbstractMetaFunction::GetAttroFunction:
+ case AbstractMetaFunction::SetAttroFunction:
+ return false;
+ default:
+ break;
+ }
+ if (func->isModifiedRemoved() && !func->isAbstract())
+ return false;
+ // weird operator overloads
+ if (func->name() == QLatin1String("operator[]") || func->name() == QLatin1String("operator->")) // FIXME: what about cast operators?
+ return false;
+ return true;
+}
+
+static void insertIntoFunctionGroups(const AbstractMetaFunctionList &lst,
+ ShibokenGenerator::FunctionGroups *results)
+{
+ for (AbstractMetaFunction *func : lst) {
+ if (isGroupable(func))
+ (*results)[func->name()].append(func);
+ }
+}
+
+ShibokenGenerator::FunctionGroups ShibokenGenerator::getGlobalFunctionGroups() const
+{
+ FunctionGroups results;
+ insertIntoFunctionGroups(globalFunctions(), &results);
+ for (auto nsp : invisibleTopNamespaces())
+ insertIntoFunctionGroups(nsp->functions(), &results);
+ return results;
+}
+
+const GeneratorClassInfoCacheEntry &ShibokenGenerator::getGeneratorClassInfo(const AbstractMetaClass *scope)
+{
+ auto cache = generatorClassInfoCache();
+ auto it = cache->find(scope);
+ if (it == cache->end()) {
+ it = cache->insert(scope, {});
+ it.value().functionGroups = getFunctionGroupsImpl(scope);
+ it.value().needsGetattroFunction = classNeedsGetattroFunctionImpl(scope);
+ }
+ return it.value();
+}
+
+ShibokenGenerator::FunctionGroups ShibokenGenerator::getFunctionGroups(const AbstractMetaClass *scope)
+{
+ Q_ASSERT(scope);
+ return getGeneratorClassInfo(scope).functionGroups;
+}
+
+ShibokenGenerator::FunctionGroups ShibokenGenerator::getFunctionGroupsImpl(const AbstractMetaClass *scope)
+{
+ AbstractMetaFunctionList lst = scope->functions();
+ scope->getFunctionsFromInvisibleNamespacesToBeGenerated(&lst);
+
+ FunctionGroups results;
+ for (AbstractMetaFunction *func : lst) {
+ if (isGroupable(func)) {
+ auto it = results.find(func->name());
+ if (it == results.end()) {
+ results.insert(func->name(), AbstractMetaFunctionList(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);
+ }
+ }
+ }
+ return results;
+}
+
+AbstractMetaFunctionList ShibokenGenerator::getInheritedOverloads(const AbstractMetaFunction *func, QSet<QString> *seen)
+{
+ AbstractMetaFunctionList results;
+ AbstractMetaClass *basis;
+ if (func->ownerClass() && (basis = func->ownerClass()->baseClass())) {
+ for (; basis; basis = basis->baseClass()) {
+ const AbstractMetaFunction *inFunc = basis->findFunction(func->name());
+ if (inFunc && !seen->contains(inFunc->minimalSignature())) {
+ seen->insert(inFunc->minimalSignature());
+ AbstractMetaFunction *newFunc = inFunc->copy();
+ newFunc->setImplementingClass(func->implementingClass());
+ results << newFunc;
+ }
+ }
+ }
+ return results;
+}
+
+AbstractMetaFunctionList ShibokenGenerator::getFunctionAndInheritedOverloads(const AbstractMetaFunction *func, QSet<QString> *seen)
+{
+ AbstractMetaFunctionList results;
+ seen->insert(func->minimalSignature());
+ results << const_cast<AbstractMetaFunction *>(func) << getInheritedOverloads(func, seen);
+ return results;
+}
+
+AbstractMetaFunctionList ShibokenGenerator::getFunctionOverloads(const AbstractMetaClass *scope, const QString &functionName)
+{
+ AbstractMetaFunctionList lst = scope ? scope->functions() : globalFunctions();
+
+ AbstractMetaFunctionList results;
+ QSet<QString> seenSignatures;
+ for (AbstractMetaFunction *func : qAsConst(lst)) {
+ if (func->name() != functionName)
+ continue;
+ if (isGroupable(func)) {
+ // PYSIDE-331: look also into base classes.
+ results << getFunctionAndInheritedOverloads(func, &seenSignatures);
+ }
+ }
+ return results;
+}
+
+Generator::OptionDescriptions ShibokenGenerator::options() const
+{
+ return OptionDescriptions()
+ << qMakePair(QLatin1String(AVOID_PROTECTED_HACK),
+ QLatin1String("Avoid the use of the '#define protected public' hack."))
+ << qMakePair(QLatin1String(DISABLE_VERBOSE_ERROR_MESSAGES),
+ QLatin1String("Disable verbose error messages. Turn the python code hard to debug\n"
+ "but safe few kB on the generated bindings."))
+ << qMakePair(QLatin1String(PARENT_CTOR_HEURISTIC),
+ QLatin1String("Enable heuristics to detect parent relationship on constructors."))
+ << qMakePair(QLatin1String(ENABLE_PYSIDE_EXTENSIONS),
+ QLatin1String("Enable PySide extensions, such as support for signal/slots,\n"
+ "use this if you are creating a binding for a Qt-based library."))
+ << qMakePair(QLatin1String(RETURN_VALUE_HEURISTIC),
+ QLatin1String("Enable heuristics to detect parent relationship on return values\n"
+ "(USE WITH CAUTION!)"))
+ << qMakePair(QLatin1String(USE_ISNULL_AS_NB_NONZERO),
+ QLatin1String("If a class have an isNull() const method, it will be used to compute\n"
+ "the value of boolean casts"))
+ << qMakePair(QLatin1String(WRAPPER_DIAGNOSTICS),
+ QLatin1String("Generate diagnostic code around wrappers"));
+}
+
+bool ShibokenGenerator::handleOption(const QString &key, const QString & /* value */)
+{
+ if (key == QLatin1String(PARENT_CTOR_HEURISTIC))
+ return (m_useCtorHeuristic = true);
+ if (key == QLatin1String(ENABLE_PYSIDE_EXTENSIONS))
+ return (m_usePySideExtensions = true);
+ if (key == QLatin1String(RETURN_VALUE_HEURISTIC))
+ return (m_userReturnValueHeuristic = true);
+ if (key == QLatin1String(DISABLE_VERBOSE_ERROR_MESSAGES))
+ return (m_verboseErrorMessagesDisabled = true);
+ if (key == QLatin1String(USE_ISNULL_AS_NB_NONZERO))
+ return (m_useIsNullAsNbNonZero = true);
+ if (key == QLatin1String(AVOID_PROTECTED_HACK))
+ return (m_avoidProtectedHack = true);
+ if (key == QLatin1String(WRAPPER_DIAGNOSTICS))
+ return (m_wrapperDiagnostics = true);
+ return false;
+}
+
+static void getCode(QStringList &code, const CodeSnipList &codeSnips)
+{
+ for (const CodeSnip &snip : qAsConst(codeSnips))
+ code.append(snip.code());
+}
+
+static void getCode(QStringList &code, const TypeEntry *type)
+{
+ getCode(code, type->codeSnips());
+
+ CustomConversion *customConversion = type->customConversion();
+ if (!customConversion)
+ return;
+
+ if (!customConversion->nativeToTargetConversion().isEmpty())
+ code.append(customConversion->nativeToTargetConversion());
+
+ const CustomConversion::TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions();
+ if (toCppConversions.isEmpty())
+ return;
+
+ for (CustomConversion::TargetToNativeConversion *toNative : qAsConst(toCppConversions))
+ code.append(toNative->conversion());
+}
+
+bool ShibokenGenerator::doSetup()
+{
+ QStringList snips;
+ const PrimitiveTypeEntryList &primitiveTypeList = primitiveTypes();
+ for (const PrimitiveTypeEntry *type : primitiveTypeList)
+ getCode(snips, type);
+ const ContainerTypeEntryList &containerTypeList = containerTypes();
+ for (const ContainerTypeEntry *type : containerTypeList)
+ getCode(snips, type);
+ for (const AbstractMetaClass *metaClass : classes())
+ getCode(snips, metaClass->typeEntry());
+
+ const TypeSystemTypeEntry *moduleEntry = TypeDatabase::instance()->defaultTypeSystemType();
+ Q_ASSERT(moduleEntry);
+ getCode(snips, moduleEntry);
+
+ const auto &functionGroups = getGlobalFunctionGroups();
+ for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) {
+ for (AbstractMetaFunction *func : it.value())
+ getCode(snips, func->injectedCodeSnips());
+ }
+
+ for (const QString &code : qAsConst(snips)) {
+ collectContainerTypesFromConverterMacros(code, true);
+ collectContainerTypesFromConverterMacros(code, false);
+ }
+
+ return true;
+}
+
+void ShibokenGenerator::collectContainerTypesFromConverterMacros(const QString &code, bool toPythonMacro)
+{
+ QString convMacro = toPythonMacro ? QLatin1String("%CONVERTTOPYTHON[") : QLatin1String("%CONVERTTOCPP[");
+ int offset = toPythonMacro ? sizeof("%CONVERTTOPYTHON") : sizeof("%CONVERTTOCPP");
+ int start = 0;
+ QString errorMessage;
+ while ((start = code.indexOf(convMacro, start)) != -1) {
+ int end = code.indexOf(QLatin1Char(']'), start);
+ start += offset;
+ if (code.at(start) != QLatin1Char('%')) {
+ QString typeString = code.mid(start, end - start);
+ if (AbstractMetaType type =
+ buildAbstractMetaTypeFromString(typeString, &errorMessage)) {
+ addInstantiatedContainersAndSmartPointers(type, type.originalTypeDescription());
+ } else {
+ qFatal("%s: Cannot translate type \"%s\": %s", __FUNCTION__,
+ qPrintable(typeString), qPrintable(errorMessage));
+ }
+ }
+ start = end;
+ }
+}
+
+bool ShibokenGenerator::useCtorHeuristic() const
+{
+ return m_useCtorHeuristic;
+}
+
+bool ShibokenGenerator::useReturnValueHeuristic() const
+{
+ return m_userReturnValueHeuristic;
+}
+
+bool ShibokenGenerator::usePySideExtensions() const
+{
+ return m_usePySideExtensions;
+}
+
+bool ShibokenGenerator::useIsNullAsNbNonZero() const
+{
+ return m_useIsNullAsNbNonZero;
+}
+
+bool ShibokenGenerator::avoidProtectedHack() const
+{
+ return m_avoidProtectedHack;
+}
+
+QString ShibokenGenerator::moduleCppPrefix(const QString &moduleName) const
+ {
+ QString result = moduleName.isEmpty() ? packageName() : moduleName;
+ result.replace(QLatin1Char('.'), QLatin1Char('_'));
+ return result;
+}
+
+QString ShibokenGenerator::cppApiVariableName(const QString &moduleName) const
+{
+ return QLatin1String("Sbk") + moduleCppPrefix(moduleName)
+ + QLatin1String("Types");
+}
+
+QString ShibokenGenerator::pythonModuleObjectName(const QString &moduleName) const
+{
+ return QLatin1String("Sbk") + moduleCppPrefix(moduleName)
+ + QLatin1String("ModuleObject");
+}
+
+QString ShibokenGenerator::convertersVariableName(const QString &moduleName) const
+{
+ QString result = cppApiVariableName(moduleName);
+ result.chop(1);
+ result.append(QLatin1String("Converters"));
+ return result;
+}
+
+static QString processInstantiationsVariableName(const AbstractMetaType &type)
+{
+ QString res = QLatin1Char('_') + _fixedCppTypeName(type.typeEntry()->qualifiedCppName()).toUpper();
+ for (const auto &instantiation : type.instantiations()) {
+ res += instantiation.isContainer()
+ ? processInstantiationsVariableName(instantiation)
+ : QLatin1Char('_') + _fixedCppTypeName(instantiation.cppSignature()).toUpper();
+ }
+ return res;
+}
+
+static void appendIndexSuffix(QString *s)
+{
+ if (!s->endsWith(QLatin1Char('_')))
+ s->append(QLatin1Char('_'));
+ s->append(QStringLiteral("IDX"));
+}
+
+QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaClass *metaClass,
+ bool alternativeTemplateName) const
+{
+ if (alternativeTemplateName) {
+ const AbstractMetaClass *templateBaseClass = metaClass->templateBaseClass();
+ if (!templateBaseClass)
+ return QString();
+ QString result = QLatin1String("SBK_")
+ + _fixedCppTypeName(templateBaseClass->typeEntry()->qualifiedCppName()).toUpper();
+ for (const auto &instantiation : metaClass->templateBaseClassInstantiations())
+ result += processInstantiationsVariableName(instantiation);
+ appendIndexSuffix(&result);
+ return result;
+ }
+ return getTypeIndexVariableName(metaClass->typeEntry());
+}
+QString ShibokenGenerator::getTypeIndexVariableName(const TypeEntry *type) const
+{
+ if (type->isCppPrimitive()) {
+ const auto *trueType = static_cast<const PrimitiveTypeEntry *>(type);
+ if (trueType->basicReferencedTypeEntry())
+ type = trueType->basicReferencedTypeEntry();
+ }
+ QString result = QLatin1String("SBK_");
+ // Disambiguate namespaces per module to allow for extending them.
+ if (type->isNamespace()) {
+ QString package = type->targetLangPackage();
+ const int dot = package.lastIndexOf(QLatin1Char('.'));
+ result += QStringView{package}.right(package.size() - (dot + 1));
+ }
+ result += _fixedCppTypeName(type->qualifiedCppName()).toUpper();
+ appendIndexSuffix(&result);
+ return result;
+}
+QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaType &type) const
+{
+ QString result = QLatin1String("SBK");
+ if (type.typeEntry()->isContainer())
+ result += QLatin1Char('_') + moduleName().toUpper();
+ result += processInstantiationsVariableName(type);
+ appendIndexSuffix(&result);
+ return result;
+}
+
+bool ShibokenGenerator::verboseErrorMessagesDisabled() const
+{
+ return m_verboseErrorMessagesDisabled;
+}
+
+bool ShibokenGenerator::pythonFunctionWrapperUsesListOfArguments(const OverloadData &overloadData)
+{
+ if (overloadData.referenceFunction()->isCallOperator())
+ return true;
+ if (overloadData.referenceFunction()->isOperatorOverload())
+ return false;
+ int maxArgs = overloadData.maxArgs();
+ int minArgs = overloadData.minArgs();
+ return (minArgs != maxArgs)
+ || (maxArgs > 1)
+ || overloadData.referenceFunction()->isConstructor()
+ || overloadData.hasArgumentWithDefaultValue();
+}
+
+void ShibokenGenerator::writeMinimalConstructorExpression(QTextStream &s,
+ const AbstractMetaType &type,
+ const QString &defaultCtor)
+{
+ if (!defaultCtor.isEmpty()) {
+ s << " = " << defaultCtor;
+ return;
+ }
+ if (isCppPrimitive(type) || type.isSmartPointer())
+ return;
+ const auto ctor = minimalConstructor(type);
+ if (ctor.isValid()) {
+ s << ctor.initialization();
+ } else {
+ const QString message = msgCouldNotFindMinimalConstructor(QLatin1String(__FUNCTION__), type.cppSignature());
+ qCWarning(lcShiboken()).noquote() << message;
+ s << ";\n#error " << message << '\n';
+ }
+}
+
+void ShibokenGenerator::writeMinimalConstructorExpression(QTextStream &s, const TypeEntry *type, const QString &defaultCtor)
+{
+ if (!defaultCtor.isEmpty()) {
+ s << " = " << defaultCtor;
+ return;
+ }
+ if (isCppPrimitive(type))
+ return;
+ const auto ctor = minimalConstructor(type);
+ if (ctor.isValid()) {
+ s << ctor.initialization();
+ } else {
+ const QString message = msgCouldNotFindMinimalConstructor(QLatin1String(__FUNCTION__), type->qualifiedCppName());
+ qCWarning(lcShiboken()).noquote() << message;
+ s << ";\n#error " << message << Qt::endl;
+ }
+}
+
+bool ShibokenGenerator::isCppIntegralPrimitive(const TypeEntry *type)
+{
+ if (!type->isCppPrimitive())
+ return false;
+ const auto *trueType = static_cast<const PrimitiveTypeEntry *>(type);
+ if (trueType->basicReferencedTypeEntry())
+ trueType = trueType->basicReferencedTypeEntry();
+ QString typeName = trueType->qualifiedCppName();
+ return !typeName.contains(QLatin1String("double"))
+ && !typeName.contains(QLatin1String("float"))
+ && !typeName.contains(QLatin1String("wchar"));
+}
+bool ShibokenGenerator::isCppIntegralPrimitive(const AbstractMetaType &type)
+{
+ return isCppIntegralPrimitive(type.typeEntry());
+}
+
+QString ShibokenGenerator::pythonArgsAt(int i)
+{
+ return QLatin1String(PYTHON_ARGS) + QLatin1Char('[')
+ + QString::number(i) + QLatin1Char(']');
+}
diff --git a/sources/shiboken6/generator/shiboken/shibokengenerator.h b/sources/shiboken6/generator/shiboken/shibokengenerator.h
new file mode 100644
index 000000000..0d49764ab
--- /dev/null
+++ b/sources/shiboken6/generator/shiboken/shibokengenerator.h
@@ -0,0 +1,585 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt for Python.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SHIBOKENGENERATOR_H
+#define SHIBOKENGENERATOR_H
+
+extern const char *CPP_ARG;
+extern const char *CPP_ARG_REMOVED;
+extern const char *CPP_RETURN_VAR;
+extern const char *CPP_SELF_VAR;
+extern const char *NULL_PTR;
+extern const char *PYTHON_ARG;
+extern const char *PYTHON_ARGS;
+extern const char *PYTHON_OVERRIDE_VAR;
+extern const char *PYTHON_RETURN_VAR;
+extern const char *PYTHON_TO_CPP_VAR;
+extern const char *SMART_POINTER_GETTER;
+
+extern const char *CONV_RULE_OUT_VAR_SUFFIX;
+extern const char *BEGIN_ALLOW_THREADS;
+extern const char *END_ALLOW_THREADS;
+
+#include <generator.h>
+
+#include "typesystem.h"
+
+#include <QtCore/QRegularExpression>
+
+class DocParser;
+class CodeSnip;
+class QPropertySpec;
+class OverloadData;
+struct GeneratorClassInfoCacheEntry;
+
+QT_FORWARD_DECLARE_CLASS(QTextStream)
+
+/**
+ * Abstract generator that contains common methods used in CppGenerator and HeaderGenerator.
+ */
+class ShibokenGenerator : public Generator
+{
+public:
+ 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, AbstractMetaFunctionList>; // Sorted
+
+ ShibokenGenerator();
+ ~ShibokenGenerator() override;
+
+ const char *name() const override { return "Shiboken"; }
+
+ /// Returns a list of all ancestor classes for the given class.
+ AbstractMetaClassList getAllAncestors(const AbstractMetaClass *metaClass) const;
+
+ /// Returns true if the user enabled PySide extensions.
+ bool usePySideExtensions() const;
+
+protected:
+ bool doSetup() override;
+
+ void writeArgumentNames(QTextStream &s,
+ const AbstractMetaFunction *func,
+ Options options = NoOption) const override;
+
+ /**
+ * Function used to write the fucntion 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
+ */
+ void writeFunctionArguments(QTextStream &s,
+ const AbstractMetaFunction *func,
+ Options options = NoOption) const override;
+
+ GeneratorContext contextForClass(const AbstractMetaClass *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 AbstractMetaClass *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.
+ */
+ AbstractMetaFunctionList getFunctionAndInheritedOverloads(const AbstractMetaFunction *func, QSet<QString> *seen);
+
+ /// Write user's custom code snippets at class or module level.
+ void writeClassCodeSnips(QTextStream &s,
+ const QVector<CodeSnip> & codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language,
+ const GeneratorContext &context);
+ void writeCodeSnips(QTextStream &s,
+ const QVector<CodeSnip> & codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language);
+ /// Write user's custom code snippets at function level.
+ void writeCodeSnips(QTextStream &s,
+ const QVector<CodeSnip> & codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language,
+ const AbstractMetaFunction *func,
+ const AbstractMetaArgument *lastArg = nullptr);
+
+ /// Replaces variables for the user's custom code at global or class level.
+ void processCodeSnip(QString &code);
+ void processClassCodeSnip(QString &code, const GeneratorContext &context);
+
+ /**
+ * Verifies if any of the function's code injections of the "native"
+ * type needs the type system variable "%PYSELF".
+ * \param func the function to check
+ * \return true if the function's native code snippets use "%PYSELF"
+ */
+ bool injectedCodeUsesPySelf(const AbstractMetaFunction *func);
+
+ /**
+ * 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
+ */
+ bool injectedCodeCallsCppFunction(const GeneratorContext &context,
+ const AbstractMetaFunction *func);
+
+ /**
+ * Verifies if any of the function's code injections of the "native" class makes a
+ * call to the C++ method. This is used by the generator to avoid writing calls to
+ * Python overrides of C++ virtual methods when the user custom code already does this.
+ * \param func the function to check
+ * \return true if the function's code snippets call the Python override for a C++ virtual method
+ */
+ bool injectedCodeCallsPythonOverride(const AbstractMetaFunction *func);
+
+ /**
+ * Verifies if any of the function's code injections attributes values to
+ * the return variable (%0 or %PYARG_0).
+ * \param func the function to check
+ * \param language the kind of code snip
+ * \return true if the function's code attributes values to "%0" or "%PYARG_0"
+ */
+ bool injectedCodeHasReturnValueAttribution(const AbstractMetaFunction *func, TypeSystem::Language language = TypeSystem::TargetLangCode);
+
+ /**
+ * Verifies if any of the function's code injections uses the type system variable
+ * for function arguments of a given index.
+ */
+ bool injectedCodeUsesArgument(const AbstractMetaFunction *func, int argumentIndex);
+
+ /**
+ * 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 AbstractMetaFunction *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 const AbstractMetaClass *getMultipleInheritingClass(const AbstractMetaClass *metaClass);
+
+ static bool useOverrideCaching(const AbstractMetaClass *metaClass);
+ AttroCheck checkAttroFunctionNeeds(const AbstractMetaClass *metaClass) const;
+
+ /// 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.
+ AbstractMetaFunctionList getMethodsWithBothStaticAndNonStaticMethods(const AbstractMetaClass *metaClass);
+
+ /// Returns a list of parent classes for a given class.
+ AbstractMetaClassList getBaseClasses(const AbstractMetaClass *metaClass) const;
+
+ void writeToPythonConversion(QTextStream &s, const AbstractMetaType &type,
+ const AbstractMetaClass *context, const QString &argumentName);
+ void writeToCppConversion(QTextStream &s, const AbstractMetaType &type, const AbstractMetaClass *context,
+ const QString &inArgName, const QString &outArgName);
+ void writeToCppConversion(QTextStream &s, const AbstractMetaClass *metaClass, const QString &inArgName, const QString &outArgName);
+
+ /// Returns true if the argument is a pointer that rejects nullptr values.
+ bool shouldRejectNullPointerArgument(const AbstractMetaFunction *func, int argIndex);
+
+ /// Verifies if the class should have a C++ wrapper generated for it, instead of only a Python wrapper.
+ bool shouldGenerateCppWrapper(const AbstractMetaClass *metaClass) const;
+
+ /// Condition to call WriteVirtualMethodNative. Was extracted because also used to count these calls.
+ bool shouldWriteVirtualMethodNative(const AbstractMetaFunction *func);
+
+ QString wrapperName(const AbstractMetaClass *metaClass) const;
+
+ QString fullPythonClassName(const AbstractMetaClass *metaClass);
+ QString fullPythonFunctionName(const AbstractMetaFunction *func);
+
+ bool wrapperDiagnostics() const { return m_wrapperDiagnostics; }
+
+ static QString protectedEnumSurrogateName(const AbstractMetaEnum *metaEnum);
+ static QString protectedFieldGetterName(const AbstractMetaField *field);
+ static QString protectedFieldSetterName(const AbstractMetaField *field);
+
+ static QString pythonPrimitiveTypeName(const QString &cppTypeName);
+ static QString pythonPrimitiveTypeName(const PrimitiveTypeEntry *type);
+
+ static QString pythonOperatorFunctionName(const QString &cppOpFuncName);
+ static QString pythonOperatorFunctionName(const AbstractMetaFunction *func);
+ static QString pythonRichCompareOperatorId(const QString &cppOpFuncName);
+ static QString pythonRichCompareOperatorId(const AbstractMetaFunction *func);
+
+ static QString fixedCppTypeName(const CustomConversion::TargetToNativeConversion *toNative);
+ static QString fixedCppTypeName(const AbstractMetaType &type);
+ static QString fixedCppTypeName(const TypeEntry *type, QString typeName = QString());
+
+ static bool isNumber(const QString &cpythonApiName);
+ static bool isNumber(const TypeEntry *type);
+ static bool isNumber(const AbstractMetaType &type);
+ static bool isPyInt(const TypeEntry *type);
+ static bool isPyInt(const AbstractMetaType &type);
+
+ /**
+ * Returns true if the type passed has a Python wrapper for it.
+ * Although namespace has a Python wrapper, it's not considered a type.
+ */
+ static bool isWrapperType(const TypeEntry *type);
+ static bool isWrapperType(const ComplexTypeEntry *type);
+ static bool isWrapperType(const AbstractMetaType &metaType);
+
+ /**
+ * Checks if the type is an Object/QObject or pointer to Value Type.
+ * In other words, tells if the type is "T*" and T has a Python wrapper.
+ */
+ static bool isPointerToWrapperType(const AbstractMetaType &type);
+
+ /**
+ * Returns true if \p type is an Object Type used as a value.
+ */
+ static bool isObjectTypeUsedAsValueType(const AbstractMetaType &type);
+
+ static bool isValueTypeWithCopyConstructorOnly(const AbstractMetaClass *metaClass);
+ bool isValueTypeWithCopyConstructorOnly(const TypeEntry *type) const;
+ bool isValueTypeWithCopyConstructorOnly(const AbstractMetaType &type) const;
+
+ /// Returns true if the type is a primitive but not a C++ primitive.
+ static bool isUserPrimitive(const TypeEntry *type);
+ static bool isUserPrimitive(const AbstractMetaType &type);
+
+ /// Returns true if the type is a C++ primitive, a void*, a const char*, or a std::string.
+ static bool isCppPrimitive(const TypeEntry *type);
+ static bool isCppPrimitive(const AbstractMetaType &type);
+
+ /// Returns true if the type is a C++ integral primitive, i.e. bool, char, int, long, and their unsigned counterparts.
+ static bool isCppIntegralPrimitive(const TypeEntry *type);
+ static bool isCppIntegralPrimitive(const AbstractMetaType &type);
+
+ /// Checks if an argument type should be dereferenced by the Python method wrapper before calling the C++ method.
+ static bool shouldDereferenceArgumentPointer(const AbstractMetaArgument &arg);
+ /// Checks if a meta type should be dereferenced by the Python method wrapper passing it to C++.
+ static bool shouldDereferenceAbstractMetaTypePointer(const AbstractMetaType &metaType);
+
+ static bool visibilityModifiedToPrivate(const AbstractMetaFunction *func);
+
+ static bool isNullPtr(const QString &value);
+
+ QString converterObject(const AbstractMetaType &type);
+ QString converterObject(const TypeEntry *type);
+
+ static QString cpythonBaseName(const AbstractMetaClass *metaClass);
+ static QString cpythonBaseName(const TypeEntry *type);
+ static QString cpythonBaseName(const AbstractMetaType &type);
+ static QString cpythonTypeName(const AbstractMetaClass *metaClass);
+ static QString cpythonTypeName(const TypeEntry *type);
+ QString cpythonTypeNameExt(const TypeEntry *type) const;
+ QString cpythonTypeNameExt(const AbstractMetaType &type) const;
+ QString cpythonCheckFunction(const TypeEntry *type, bool genericNumberType = false);
+ QString cpythonCheckFunction(AbstractMetaType metaType, bool genericNumberType = false);
+ /**
+ * Receives the argument \p type and tries to find the appropriate AbstractMetaType for it
+ * or a custom type check.
+ * \param type A string representing the type to be discovered.
+ * \param metaType A pointer to an AbstractMetaType pointer, to where write a new meta type object
+ * if one is produced from the \p type string. This object must be deallocated by
+ * the caller. It will set the target variable to nullptr, is \p type is a Python type.
+ * \return A custom check if \p type is a custom type, or an empty string if \p metaType
+ * receives an existing type object.
+ */
+ QString guessCPythonCheckFunction(const QString &type, AbstractMetaType *metaType);
+ QString cpythonIsConvertibleFunction(const TypeEntry *type, bool genericNumberType = false, bool checkExact = false);
+ QString cpythonIsConvertibleFunction(AbstractMetaType metaType, bool genericNumberType = false);
+ QString cpythonIsConvertibleFunction(const AbstractMetaArgument &metaArg, bool genericNumberType = false);
+
+ QString cpythonToCppConversionFunction(const AbstractMetaClass *metaClass);
+ QString cpythonToCppConversionFunction(const AbstractMetaType &type, const AbstractMetaClass *context = nullptr);
+ QString cpythonToPythonConversionFunction(const AbstractMetaType &type, const AbstractMetaClass *context = nullptr);
+ QString cpythonToPythonConversionFunction(const AbstractMetaClass *metaClass);
+ QString cpythonToPythonConversionFunction(const TypeEntry *type);
+
+ QString cpythonFunctionName(const AbstractMetaFunction *func);
+ QString cpythonMethodDefinitionName(const AbstractMetaFunction *func);
+ QString cpythonGettersSettersDefinitionName(const AbstractMetaClass *metaClass);
+ static QString cpythonGetattroFunctionName(const AbstractMetaClass *metaClass);
+ static QString cpythonSetattroFunctionName(const AbstractMetaClass *metaClass);
+ static QString cpythonGetterFunctionName(const AbstractMetaField *metaField);
+ static QString cpythonSetterFunctionName(const AbstractMetaField *metaField);
+ static QString cpythonGetterFunctionName(const QPropertySpec *property,
+ const AbstractMetaClass *metaClass);
+ static QString cpythonSetterFunctionName(const QPropertySpec *property,
+ const AbstractMetaClass *metaClass);
+ QString cpythonWrapperCPtr(const AbstractMetaClass *metaClass,
+ const QString &argName = QLatin1String("self")) const;
+ QString cpythonWrapperCPtr(const AbstractMetaType &metaType, const QString &argName) const;
+ QString cpythonWrapperCPtr(const TypeEntry *type, const QString &argName) const;
+
+ /// Guesses the scope to where belongs an argument's default value.
+ QString guessScopeForDefaultValue(const AbstractMetaFunction *func,
+ const AbstractMetaArgument &arg) const;
+ QString guessScopeForDefaultFlagsValue(const AbstractMetaFunction *func,
+ const AbstractMetaArgument &arg,
+ const QString &value) const;
+
+ static QString cpythonEnumName(const EnumTypeEntry *enumEntry);
+ static QString cpythonEnumName(const AbstractMetaEnum *metaEnum);
+
+ static QString cpythonFlagsName(const FlagsTypeEntry *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 AbstractMetaClass *metaClass);
+
+ QString getFormatUnitString(const AbstractMetaFunction *func, bool incRef = false) const;
+
+ /// Returns the file name for the module global header. If no module name is provided the current will be used.
+ QString getModuleHeaderFileName(const QString &moduleName = QString()) const;
+
+ OptionDescriptions options() const override;
+ bool handleOption(const QString &key, const QString &value) override;
+
+ /// Returns true if the user enabled the so called "parent constructor heuristic".
+ bool useCtorHeuristic() const;
+ /// Returns true if the user enabled the so called "return value heuristic".
+ bool useReturnValueHeuristic() const;
+ /// Returns true if the generator should use the result of isNull()const to compute boolean casts.
+ bool useIsNullAsNbNonZero() const;
+ /// Returns true if the generated code should use the "#define protected public" hack.
+ bool avoidProtectedHack() const;
+ QString cppApiVariableName(const QString &moduleName = QString()) const;
+ QString pythonModuleObjectName(const QString &moduleName = QString()) const;
+ QString convertersVariableName(const QString &moduleName = QString()) const;
+ /**
+ * Returns the type index variable name for a given class. If \p alternativeTemplateName is true
+ * and the class is a typedef for a template class instantiation, it will return an alternative name
+ * made of the template class and the instantiation values, or an empty string if the class isn't
+ * derived from a template class at all.
+ */
+ QString getTypeIndexVariableName(const AbstractMetaClass *metaClass, bool alternativeTemplateName = false) const;
+ QString getTypeIndexVariableName(const TypeEntry *type) const;
+ QString getTypeIndexVariableName(const AbstractMetaType &type) const;
+
+ /// Returns true if the user don't want verbose error messages on the generated bindings.
+ bool verboseErrorMessagesDisabled() const;
+
+ /**
+ * Builds an AbstractMetaType object from a QString.
+ * Returns nullptr if no type could be built from the string.
+ * \param typeSignature The string describing the type to be built.
+ * \return A new AbstractMetaType object that must be deleted by the caller,
+ * or a nullptr pointer in case of failure.
+ */
+ AbstractMetaType buildAbstractMetaTypeFromString(QString typeSignature,
+ QString *errorMessage = nullptr);
+
+ /// Creates an AbstractMetaType object from a TypeEntry.
+ AbstractMetaType buildAbstractMetaTypeFromTypeEntry(const TypeEntry *typeEntry);
+ /// Creates an AbstractMetaType object from an AbstractMetaClass.
+ AbstractMetaType buildAbstractMetaTypeFromAbstractMetaClass(const AbstractMetaClass *metaClass);
+
+ void writeMinimalConstructorExpression(QTextStream &s, const AbstractMetaType &type,
+ const QString &defaultCtor = QString());
+ void writeMinimalConstructorExpression(QTextStream &s, const TypeEntry *type, const QString &defaultCtor = QString());
+
+ void collectContainerTypesFromConverterMacros(const QString &code, bool toPythonMacro);
+ // verify whether the class is copyable
+ bool isCopyable(const AbstractMetaClass *metaClass);
+
+ void clearTpFuncs();
+
+
+ /// Initializes correspondences between primitive and Python types.
+ static void initPrimitiveTypesCorrespondences();
+ /// Initializes a list of Python known type names.
+ static void initKnownPythonTypes();
+
+ void writeFunctionCall(QTextStream &s,
+ const AbstractMetaFunction *metaFunc,
+ Options options = NoOption) const;
+
+ void writeUnusedVariableCast(QTextStream &s, const QString &variableName);
+
+ AbstractMetaFunctionList filterFunctions(const AbstractMetaClass *metaClass);
+
+ // 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<const TypeEntry *, QVector<const AbstractMetaClass *> >;
+ /// 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.
+ QVector<const CustomConversion *> getPrimitiveCustomConversions();
+
+ /// Returns true if the Python wrapper for the received OverloadData must accept a list of arguments.
+ static bool pythonFunctionWrapperUsesListOfArguments(const OverloadData &overloadData);
+
+ Indentor INDENT;
+
+ const QRegularExpression &convertToCppRegEx() const
+ { return m_typeSystemConvRegEx[TypeSystemToCppFunction]; }
+
+ static QString pythonArgsAt(int i);
+
+ static QHash<QString, QString> m_pythonPrimitiveTypeName;
+ static QHash<QString, QString> m_pythonOperators;
+ static QHash<QString, QString> m_formatUnits;
+ static QHash<QString, QString> m_tpFuncs;
+ static QStringList m_knownPythonTypes;
+
+private:
+ static QString cpythonGetterFunctionName(const QString &name,
+ const AbstractMetaClass *enclosingClass);
+ static QString cpythonSetterFunctionName(const QString &name,
+ const AbstractMetaClass *enclosingClass);
+
+ static const GeneratorClassInfoCacheEntry &getGeneratorClassInfo(const AbstractMetaClass *scope);
+ static FunctionGroups getFunctionGroupsImpl(const AbstractMetaClass *scope);
+ static bool classNeedsGetattroFunctionImpl(const AbstractMetaClass *metaClass);
+
+ QString translateTypeForWrapperMethod(const AbstractMetaType &cType,
+ const AbstractMetaClass *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.
+ */
+ AbstractMetaFunctionList getInheritedOverloads(const AbstractMetaFunction *func,
+ QSet<QString> *seen);
+
+ /**
+ * Returns all overloads for a function named \p functionName.
+ * \param scope scope used to search for overloads.
+ * \param functionName the function name.
+ */
+ AbstractMetaFunctionList getFunctionOverloads(const AbstractMetaClass *scope,
+ const QString &functionName);
+ /**
+ * 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(QTextStream &s,
+ const AbstractMetaFunction *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 AbstractMetaFunction *func,
+ const AbstractMetaArgument &argument,
+ Options options = NoOption) const;
+
+ QString functionReturnType(const AbstractMetaFunction *func, Options options = NoOption) const;
+
+ /// Utility function for writeCodeSnips.
+ using ArgumentVarReplacementPair = QPair<AbstractMetaArgument, QString>;
+ using ArgumentVarReplacementList = QVector<ArgumentVarReplacementPair>;
+ ArgumentVarReplacementList getArgumentReplacement(const AbstractMetaFunction* 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.
+ QString getCodeSnippets(const QVector<CodeSnip> & codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language);
+
+ enum TypeSystemConverterVariable {
+ TypeSystemCheckFunction = 0,
+ TypeSystemIsConvertibleFunction,
+ TypeSystemToCppFunction,
+ TypeSystemToPythonFunction,
+ TypeSystemConverterVariables
+ };
+ void replaceConverterTypeSystemVariable(TypeSystemConverterVariable converterVariable, QString &code);
+
+ /// Replaces the %CONVERTTOPYTHON type system variable.
+ inline void replaceConvertToPythonTypeSystemVariable(QString &code)
+ {
+ replaceConverterTypeSystemVariable(TypeSystemToPythonFunction, code);
+ }
+ /// Replaces the %CONVERTTOCPP type system variable.
+ inline void replaceConvertToCppTypeSystemVariable(QString &code)
+ {
+ replaceConverterTypeSystemVariable(TypeSystemToCppFunction, code);
+ }
+ /// Replaces the %ISCONVERTIBLE type system variable.
+ inline void replaceIsConvertibleToCppTypeSystemVariable(QString &code)
+ {
+ replaceConverterTypeSystemVariable(TypeSystemIsConvertibleFunction, code);
+ }
+ /// Replaces the %CHECKTYPE type system variable.
+ inline void replaceTypeCheckTypeSystemVariable(QString &code)
+ {
+ replaceConverterTypeSystemVariable(TypeSystemCheckFunction, code);
+ }
+
+ /// Return a prefix with '_' suitable for names in C++
+ QString moduleCppPrefix(const QString &moduleName = QString()) const;
+
+ bool m_useCtorHeuristic = false;
+ bool m_userReturnValueHeuristic = false;
+ bool m_usePySideExtensions = false;
+ bool m_verboseErrorMessagesDisabled = false;
+ bool m_useIsNullAsNbNonZero = false;
+ bool m_avoidProtectedHack = false;
+ bool m_wrapperDiagnostics = false;
+
+ using AbstractMetaTypeCache = QHash<QString, AbstractMetaType>;
+ AbstractMetaTypeCache m_metaTypeFromStringCache;
+
+ /// Type system converter variable replacement names and regular expressions.
+ QString m_typeSystemConvName[TypeSystemConverterVariables];
+ QRegularExpression m_typeSystemConvRegEx[TypeSystemConverterVariables];
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(ShibokenGenerator::AttroCheck);
+
+#endif // SHIBOKENGENERATOR_H