diff options
Diffstat (limited to 'sources/shiboken6/generator/shiboken/cppgenerator.cpp')
-rw-r--r-- | sources/shiboken6/generator/shiboken/cppgenerator.cpp | 6197 |
1 files changed, 3163 insertions, 3034 deletions
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp index 43a2b4e78..48e7f4fe5 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp +++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp @@ -1,44 +1,27 @@ -/**************************************************************************** -** -** 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> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "cppgenerator.h" +#include "configurablescope.h" +#include "generatorargument.h" +#include "generatorstrings.h" +#include "defaultvalue.h" +#include "generatorcontext.h" +#include "codesnip.h" +#include "customconversion.h" +#include "headergenerator.h" #include "apiextractorresult.h" #include "ctypenames.h" #include <exception.h> #include "pytypenames.h" #include "fileout.h" #include "overloaddata.h" +#include "pymethoddefentry.h" #include <abstractmetaenum.h> #include <abstractmetafield.h> #include <abstractmetafunction.h> #include <abstractmetalang.h> +#include <abstractmetalang_helpers.h> #include <messages.h> #include <modifications.h> #include <propertyspec.h> @@ -46,80 +29,114 @@ #include <sourcelocation.h> #include <textstream.h> #include <typedatabase.h> +#include <containertypeentry.h> +#include <enumtypeentry.h> +#include <flagstypeentry.h> +#include <functiontypeentry.h> +#include <namespacetypeentry.h> +#include <primitivetypeentry.h> +#include <smartpointertypeentry.h> +#include <typesystemtypeentry.h> +#include <valuetypeentry.h> #include <parser/enumvalue.h> +#include "qtcompat.h" + +#include <QtCore/QDebug> #include <QtCore/QDir> #include <QtCore/QMetaObject> +#include <QtCore/QMetaType> #include <QtCore/QRegularExpression> #include <QtCore/QTextStream> -#include <QtCore/QDebug> -#include <QMetaType> #include <algorithm> #include <cstring> +#include <memory> +#include <set> + +using namespace Qt::StringLiterals; -static const char CPP_ARG0[] = "cppArg0"; +static const char shibokenErrorsOccurred[] = "Shiboken::Errors::occurred() != nullptr"; -static inline QString reprFunction() { return QStringLiteral("__repr__"); } +static constexpr auto virtualMethodStaticReturnVar = "result"_L1; -QString CppGenerator::m_currentErrorCode(QLatin1String("{}")); +static constexpr auto sbkObjectTypeF = "SbkObject_TypeF()"_L1; +static const char initInheritanceFunction[] = "initInheritance"; -static const char typeNameFunc[] = R"CPP( -template <class T> -static const char *typeNameOf(const T &t) +static QString mangleName(QString name) { - 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; + if (name == u"None" || name == u"False" || name == u"True" || name == u"from") + name += u'_'; + return name; } -)CPP"; -// utility functions -inline AbstractMetaType getTypeWithoutContainer(const AbstractMetaType &arg) +struct sbkUnusedVariableCast { - if (arg.typeEntry()->isContainer()) { - // only support containers with 1 type - if (arg.instantiations().size() == 1) - return arg.instantiations().constFirst(); - } - return arg; -} + explicit sbkUnusedVariableCast(QAnyStringView name) : m_name(name) {} -// A helper for writing C++ return statements for either void ("return;") -// or some return value ("return value;") -class returnStatement + const QAnyStringView m_name; +}; + +TextStream &operator<<(TextStream &str, const sbkUnusedVariableCast &c) { -public: - explicit returnStatement(QString s) : m_returnValue(std::move(s)) {} + str << "SBK_UNUSED(" << c.m_name << ")\n"; + return str; +} - friend TextStream &operator<<(TextStream &s, const returnStatement &r); +struct pyTypeGetSlot +{ + explicit pyTypeGetSlot(QAnyStringView funcType, QAnyStringView typeObject, + QAnyStringView aSlot) : + m_funcType(funcType), m_typeObject(typeObject), m_slot(aSlot) {} -private: - const QString m_returnValue; + const QAnyStringView m_funcType; + const QAnyStringView m_typeObject; + const QAnyStringView m_slot; }; -TextStream &operator<<(TextStream &s, const returnStatement &r) +TextStream &operator<<(TextStream &str, const pyTypeGetSlot &p) +{ + str << "reinterpret_cast<" << p.m_funcType << ">(PepType_GetSlot(" + << p.m_typeObject << ", " << p.m_slot << "));\n"; + return str; +} + +TextStream &operator<<(TextStream &s, CppGenerator::ErrorReturn r) { s << "return"; - if (!r.m_returnValue.isEmpty()) - s << ' ' << r.m_returnValue; - s << ';'; + switch (r) { + case CppGenerator::ErrorReturn::Default: + s << " {}"; + break; + case CppGenerator::ErrorReturn::Zero: + s << " 0"; + break; + case CppGenerator::ErrorReturn::MinusOne: + s << " -1"; + break; + case CppGenerator::ErrorReturn::Void: + break; + } + s << ";\n"; + return s; +} + +static constexpr auto converterVar = "converter"_L1; + +struct registerConverterName +{ + explicit registerConverterName(QAnyStringView typeName, + QAnyStringView varName = converterVar) : + m_typeName(typeName), m_varName(varName) {} + + QAnyStringView m_typeName; + QAnyStringView m_varName; +}; + +TextStream &operator<<(TextStream &s, const registerConverterName &r) +{ + s << "Shiboken::Conversions::registerConverterName(" << r.m_varName + << ", \"" << r.m_typeName << "\");\n"; return s; } @@ -147,15 +164,15 @@ static bool contains(const ProtocolEntries &l, const QString &needle) const ProtocolEntries &mappingProtocols() { static const ProtocolEntries result = { - {QLatin1String("__mlen__"), - QLatin1String("PyObject *self"), - QLatin1String("Py_ssize_t")}, - {QLatin1String("__mgetitem__"), - QLatin1String("PyObject *self, PyObject *_key"), - QLatin1String("PyObject*")}, - {QLatin1String("__msetitem__"), - QLatin1String("PyObject *self, PyObject *_key, PyObject *_value"), - intT()}}; + {u"__mlen__"_s, + u"PyObject *self"_s, + u"Py_ssize_t"_s}, + {u"__mgetitem__"_s, + u"PyObject *self, PyObject *_key"_s, + u"PyObject*"_s}, + {u"__msetitem__"_s, + u"PyObject *self, PyObject *_key, PyObject *_value"_s, + intT}}; return result; } @@ -164,157 +181,71 @@ const ProtocolEntries &mappingProtocols() const ProtocolEntries &sequenceProtocols() { static const ProtocolEntries result = { - {QLatin1String("__len__"), - QLatin1String("PyObject *self"), - QLatin1String("Py_ssize_t")}, - {QLatin1String("__getitem__"), - QLatin1String("PyObject *self, Py_ssize_t _i"), - QLatin1String("PyObject*")}, - {QLatin1String("__setitem__"), - QLatin1String("PyObject *self, Py_ssize_t _i, PyObject *_value"), - intT()}, - {QLatin1String("__getslice__"), - QLatin1String("PyObject *self, Py_ssize_t _i1, Py_ssize_t _i2"), - QLatin1String("PyObject*")}, - {QLatin1String("__setslice__"), - QLatin1String("PyObject *self, Py_ssize_t _i1, Py_ssize_t _i2, PyObject *_value"), - intT()}, - {QLatin1String("__contains__"), - QLatin1String("PyObject *self, PyObject *_value"), - intT()}, - {QLatin1String("__concat__"), - QLatin1String("PyObject *self, PyObject *_other"), - QLatin1String("PyObject*")} + {u"__len__"_s, + u"PyObject *self"_s, + u"Py_ssize_t"_s}, + {u"__getitem__"_s, + u"PyObject *self, Py_ssize_t _i"_s, + u"PyObject*"_s}, + {u"__setitem__"_s, + u"PyObject *self, Py_ssize_t _i, PyObject *_value"_s, + intT}, + {u"__getslice__"_s, + u"PyObject *self, Py_ssize_t _i1, Py_ssize_t _i2"_s, + u"PyObject*"_s}, + {u"__setslice__"_s, + u"PyObject *self, Py_ssize_t _i1, Py_ssize_t _i2, PyObject *_value"_s, + intT}, + {u"__contains__"_s, + u"PyObject *self, PyObject *_value"_s, + intT}, + {u"__concat__"_s, + u"PyObject *self, PyObject *_other"_s, + u"PyObject*"_s} }; return result; } -CppGenerator::CppGenerator() = default; - -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(); -} - -static bool isInplaceAdd(const AbstractMetaFunctionCPtr &func) -{ - return func->name() == u"operator+="; -} - -static bool isIncrementOperator(const AbstractMetaFunctionCPtr &func) -{ - return func->functionType() == AbstractMetaFunction::IncrementOperator; -} - -static bool isDecrementOperator(const AbstractMetaFunctionCPtr &func) -{ - return func->functionType() == AbstractMetaFunction::DecrementOperator; -} - -// Filter predicate for operator functions -static bool skipOperatorFunc(const AbstractMetaFunctionCPtr &func) -{ - if (func->isModifiedRemoved() || func->usesRValueReferences()) - return true; - const auto &name = func->name(); - return name == u"operator[]" || name == u"operator->" || name == u"operator!"; -} - -QList<AbstractMetaFunctionCList> - CppGenerator::filterGroupedOperatorFunctions(const AbstractMetaClass *metaClass, - OperatorQueryOptions query) +// Return name of function to create PyObject wrapping a container +static QString opaqueContainerCreationFunc(const AbstractMetaType &type) { - // ( func_name, num_args ) => func_list - QMap<QPair<QString, int>, AbstractMetaFunctionCList> results; - - auto funcs = metaClass->operatorOverloads(query); - auto end = std::remove_if(funcs.begin(), funcs.end(), skipOperatorFunc); - funcs.erase(end, funcs.end()); - - // If we have operator+=, we remove the operator++/-- which would - // otherwise be used for emulating __iadd__, __isub__. - if (std::any_of(funcs.cbegin(), funcs.cend(), isInplaceAdd)) { - end = std::remove_if(funcs.begin(), funcs.end(), - [] (const AbstractMetaFunctionCPtr &func) { - return func->isIncDecrementOperator(); - }); - funcs.erase(end, funcs.end()); - } else { - // If both prefix/postfix ++/-- are present, remove one - if (std::count_if(funcs.begin(), funcs.end(), isIncrementOperator) > 1) - funcs.erase(std::find_if(funcs.begin(), funcs.end(), isIncrementOperator)); - if (std::count_if(funcs.begin(), funcs.end(), isDecrementOperator) > 1) - funcs.erase(std::find_if(funcs.begin(), funcs.end(), isDecrementOperator)); - } - - for (const auto &func : funcs) { - int args; - if (func->isComparisonOperator()) { - args = -1; - } else { - args = func->arguments().size(); - } - QPair<QString, int > op(func->name(), args); - results[op].append(func); - } - QList<AbstractMetaFunctionCList> result; - result.reserve(results.size()); - for (auto it = results.cbegin(), end = results.cend(); it != end; ++it) - result.append(it.value()); + const auto containerTypeEntry = + std::static_pointer_cast<const ContainerTypeEntry>(type.typeEntry()); + const auto instantiationTypeEntry = + type.instantiations().constFirst().typeEntry(); + QString result = u"create"_s; + if (type.isConstant()) + result += u"Const"_s; + result += containerTypeEntry->opaqueContainerName(type.instantiationCppSignatures()); return result; } -AbstractMetaFunctionCPtr CppGenerator::boolCast(const AbstractMetaClass *metaClass) const +// Write declaration of the function to create PyObject wrapping a container +static void writeOpaqueContainerCreationFuncDecl(TextStream &s, const QString &name, + AbstractMetaType type) { - const auto *te = metaClass->typeEntry(); - auto mode = te->operatorBoolMode(); - if (useOperatorBoolAsNbNonZero() - ? mode != TypeSystem::BoolCast::Disabled : mode == TypeSystem::BoolCast::Enabled) { - const auto func = metaClass->findOperatorBool(); - if (!func.isNull()) - return func; - } - - mode = te->isNullMode(); - if (useIsNullAsNbNonZero() - ? mode != TypeSystem::BoolCast::Disabled : mode == TypeSystem::BoolCast::Enabled) { - const auto func = metaClass->findQtIsNullMethod(); - if (!func.isNull()) - return func; - } - return {}; + type.setReferenceType(NoReference); + // Maintain const + s << "PyObject *" << name << '(' << type.cppSignature() << "*);\n"; } -std::optional<AbstractMetaType> - CppGenerator::findSmartPointerInstantiation(const TypeEntry *entry) const +CppGenerator::CppGenerator() = default; + +QString CppGenerator::fileNameForContext(const GeneratorContext &context) const { - for (const auto &i : instantiatedSmartPointers()) { - if (i.instantiations().at(0).typeEntry() == entry) - return i; - } - return {}; + return fileNameForContextHelper(context, u"_wrapper.cpp"_s); } void CppGenerator::clearTpFuncs() { + // Functions that should not be registered under a name in PyMethodDef, + // but under a special constant under slots. m_tpFuncs = { - {QLatin1String("__str__"), {}}, {QLatin1String("__str__"), {}}, - {reprFunction(), {}}, {QLatin1String("__iter__"), {}}, - {QLatin1String("__next__"), {}} + {u"__str__"_s, {}}, {u"__str__"_s, {}}, + {REPR_FUNCTION, {}}, {u"__iter__"_s, {}}, + {u"__next__"_s, {}} }; + m_nbFuncs = { {u"__abs__"_s, {}}, {u"__pow__"_s, {} }}; } // Prevent ELF symbol qt_version_tag from being generated into the source @@ -322,13 +253,13 @@ static const char includeQDebug[] = "#ifndef QT_NO_VERSION_TAGGING\n" "# define QT_NO_VERSION_TAGGING\n" "#endif\n" -"#include <QDebug>\n"; +"#include <QtCore/QDebug>\n"; -static QString chopType(QString s) +QString CppGenerator::chopType(QString s) { - if (s.endsWith(QLatin1String("_Type"))) + if (s.endsWith(u"_Type")) s.chop(5); - else if (s.endsWith(QLatin1String("_TypeF()"))) + else if (s.endsWith(u"_TypeF()")) s.chop(8); return s; } @@ -336,133 +267,314 @@ static QString chopType(QString s) static bool isStdSetterName(QString setterName, QString propertyName) { return setterName.size() == propertyName.size() + 3 - && setterName.startsWith(QLatin1String("set")) + && setterName.startsWith(u"set") && setterName.endsWith(QStringView{propertyName}.right(propertyName.size() - 1)) && setterName.at(3) == propertyName.at(0).toUpper(); } static QString buildPropertyString(const QPropertySpec &spec) { - QString text; - text += QLatin1Char('"'); - text += spec.name(); - text += QLatin1Char(':'); + QString text = u'"' + spec.name() + u':'; if (spec.read() != spec.name()) text += spec.read(); if (!spec.write().isEmpty()) { - text += QLatin1Char(':'); + text += u':'; if (!isStdSetterName(spec.write(), spec.name())) text += spec.write(); } - text += QLatin1Char('"'); + text += u'"'; return text; } +static QString _plainName(const QString &s) +{ + auto cutPos = s.lastIndexOf(u"::"_s); + return cutPos < 0 ? s : s.right(s.length() - (cutPos + 2)); +} + +/********************************************************************** + * + * Decision whether to use an IntEnum/IntFlag + * ------------------------------------------ + * + * Unfortunately, all attempts to drive this decision automagically + * did not work out. We therefore compile a list in with known + * IntEnum and IntFlag. + */ + +/* + * This function is now unused and replaced by TypeSystem::PythonEnumType + */ +#if 0 +static QSet<QString> useIntSet() +{ + static const QSet<QString> result{ + /* IntEnum */ u"PySide6.QtCore.QDataStream.Version"_s, + /* IntEnum */ u"PySide6.QtCore.QEvent.Type"_s, + /* IntEnum */ u"PySide6.QtCore.QLocale.FloatingPointPrecisionOption"_s, + /* IntFlag */ u"PySide6.QtCore.QLocale.LanguageCodeType"_s, + /* IntFlag */ u"PySide6.QtCore.QUrl.ComponentFormattingOption"_s, + // note: "QUrl::UrlFormattingOption" is set as IntFlag without flags + /* IntFlag */ u"PySide6.QtCore.QUrl.UrlFormattingOption"_s, + /* IntFlag */ u"PySide6.QtCore.Qt.AlignmentFlag"_s, + /* IntFlag */ u"PySide6.QtCore.Qt.FocusPolicy"_s, + /* IntEnum */ u"PySide6.QtCore.Qt.GestureType"_s, + /* IntEnum */ u"PySide6.QtCore.Qt.ItemDataRole"_s, + /* IntEnum */ u"PySide6.QtCore.Qt.Key"_s, + /* Flag */ u"PySide6.QtCore.Qt.Modifier"_s, + // note: "Qt::TextFlag" is set as IntFlag without flags + /* IntFlag */ u"PySide6.QtCore.Qt.TextFlag"_s, + /* IntFlag */ u"PySide6.QtCore.Qt.WindowType"_s, + // This is found in QtWidgets but should be in QtGui. + /* IntEnum */ u"PySide6.QtGui.QFileSystemModel.Roles"_s, + /* IntEnum */ u"PySide6.QtGui.QFont.Stretch"_s, + /* IntEnum */ u"PySide6.QtGui.QFont.Weight"_s, + /* IntEnum */ u"PySide6.QtGui.QTextDocument.ResourceType"_s, + /* IntEnum */ u"PySide6.QtGui.QTextFormat.FormatType"_s, + /* IntEnum */ u"PySide6.QtGui.QTextFormat.ObjectTypes"_s, + /* IntEnum */ u"PySide6.QtGui.QTextFormat.Property"_s, + /* IntEnum */ u"PySide6.QtWidgets.QDialog.DialogCode"_s, + /* IntEnum */ u"PySide6.QtWidgets.QFrame.Shadow"_s, + /* IntEnum */ u"PySide6.QtWidgets.QFrame.Shape"_s, + /* IntEnum */ u"PySide6.QtWidgets.QListWidgetItem.ItemType"_s, + /* IntFlag */ u"PySide6.QtWidgets.QMessageBox.StandardButton"_s, + // note: "QSizePolicy::PolicyFlag" is set as IntFlag without flags + /* IntFlag */ u"PySide6.QtWidgets.QSizePolicy.PolicyFlag"_s, + /* IntEnum */ u"PySide6.QtWidgets.QStyle.ComplexControl"_s, + /* IntEnum */ u"PySide6.QtWidgets.QStyle.ContentsType"_s, + /* IntEnum */ u"PySide6.QtWidgets.QStyle.ControlElement"_s, + /* IntEnum */ u"PySide6.QtWidgets.QStyle.PixelMetric"_s, + /* IntEnum */ u"PySide6.QtWidgets.QStyle.PrimitiveElement"_s, + /* IntEnum */ u"PySide6.QtWidgets.QStyle.StandardPixmap"_s, + /* IntEnum */ u"PySide6.QtWidgets.QStyle.StyleHint"_s, + /* IntEnum */ u"PySide6.QtWidgets.QStyle.SubElement"_s, + /* IntEnum */ u"PySide6.QtWidgets.QTableWidgetItem.ItemType"_s, + /* IntEnum */ u"PySide6.QtWidgets.QTreeWidgetItem.ItemType"_s, + /* IntEnum */ u"PySide6.QtCharts.QBoxSet.ValuePositions"_s, + /* IntEnum */ u"PySide6.QtMultimedia.QMediaPlayer.Loops"_s, + /* IntEnum */ u"PySide6.QtQuick.QSGGeometry.DrawingMode"_s, + /* IntEnum */ u"PySide6.QtWebEngineCore.QWebEngineScript.ScriptWorldId"_s, + // Added because it should really be used as number + /* IntEnum */ u"PySide6.QtCore.QMetaType.Type"_s, + /* IntEnum */ u"PySide6.QtSerialPort.QSerialPort.BaudRate"_s, + }; + return result; +} +#endif + +static bool _shouldInheritInt(const AbstractMetaEnum &cppEnum) +{ + return !cppEnum.fullName().startsWith(u"PySide6."_s); +} + +static QString BuildEnumFlagInfo(const AbstractMetaEnum &cppEnum) +{ + auto enumType = cppEnum.typeEntry(); + QString result = _plainName(enumType->name()); + auto flags = enumType->flags(); + auto decision = enumType->pythonEnumType(); + bool _int = _shouldInheritInt(cppEnum); + bool _flag = bool(flags); + + if (decision != TypeSystem::PythonEnumType::Unspecified) { + _int = decision == TypeSystem::PythonEnumType::IntEnum || + decision == TypeSystem::PythonEnumType::IntFlag; + _flag = decision == TypeSystem::PythonEnumType::Flag || + decision == TypeSystem::PythonEnumType::IntFlag; + } + result += _flag ? (_int ? u":IntFlag"_s : u":Flag"_s) + : (_int ? u":IntEnum"_s : u":Enum"_s); + if (flags) + result += u':' + _plainName(flags->flagsName()); + return u'"' + result + u'"'; +} + static void writePyGetSetDefEntry(TextStream &s, const QString &name, const QString &getFunc, const QString &setFunc) { - s << "{const_cast<char *>(\"" << name << "\"), " << getFunc << ", " - << (setFunc.isEmpty() ? QLatin1String(NULL_PTR) : setFunc) << "},\n"; + s << "{const_cast<char *>(\"" << mangleName(name) << "\"), " << getFunc << ", " + << (setFunc.isEmpty() ? NULL_PTR : setFunc) << ", nullptr, nullptr},\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(TextStream &s, const GeneratorContext &classContext) +static bool generateRichComparison(const GeneratorContext &c) { - s.setLanguage(TextStream::Language::Cpp); - const AbstractMetaClass *metaClass = classContext.metaClass(); + const auto metaClass = c.metaClass(); + if (c.forSmartPointer()) { + auto te = std::static_pointer_cast<const SmartPointerTypeEntry>(metaClass->typeEntry()); + return te->smartPointerType() == TypeSystem::SmartPointerType::Shared; + } + + return !metaClass->isNamespace() && metaClass->hasComparisonOperatorOverload(); +} + +void CppGenerator::generateIncludes(TextStream &s, const GeneratorContext &classContext, + const IncludeGroupList &includes, + const AbstractMetaClassCList &innerClasses) const +{ + const auto metaClass = classContext.metaClass(); // write license comment s << licenseComment() << '\n'; - if (!avoidProtectedHack() && !metaClass->isNamespace() && !metaClass->hasPrivateDestructor()) { - s << "//workaround to access protected functions\n"; - s << "#define protected public\n\n"; - } + const bool normalClass = !classContext.forSmartPointer(); + // Normally only required for classes for which we want to generate protected API, + // but it needs to be generated into all files to ensure ODR for Unity builds. + if (!avoidProtectedHack()) + s << HeaderGenerator::protectedHackDefine; + QByteArrayList cppIncludes{"typeinfo", "iterator", // for containers + "cctype", "cstring"}; // headers s << "// default includes\n"; s << "#include <shiboken.h>\n"; - if (usePySideExtensions()) { + if (wrapperDiagnostics()) { + s << "#include <helper.h>\n"; + cppIncludes << "iostream"; + } + + if (normalClass && usePySideExtensions()) { s << includeQDebug; - s << "#include <pysidesignal.h>\n" - << "#include <pysideproperty.h>\n" - << "#include <pyside.h>\n" - << "#include <pysideqenum.h>\n" + if (metaClass->hasToStringCapability()) + s << "#include <QtCore/QBuffer>\n"; + if (isQObject(metaClass)) { + s << "#include <pysideqobject.h>\n" + << "#include <pysidesignal.h>\n" + << "#include <pysideproperty.h>\n" + << "#include <signalmanager.h>\n" + << "#include <pysidemetafunction.h>\n"; + } + s << "#include <pysideqenum.h>\n" + << "#include <pysideqmetatype.h>\n" + << "#include <pysideutils.h>\n" << "#include <feature_select.h>\n" << "QT_WARNING_DISABLE_DEPRECATED\n\n"; - } - - 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"; + if (normalClass && getMultipleInheritingClass(metaClass) != nullptr) + cppIncludes << "algorithm" << "set"; + if (normalClass && metaClass->generateExceptionHandling()) + cppIncludes << "exception"; s << "\n// module include\n" << "#include \"" << getModuleHeaderFileName() << "\"\n"; if (hasPrivateClasses()) s << "#include \"" << getPrivateModuleHeaderFileName() << "\"\n"; - QString headerfile = fileNameForContext(classContext); - headerfile.replace(QLatin1String(".cpp"), QLatin1String(".h")); - s << "\n// main header\n" << "#include \"" << headerfile << "\"\n"; - - s << '\n' << "// 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"; + s << "\n// main header\n" << "#include \"" + << HeaderGenerator::headerFileNameForContext(classContext) << "\"\n"; + + if (!innerClasses.isEmpty()) { + s << "\n// inner classes\n"; + for (const auto &innerClass : innerClasses) { + GeneratorContext innerClassContext = contextForClass(innerClass); + s << "#include \"" + << HeaderGenerator::headerFileNameForContext(innerClassContext) << "\"\n"; } } - AbstractMetaEnumList classEnums = metaClass->enums(); - metaClass->getEnumsFromInvisibleNamespacesToBeGenerated(&classEnums); + if (avoidProtectedHack()) + s << baseWrapperIncludes(classContext); - //Extra includes - QList<Include> includes; - if (!classContext.useWrapper()) - includes += metaClass->typeEntry()->extraIncludes(); - for (const 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() << '\n'; - s << '\n'; + for (const auto &g : includes) + s << g; + + // C++ includes + std::sort(cppIncludes.begin(), cppIncludes.end()); + s << '\n'; + for (const auto &i : std::as_const(cppIncludes)) + s << "#include <" << i << ">\n"; +} + +// Write methods definition +void CppGenerator::writePyMethodDefs(TextStream &s, const QString &className, + const QString &methodsDefinitions) +{ + s << "static PyMethodDef " << className << "_methods[] = {\n" << indent + << methodsDefinitions << METHOD_DEF_SENTINEL << outdent << "};\n\n"; +} + +void CppGenerator::writeModuleCodeSnips(TextStream &s, const CodeSnipList &codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language) const +{ + if (!codeSnips.isEmpty()) { + try { + writeCodeSnips(s, codeSnips, position, language); + } catch (const std::exception &e) { + throw Exception(msgSnippetError("module source of "_L1 + moduleName(), e.what())); + } + } +} + +bool CppGenerator::hasHashFunction(const AbstractMetaClassCPtr &c) +{ + return !c->typeEntry()->hashFunction().isEmpty() + || c->hasHashFunction(); +} + +static bool needsTypeDiscoveryFunction(const AbstractMetaClassCPtr &c) +{ + return c->baseClass() != nullptr + && (c->isPolymorphic() || !c->typeEntry()->polymorphicIdValue().isEmpty()); +} + +static void writeAddedTypeSignatures(TextStream &s, const ComplexTypeEntryCPtr &te) +{ + for (const auto &e : te->addedPyMethodDefEntrys()) { + if (auto count = e.signatures.size()) { + for (qsizetype i = 0; i < count; ++i) { + if (count > 1) + s << i << ':'; + s << e.signatures.at(i) << '\n'; + } + } } +} - s << "\n#include <cctype>\n#include <cstring>\n"; +/// Function used to write the class generated binding code on the buffer +/// \param s the output buffer +/// \param classContext the pointer to metaclass information +void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classContext) +{ + if (classContext.forSmartPointer()) { + generateSmartPointerClass(s, classContext); + return; + } - if (metaClass->typeEntry()->typeFlags() & ComplexTypeEntry::Deprecated) + s.setLanguage(TextStream::Language::Cpp); + AbstractMetaClassCPtr metaClass = classContext.metaClass(); + const auto typeEntry = metaClass->typeEntry(); + + auto innerClasses = metaClass->innerClasses(); + for (auto it = innerClasses.begin(); it != innerClasses.end(); ) { + auto innerTypeEntry = (*it)->typeEntry(); + if (shouldGenerate(innerTypeEntry) && !innerTypeEntry->isSmartPointer()) + ++it; + else + it = innerClasses.erase(it); + } + + AbstractMetaEnumList classEnums = metaClass->enums(); + metaClass->getEnumsFromInvisibleNamespacesToBeGenerated(&classEnums); + + IncludeGroupList includeGroups; + if (!classContext.useWrapper() || !avoidProtectedHack()) + includeGroups.append(classIncludes(metaClass)); + generateIncludes(s, classContext, includeGroups, innerClasses); + + if (typeEntry->typeFlags().testFlag(ComplexTypeEntry::Deprecated)) s << "#Deprecated\n"; // Use class base namespace { - const AbstractMetaClass *context = metaClass->enclosingClass(); + AbstractMetaClassCPtr context = metaClass->enclosingClass(); while (context) { if (context->isNamespace() && !context->enclosingClass() - && static_cast<const NamespaceTypeEntry *>(context->typeEntry())->generateUsing()) { + && std::static_pointer_cast<const NamespaceTypeEntry>(context->typeEntry())->generateUsing()) { s << "\nusing namespace " << context->qualifiedCppName() << ";\n"; break; } @@ -470,149 +582,109 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon } } - s << "\n\n" << typeNameFunc << '\n'; - - // 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 << "\";"; - } + s << '\n'; // class inject-code native/beginning - if (!metaClass->typeEntry()->codeSnips().isEmpty()) { - writeClassCodeSnips(s, metaClass->typeEntry()->codeSnips(), + if (!typeEntry->codeSnips().isEmpty()) { + writeClassCodeSnips(s, typeEntry->codeSnips(), TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode, classContext); s << '\n'; } // python conversion rules - if (metaClass->typeEntry()->hasTargetConversionRule()) { - s << "// Python Conversion\n"; - s << metaClass->typeEntry()->targetConversionRule() << '\n'; + if (typeEntry->isValue()) { + auto vte = std::static_pointer_cast<const ValueTypeEntry>(typeEntry); + if (vte->hasTargetConversionRule()) { + s << "// Python Conversion\n"; + s << vte->targetConversionRule() << '\n'; + } } if (classContext.useWrapper()) { s << "// Native ---------------------------------------------------------\n\n"; if (avoidProtectedHack() && usePySideExtensions()) { - s << "void " << classContext.wrapperName() << "::pysideInitQtMetaTypes()\n{\n"; - Indentation indent(s); + s << "void " << classContext.wrapperName() << "::pysideInitQtMetaTypes()\n{\n" + << indent; writeInitQtMetaTypeFunctionBody(s, classContext); - s << "}\n\n"; + s << outdent << "}\n\n"; } - const auto &funcs = filterFunctions(metaClass); int maxOverrides = 0; writeCacheResetNative(s, classContext); - for (const auto &func : funcs) { - const bool notAbstract = !func->isAbstract(); - if ((func->isPrivate() && notAbstract && !func->isVisibilityModifiedToPrivate()) - || (func->isModifiedRemoved() && notAbstract)) - continue; - if (func->functionType() == AbstractMetaFunction::ConstructorFunction && !func->isUserAdded()) + for (const auto &func : metaClass->functions()) { + const auto generation = functionGeneration(func); + if (generation.testFlag(FunctionGenerationFlag::WrapperConstructor)) writeConstructorNative(s, classContext, func); - else if (shouldWriteVirtualMethodNative(func)) + else if (generation.testFlag(FunctionGenerationFlag::VirtualMethod)) writeVirtualMethodNative(s, func, maxOverrides++); } - if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) { - if (usePySideExtensions() && metaClass->isQObject()) - writeMetaObjectMethod(s, classContext); + if (shouldGenerateMetaObjectFunctions(metaClass)) + writeMetaObjectMethod(s, classContext); + if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) writeDestructorNative(s, classContext); - } } + for (const auto &f : metaClass->userAddedPythonOverrides()) + writeUserAddedPythonOverride(s, f); + StringStream smd(TextStream::Language::Cpp); StringStream md(TextStream::Language::Cpp); StringStream signatureStream(TextStream::Language::Cpp); - s << "\n// Target ---------------------------------------------------------\n\n" - << "extern \"C\" {\n"; + s << openTargetExternC; + const auto &functionGroups = getFunctionGroups(metaClass); for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) { + if (contains(sequenceProtocols(), it.key()) || contains(mappingProtocols(), it.key())) + continue; const AbstractMetaFunctionCList &overloads = it.value(); if (overloads.isEmpty()) continue; const auto rfunc = overloads.constFirst(); - if (contains(sequenceProtocols(), rfunc->name()) - || contains(mappingProtocols(), rfunc->name())) { - continue; - } - OverloadData overloadData(overloads, api()); 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, overloadData, classContext); writeSignatureInfo(signatureStream, overloadData); } // call operators - else if (rfunc->name() == QLatin1String("operator()")) { + else if (rfunc->name() == u"operator()") { writeMethodWrapper(s, overloadData, classContext); writeSignatureInfo(signatureStream, overloadData); } 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. - auto pointerToInnerType = - AbstractMetaType::fromString(pointerToInnerTypeName); - Q_ASSERT(pointerToInnerType.has_value()); - auto mutableRfunc = overloads.constFirst(); - qSharedPointerConstCast<AbstractMetaFunction>(mutableRfunc)->setType(pointerToInnerType.value()); - } 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, overloadData, classContext); writeSignatureInfo(signatureStream, overloadData); // For a mixture of static and member function overloads, // a separate PyMethodDef entry is written which is referenced // in the PyMethodDef list and later in getattro() for handling // the non-static case. + const auto defEntries = methodDefinitionEntries(overloadData); if (OverloadData::hasStaticAndInstanceFunctions(overloads)) { QString methDefName = cpythonMethodDefinitionName(rfunc); - smd << "static PyMethodDef " << methDefName << " = " << indent; - writeMethodDefinitionEntries(smd, overloadData, 1); - smd << outdent << ";\n\n"; + smd << "static PyMethodDef " << methDefName << " = " << indent + << defEntries.constFirst() << outdent << ";\n\n"; } - writeMethodDefinition(md, overloadData); + const auto &fname = rfunc->name(); + if (!m_tpFuncs.contains(fname) && !m_nbFuncs.contains(fname)) + md << defEntries; } } + for (const auto &pyMethodDef : typeEntry->addedPyMethodDefEntrys()) + md << pyMethodDef << ",\n"; + + if (typeEntry->isValue()) + writeCopyFunction(s, md, signatureStream, classContext); + const QString methodsDefinitions = md.toString(); const QString singleMethodDefinitions = smd.toString(); const QString className = chopType(cpythonTypeName(metaClass)); - if (metaClass->typeEntry()->isValue() || metaClass->typeEntry()->isSmartPointer()) { - writeCopyFunction(s, classContext); - signatureStream << fullPythonClassName(metaClass) << ".__copy__()\n"; - } - // Write single method definitions s << singleMethodDefinitions; @@ -628,77 +700,41 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon s << '\n'; s << "static const char *" << className << "_PropertyStrings[] = {\n" << indent; - for (const auto &entry : qAsConst(sorter)) + for (const auto &entry : std::as_const(sorter)) + s << entry << ",\n"; + s << NULL_PTR << " // Sentinel\n" + << outdent << "};\n\n"; + + } + // PYSIDE-1735: Write an EnumFlagInfo structure + QStringList sorter; + for (const auto &entry : std::as_const(classEnums)) + sorter.append(BuildEnumFlagInfo(entry)); + sorter.sort(); + if (!sorter.empty()) { + s << "static const char *" << className << "_EnumFlagInfo[] = {\n" << indent; + for (const auto &entry : std::as_const(sorter)) s << entry << ",\n"; s << NULL_PTR << " // Sentinel\n" << outdent << "};\n\n"; } // Write methods definition - s << "static PyMethodDef " << className << "_methods[] = {\n" << indent - << methodsDefinitions << '\n'; - if (metaClass->typeEntry()->isValue() || metaClass->typeEntry()->isSmartPointer()) { - s << "{\"__copy__\", reinterpret_cast<PyCFunction>(" << className << "___copy__)" - << ", METH_NOARGS},\n"; - } - s << '{' << NULL_PTR << ", " << NULL_PTR << "} // Sentinel\n" << outdent - << "};\n\n"; + writePyMethodDefs(s, className, methodsDefinitions); // 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); - } - - const auto f = boolCast(metaClass); - if (!f.isNull()) { - ErrorCode errorCode(-1); - s << "static int " << cpythonBaseName(metaClass) << "___nb_bool(PyObject *self)\n" - << "{\n" << indent; - writeCppSelfDefinition(s, classContext); - - const bool allowThread = f->allowThread(); - if (allowThread) - s << "int result;\n" << BEGIN_ALLOW_THREADS << "\nresult = "; - else - s << "return "; + if ((attroCheck & AttroCheckFlag::GetattroMask) != 0) + writeGetattroFunction(s, attroCheck, classContext); + if ((attroCheck & AttroCheckFlag::SetattroMask) != 0) + writeSetattroFunction(s, attroCheck, classContext); - if (f->isOperatorBool()) - s << '*' << CPP_SELF_VAR << " ? 1 : 0;\n"; - else - s << CPP_SELF_VAR << "->isNull() ? 0 : 1;\n"; - - if (allowThread) - s << END_ALLOW_THREADS << "\nreturn result;\n"; - s << outdent << "}\n\n"; - } - - if (supportsNumberProtocol(metaClass) && !metaClass->typeEntry()->isSmartPointer()) { - const QList<AbstractMetaFunctionCList> opOverloads = filterGroupedOperatorFunctions( - metaClass, - OperatorQueryOption::ArithmeticOp - | OperatorQueryOption::IncDecrementOp - | OperatorQueryOption::LogicalOp - | OperatorQueryOption::BitwiseOp); - - for (const AbstractMetaFunctionCList &allOverloads : opOverloads) { - AbstractMetaFunctionCList overloads; - for (const auto &func : allOverloads) { - if (!func->isModifiedRemoved() - && !func->isPrivate() - && (func->ownerClass() == func->implementingClass() || func->isAbstract())) - overloads.append(func); - } - - if (overloads.isEmpty()) - continue; + if (const auto f = boolCast(metaClass) ; f.has_value()) + writeNbBoolFunction(classContext, f.value(), s); + if (supportsNumberProtocol(metaClass)) { + const auto numberProtocolOps = numberProtocolOperators(metaClass); + for (const auto &overloads : numberProtocolOps) { OverloadData overloadData(overloads, api()); writeMethodWrapper(s, overloadData, classContext); writeSignatureInfo(signatureStream, overloadData); @@ -713,12 +749,12 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon writeMappingMethods(s, metaClass, classContext); } - if (!metaClass->isNamespace() && metaClass->hasComparisonOperatorOverload()) { + if (generateRichComparison(classContext)) { s << "// Rich comparison\n"; writeRichCompareFunction(s, classContext); } - if (shouldGenerateGetSetList(metaClass) && !classContext.forSmartPointer()) { + if (shouldGenerateGetSetList(metaClass)) { const AbstractMetaFieldList &fields = metaClass->fields(); for (const AbstractMetaField &metaField : fields) { if (metaField.canGenerateGetter()) @@ -761,13 +797,13 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon cpythonGetterFunctionName(property, metaClass), setter); } } - s << '{' << NULL_PTR << "} // Sentinel\n" + s << "{nullptr, nullptr, nullptr, nullptr, nullptr} // Sentinel\n" << outdent << "};\n\n"; } - s << "} // extern \"C\"\n\n"; + s << closeExternC; - if (!metaClass->typeEntry()->hashFunction().isEmpty()) + if (hasHashFunction(metaClass)) writeHashFunction(s, classContext); // Write tp_traverse and tp_clear functions. @@ -777,27 +813,38 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon writeClassDefinition(s, metaClass, classContext); s << '\n'; - if (metaClass->isPolymorphic() && metaClass->baseClass()) + if (needsTypeDiscoveryFunction(metaClass)) { writeTypeDiscoveryFunction(s, metaClass); - - writeFlagsNumberMethodsDefinitions(s, classEnums); - s << '\n'; + s << '\n'; + } writeConverterFunctions(s, metaClass, classContext); + writeAddedTypeSignatures(signatureStream, typeEntry); writeClassRegister(s, metaClass, classContext, signatureStream); if (metaClass->hasStaticFields()) writeStaticFieldInitialization(s, metaClass); // class inject-code native/end - if (!metaClass->typeEntry()->codeSnips().isEmpty()) { - writeClassCodeSnips(s, metaClass->typeEntry()->codeSnips(), + if (!typeEntry->codeSnips().isEmpty()) { + writeClassCodeSnips(s, typeEntry->codeSnips(), TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode, classContext); s << '\n'; } } +void CppGenerator::writeMethodWrapper(TextStream &s, TextStream &definitionStream, + TextStream &signatureStream, + const AbstractMetaFunctionCList &overloads, + const GeneratorContext &classContext) const +{ + OverloadData overloadData(overloads, api()); + writeMethodWrapper(s, overloadData, classContext); + writeSignatureInfo(signatureStream, overloadData); + definitionStream << methodDefinitionEntries(overloadData); +} + void CppGenerator::writeCacheResetNative(TextStream &s, const GeneratorContext &classContext) { s << "void " << classContext.wrapperName() @@ -809,7 +856,7 @@ void CppGenerator::writeCacheResetNative(TextStream &s, const GeneratorContext & void CppGenerator::writeConstructorNative(TextStream &s, const GeneratorContext &classContext, const AbstractMetaFunctionCPtr &func) const { - const QString qualifiedName = classContext.wrapperName() + QLatin1String("::"); + const QString qualifiedName = classContext.wrapperName() + u"::"_s; s << functionSignature(func, qualifiedName, QString(), OriginalTypeDescription | SkipDefaultValues); s << " : "; @@ -828,7 +875,7 @@ void CppGenerator::writeConstructorNative(TextStream &s, const GeneratorContext } void CppGenerator::writeDestructorNative(TextStream &s, - const GeneratorContext &classContext) const + const GeneratorContext &classContext) { s << classContext.wrapperName() << "::~" << classContext.wrapperName() << "()\n{\n" << indent; @@ -840,64 +887,51 @@ Shiboken::Object::destroy(wrapper, this); )" << outdent << "}\n"; } -static bool allArgumentsRemoved(const AbstractMetaFunctionCPtr& 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; -} - // Return type for error messages when getting invalid types from virtual // methods implemented in Python in C++ wrappers QString CppGenerator::getVirtualFunctionReturnTypeName(const AbstractMetaFunctionCPtr &func) const { if (func->type().isVoid()) - return QLatin1String("\"\""); + return u"\"\""_s; - if (!func->typeReplaced(0).isEmpty()) - return QLatin1Char('"') + func->typeReplaced(0) + QLatin1Char('"'); + if (func->isTypeModified()) + return u'"' + func->modifiedTypeName() + u'"'; // SbkType would return null when the type is a container. auto typeEntry = func->type().typeEntry(); if (typeEntry->isContainer()) { - const auto *cte = static_cast<const ContainerTypeEntry *>(typeEntry); + const auto cte = std::static_pointer_cast<const ContainerTypeEntry>(typeEntry); switch (cte->containerKind()) { case ContainerTypeEntry::ListContainer: + case ContainerTypeEntry::SpanContainer: break; case ContainerTypeEntry::SetContainer: - return uR"("set")"_qs; + return uR"("set")"_s; break; case ContainerTypeEntry::MapContainer: case ContainerTypeEntry::MultiMapContainer: - return uR"("dict")"_qs; + return uR"("dict")"_s; break; case ContainerTypeEntry::PairContainer: - return uR"("tuple")"_qs; + return uR"("tuple")"_s; break; } - return uR"("list")"_qs; + return uR"("list")"_s; } if (typeEntry->isSmartPointer()) - return QLatin1Char('"') + typeEntry->qualifiedCppName() + QLatin1Char('"'); + return u'"' + typeEntry->qualifiedCppName() + u'"'; if (avoidProtectedHack()) { auto metaEnum = api().findAbstractMetaEnum(func->type().typeEntry()); - if (metaEnum.has_value() && metaEnum->isProtected()) { - return QLatin1Char('"') + protectedEnumSurrogateName(metaEnum.value()) - + QLatin1Char('"'); - } + if (metaEnum.has_value() && metaEnum->isProtected()) + return u'"' + protectedEnumSurrogateName(metaEnum.value()) + u'"'; } if (func->type().isPrimitive()) - return QLatin1Char('"') + func->type().name() + QLatin1Char('"'); + return u'"' + func->type().name() + u'"'; - return QLatin1String("reinterpret_cast<PyTypeObject *>(Shiboken::SbkType< ") - + typeEntry->qualifiedCppName() + QLatin1String(" >())->tp_name"); + return u"Shiboken::SbkType< "_s + + typeEntry->qualifiedCppName() + u" >()->tp_name"_s; } // When writing an overridden method of a wrapper class, write the part @@ -907,8 +941,8 @@ void CppGenerator::writeVirtualMethodCppCall(TextStream &s, const QString &funcName, const CodeSnipList &snips, const AbstractMetaArgument *lastArg, - const TypeEntry *retType, - const QString &returnStatement) const + const TypeEntryCPtr &retType, + const QString &returnStatement, bool hasGil) const { if (!snips.isEmpty()) { writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, @@ -916,13 +950,17 @@ void CppGenerator::writeVirtualMethodCppCall(TextStream &s, } if (func->isAbstract()) { - s << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '" - << func->ownerClass()->name() << '.' << funcName - << "()' not implemented.\");\n" + if (!hasGil) + s << "Shiboken::GilState gil;\n"; + s << "Shiboken::Errors::setPureVirtualMethodError(\"" + << func->ownerClass()->name() << '.' << funcName << "\");\n" << returnStatement << '\n'; return; } + if (hasGil) + s << "gil.release();\n"; + if (retType) s << "return "; s << "this->::" << func->implementingClass()->qualifiedCppName() << "::"; @@ -938,17 +976,24 @@ void CppGenerator::writeVirtualMethodCppCall(TextStream &s, } // Determine the return statement (void or a result value). -QString CppGenerator::virtualMethodReturn(TextStream &s, const ApiExtractorResult &api, - const AbstractMetaFunctionCPtr &func, - const FunctionModificationList &functionModifications) + +CppGenerator::VirtualMethodReturn + CppGenerator::virtualMethodReturn(const ApiExtractorResult &api, + const AbstractMetaFunctionCPtr &func, + const FunctionModificationList &functionModifications) { - if (func->isVoid()) - return QLatin1String("return;"); + VirtualMethodReturn result; + if (func->isVoid()) { + result.statement = "return;"_L1; + return result; + } + + result.statement = "return "_L1; const AbstractMetaType &returnType = func->type(); for (const FunctionModification &mod : functionModifications) { for (const ArgumentModification &argMod : mod.argument_mods()) { if (argMod.index() == 0 && !argMod.replacedDefaultExpression().isEmpty()) { - static const QRegularExpression regex(QStringLiteral("%(\\d+)")); + static const QRegularExpression regex("%(\\d+)"_L1); Q_ASSERT(regex.isValid()); QString expr = argMod.replacedDefaultExpression(); for (int offset = 0; ; ) { @@ -956,7 +1001,7 @@ QString CppGenerator::virtualMethodReturn(TextStream &s, const ApiExtractorResul if (!match.hasMatch()) break; const int argId = match.capturedView(1).toInt() - 1; - if (argId < 0 || argId > func->arguments().count()) { + if (argId < 0 || argId > func->arguments().size()) { qCWarning(lcShiboken, "The expression used in return value contains an invalid index."); break; } @@ -964,56 +1009,178 @@ QString CppGenerator::virtualMethodReturn(TextStream &s, const ApiExtractorResul offset = match.capturedStart(1); } DefaultValue defaultReturnExpr(DefaultValue::Custom, expr); - return QLatin1String("return ") + defaultReturnExpr.returnValue() - + QLatin1Char(';'); + result.statement += defaultReturnExpr.returnValue() + u';'; + return result; } } } QString errorMessage; const auto defaultReturnExpr = minimalConstructor(api, returnType, &errorMessage); if (!defaultReturnExpr.has_value()) { - QString errorMsg = QLatin1String(__FUNCTION__) + QLatin1String(": "); - if (const AbstractMetaClass *c = func->implementingClass()) - errorMsg += c->qualifiedCppName() + QLatin1String("::"); - errorMsg += func->signature(); + QString errorMsg = QLatin1StringView(__FUNCTION__) + u": "_s + + func->classQualifiedSignature(); errorMsg = msgCouldNotFindMinimalConstructor(errorMsg, func->type().cppSignature(), errorMessage); - qCWarning(lcShiboken).noquote().nospace() << errorMsg; - s << "\n#error " << errorMsg << '\n'; + throw Exception(errorMsg); + } + + result.needsReference = returnType.referenceType() == LValueReference; + result.statement += (result.needsReference + ? virtualMethodStaticReturnVar : defaultReturnExpr->returnValue()) + u';'; + return result; +} + +// Create an argument for Py_BuildValue() when writing virtual methods. +// Return a pair of (argument, format-char). +std::pair<QString, QChar> CppGenerator::virtualMethodNativeArg(const AbstractMetaFunctionCPtr &func, + const AbstractMetaArgument &arg) +{ + if (func->hasConversionRule(TypeSystem::TargetLangCode, arg.argumentIndex() + 1)) + return {arg.name() + CONV_RULE_OUT_VAR_SUFFIX, u'N'}; + + const auto &type = arg.type(); + auto argTypeEntry = type.typeEntry(); + // Check for primitive types convertible by Py_BuildValue() + if (argTypeEntry->isPrimitive() && !type.isCString()) { + const auto pte = basicReferencedTypeEntry(argTypeEntry); + auto it = formatUnits().constFind(pte->name()); + if (it != formatUnits().constEnd()) + return {arg.name(), it.value()}; + } + + // Rest: convert + StringStream ac(TextStream::Language::Cpp); + writeToPythonConversion(ac, type, func->ownerClass(), arg.name()); + return {ac.toString(), u'N'}; +} + +static const char PYTHON_ARGS_ARRAY[] = "pyArgArray"; + +void CppGenerator::writeVirtualMethodNativeVectorCallArgs(TextStream &s, + const AbstractMetaFunctionCPtr &func, + const AbstractMetaArgumentList &arguments, + const QList<int> &invalidateArgs) +{ + Q_ASSERT(!arguments.isEmpty()); + s << "PyObject *" << PYTHON_ARGS_ARRAY <<'[' << arguments.size() << "] = {\n" << indent; + const qsizetype last = arguments.size() - 1; + for (qsizetype i = 0; i <= last; ++i) { + const AbstractMetaArgument &arg = arguments.at(i); + if (func->hasConversionRule(TypeSystem::TargetLangCode, arg.argumentIndex() + 1)) { + s << arg.name() + CONV_RULE_OUT_VAR_SUFFIX; + } else { + writeToPythonConversion(s, arg.type(), func->ownerClass(), arg.name()); + } + if (i < last) + s << ','; + s << '\n'; + } + s << outdent << "};\n"; + + if (!invalidateArgs.isEmpty()) + s << '\n'; + for (int index : invalidateArgs) { + s << "const bool invalidateArg" << index << " = Py_REFCNT(" << PYTHON_ARGS_ARRAY << + '[' << index - 1 << "]) == 1;\n"; + } +} + +void CppGenerator::writeVirtualMethodNativeArgs(TextStream &s, + const AbstractMetaFunctionCPtr &func, + const AbstractMetaArgumentList &arguments, + const QList<int> &invalidateArgs) +{ + s << "Shiboken::AutoDecRef " << PYTHON_ARGS << '('; + if (arguments.isEmpty()) { + s << "PyTuple_New(0));\n"; + return; + } + + QString format; + QStringList argConversions; + for (const AbstractMetaArgument &arg : arguments) { + auto argPair = virtualMethodNativeArg(func, arg); + argConversions.append(argPair.first); + format += argPair.second; } - if (returnType.referenceType() == LValueReference) { - s << "static " << returnType.typeEntry()->qualifiedCppName() - << " result;\n"; - return QLatin1String("return result;"); + + s << "Py_BuildValue(\"(" << format << ")\",\n" + << indent << argConversions.join(u",\n"_s) << outdent << "\n));\n"; + + for (int index : std::as_const(invalidateArgs)) { + s << "bool invalidateArg" << index << " = Py_REFCNT(PyTuple_GET_ITEM(" << PYTHON_ARGS + << ", " << index - 1 << ")) == 1;\n"; } - return QLatin1String("return ") + defaultReturnExpr->returnValue() - + QLatin1Char(';'); +} + +static bool isArgumentNotRemoved(const AbstractMetaArgument &a) +{ + return !a.isModifiedRemoved(); +} + +// PyObject_Vectorcall(): since 3.9 +static const char vectorCallCondition[] = + "#if !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)\n"; + +// PyObject_CallNoArgs(): since 3.9, stable API since 3.10 +static const char noArgsCallCondition[] = + "#if !defined(PYPY_VERSION) && ((defined(Py_LIMITED_API) && Py_LIMITED_API >= 0x030A0000) || !defined(Py_LIMITED_API))\n"; +static const char inverseNoArgsCallCondition[] = + "#if defined(PYPY_VERSION) || (defined(Py_LIMITED_API) && Py_LIMITED_API < 0x030A0000)\n"; + +static inline void writeVirtualMethodStaticReturnVar(TextStream &s, const AbstractMetaFunctionCPtr &func) +{ + s << "static " << func->type().typeEntry()->qualifiedCppName() << ' ' + << virtualMethodStaticReturnVar << ";\n"; +} + +static void writeFuncNameVar(TextStream &s, const AbstractMetaFunctionCPtr &func, + const QString &funcName) +{ + // PYSIDE-1019: Add info about properties + int propFlag = 0; + if (func->isPropertyReader()) + propFlag |= 1; + if (func->isPropertyWriter()) + propFlag |= 2; + if (propFlag && func->isStatic()) + propFlag |= 4; + QString propStr; + if (propFlag != 90) + propStr = QString::number(propFlag) + u':'; + + if (propFlag != 0) + s << "// This method belongs to a property.\n"; + s << "static const char *funcName = \""; + if (propFlag != 0) + s << propFlag << ':'; + s << funcName << "\";\n"; } void CppGenerator::writeVirtualMethodNative(TextStream &s, const AbstractMetaFunctionCPtr &func, int cacheIndex) const { - //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(); + TypeEntryCPtr retType = func->type().typeEntry(); const QString funcName = func->isOperatorOverload() ? pythonOperatorFunctionName(func) : func->definitionNames().constFirst(); - QString prefix = wrapperName(func->ownerClass()) + QLatin1String("::"); - s << functionSignature(func, prefix, QString(), Generator::SkipDefaultValues|Generator::OriginalTypeDescription) + QString prefix = wrapperName(func->ownerClass()) + u"::"_s; + s << functionSignature(func, prefix, QString(), Generator::SkipDefaultValues | + Generator::OriginalTypeDescription) << "\n{\n" << indent; - const QString returnStatement = virtualMethodReturn(s, api(), func, - func->modifications()); + const auto returnStatement = virtualMethodReturn(api(), func, + func->modifications()); - if (func->isAbstract() && func->isModifiedRemoved()) { - qCWarning(lcShiboken, "%s", qPrintable(msgPureVirtualFunctionRemoved(func.data()))); - s << returnStatement << '\n' << outdent << "}\n\n"; + if (returnStatement.needsReference) + writeVirtualMethodStaticReturnVar(s, func); + + const bool isAbstract = func->isAbstract(); + if (isAbstract && func->isModifiedRemoved()) { + qCWarning(lcShiboken, "%s", qPrintable(msgPureVirtualFunctionRemoved(func.get()))); + s << returnStatement.statement << '\n' << outdent << "}\n\n"; return; } @@ -1022,7 +1189,7 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast(); - //Write declaration/native injected code + // Write declaration/native injected code. if (!snips.isEmpty()) { writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionDeclaration, TypeSystem::ShellCode, func, false, lastArg); @@ -1037,243 +1204,269 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, << 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 << "if (m_PyMethodCache[" << cacheIndex << "])" << (multi_line ? " {\n" : "\n"); - { - Indentation indentation(s); - writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType, - returnStatement); - } + // PYSIDE-803: Build a boolean cache for unused overrides + const bool multi_line = func->isVoid() || !snips.isEmpty() || isAbstract; + s << "if (m_PyMethodCache[" << cacheIndex << "])" << (multi_line ? " {\n" : "\n") + << indent; + writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType, + returnStatement.statement, false); + s << outdent; if (multi_line) s << "}\n"; s << "Shiboken::GilState gil;\n"; // Get out of virtual method call if someone already threw an error. - s << "if (PyErr_Occurred())\n" << indent - << returnStatement << '\n' << outdent; - - //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 << "if (" << shibokenErrorsOccurred << ")\n" << indent + << returnStatement.statement << '\n' << outdent; s << "static PyObject *nameCache[2] = {};\n"; - if (propFlag) - s << "// This method belongs to a property.\n"; - s << "static const char *funcName = \"" << propStr << funcName << "\";\n" - << "Shiboken::AutoDecRef " << PYTHON_OVERRIDE_VAR + writeFuncNameVar(s, func, funcName); + s << "Shiboken::AutoDecRef " << PYTHON_OVERRIDE_VAR << "(Shiboken::BindingManager::instance().getOverride(this, nameCache, funcName));\n" - << "if (" << PYTHON_OVERRIDE_VAR << ".isNull()) {\n" - << indent << "gil.release();\n"; + << "if (" << PYTHON_OVERRIDE_VAR << ".isNull()) {\n" << indent; if (useOverrideCaching(func->ownerClass())) s << "m_PyMethodCache[" << cacheIndex << "] = true;\n"; writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType, - returnStatement); + returnStatement.statement, true); s << outdent << "}\n\n"; //WS - writeConversionRule(s, func, TypeSystem::TargetLangCode, false); - - s << "Shiboken::AutoDecRef " << PYTHON_ARGS << "("; + if (!snips.isEmpty()) { + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionPyOverride, + TypeSystem::ShellCode, func, false, lastArg); + } - 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; + writeVirtualMethodPythonOverride(s, func, snips, returnStatement); +} - const auto &argType = arg.type(); - const auto *argTypeEntry = 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()) { - const auto *pte = static_cast<const PrimitiveTypeEntry *>(argTypeEntry); - if (pte->basicReferencedTypeEntry()) - pte = pte->basicReferencedTypeEntry(); - convert = !formatUnits().contains(pte->name()); - } +void CppGenerator::writeVirtualMethodPythonOverride(TextStream &s, + const AbstractMetaFunctionCPtr &func, + const CodeSnipList &snips, + const VirtualMethodReturn &returnStatement) const +{ + writeConversionRule(s, func, TypeSystem::TargetLangCode, false); - StringStream ac(TextStream::Language::Cpp); - if (!func->conversionRule(TypeSystem::TargetLangCode, arg.argumentIndex() + 1).isEmpty()) { - // Has conversion rule. - ac << arg.name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX); + bool invalidateReturn = false; + QList<int> invalidateArgs; + for (const FunctionModification &funcMod : func->modifications()) { + for (const ArgumentModification &argMod : funcMod.argument_mods()) { + const int index = argMod.index(); + if (index == 0) { + if (argMod.targetOwnerShip() == TypeSystem::CppOwnership) + invalidateReturn = true; } else { - QString argName = arg.name(); - if (convert) - writeToPythonConversion(ac, arg.type(), func->ownerClass(), argName); - else - ac << argName; + const int actualIndex = func->actualArgumentIndex(index - 1) + 1; + if (argMod.resetAfterUse() && !invalidateArgs.contains(actualIndex)) + invalidateArgs.append(actualIndex); } - - argConversions << ac.toString(); } + } + std::sort(invalidateArgs.begin(), invalidateArgs.end()); - s << "Py_BuildValue(\"(" << getFormatUnitString(func, false) << ")\",\n" - << argConversions.join(QLatin1String(",\n")) << "\n));\n"; + auto arguments = func->arguments(); + auto removedEnd = std::stable_partition(arguments.begin(), arguments.end(), + isArgumentNotRemoved); + if (func->isAbstract()) { // Base function is not called, indicate unused arguments. + for (auto it = removedEnd; it != arguments.end(); ++it) + s << sbkUnusedVariableCast(it->name()); } + arguments.erase(removedEnd, arguments.end()); - bool invalidateReturn = false; - QSet<int> invalidateArgs; - for (const FunctionModification &funcMod : func->modifications()) { - for (const ArgumentModification &argMod : funcMod.argument_mods()) { - const int index = argMod.index(); - if (argMod.resetAfterUse() && !invalidateArgs.contains(index)) { - invalidateArgs.insert(index); - s << "bool invalidateArg" << index - << " = PyTuple_GET_ITEM(" << PYTHON_ARGS << ", " - << index - 1 << ")->ob_refcnt == 1;\n"; - } else if (index == 0 && - argMod.targetOwnerShip() == TypeSystem::CppOwnership) { - invalidateReturn = true; - } + // FIXME PYSIDE-7: new functions PyObject_Vectorcall() (since 3.9) and + // PyObject_CallNoArgs() (since 3.9, stable API since 3.10) might have + // become part of the stable API? + + // Code snips might expect the args tuple, don't generate new code + const bool generateNewCall = snips.isEmpty(); + const qsizetype argCount = arguments.size(); + const char *newCallCondition = argCount == 0 ? noArgsCallCondition : vectorCallCondition; + if (generateNewCall) { + if (argCount > 0) { + s << newCallCondition; + writeVirtualMethodNativeVectorCallArgs(s, func, arguments, invalidateArgs); + s << "#else\n"; + } else { + s << inverseNoArgsCallCondition; } } + writeVirtualMethodNativeArgs(s, func, arguments, invalidateArgs); + if (generateNewCall) + s << "#endif\n"; s << '\n'; if (!snips.isEmpty()) { if (func->injectedCodeUsesPySelf()) s << "PyObject *pySelf = BindingManager::instance().retrieveWrapper(this);\n"; - const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast(); + const AbstractMetaArgument *lastArg = func->arguments().isEmpty() + ? nullptr : &func->arguments().constLast(); writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode, func, false, lastArg); } + qsizetype returnIndirections = 0; + if (!func->injectedCodeCallsPythonOverride()) { + if (generateNewCall) { + s << newCallCondition << "Shiboken::AutoDecRef " << PYTHON_RETURN_VAR << '('; + if (argCount > 0) { + s << "PyObject_Vectorcall(" << PYTHON_OVERRIDE_VAR << ", " + << PYTHON_ARGS_ARRAY << ", " << argCount << ", nullptr));\n"; + for (int argIndex : std::as_const(invalidateArgs)) { + s << "if (invalidateArg" << argIndex << ")\n" << indent + << "Shiboken::Object::invalidate(" << PYTHON_ARGS_ARRAY + << '[' << (argIndex - 1) << "]);\n" << outdent; + } + for (qsizetype i = 0, size = arguments.size(); i < size; ++i) + s << "Py_DECREF(" << PYTHON_ARGS_ARRAY << '[' << i << "]);\n"; + } else { + s << "PyObject_CallNoArgs(" << PYTHON_OVERRIDE_VAR << "));\n"; + } + s << "#else\n"; + } s << "Shiboken::AutoDecRef " << PYTHON_RETURN_VAR << "(PyObject_Call(" - << PYTHON_OVERRIDE_VAR << ", " << PYTHON_ARGS << ", nullptr));\n" + << PYTHON_OVERRIDE_VAR << ", " << PYTHON_ARGS << ", nullptr));\n"; + + for (int argIndex : std::as_const(invalidateArgs)) { + s << "if (invalidateArg" << argIndex << ")\n" << indent + << "Shiboken::Object::invalidate(PyTuple_GET_ITEM(" << PYTHON_ARGS + << ", " << (argIndex - 1) << "));\n" << outdent; + } + if (generateNewCall) + s << "#endif\n"; + + s << "if (" << PYTHON_RETURN_VAR << ".isNull()) {\n" << indent << "// An error happened in python code!\n" - << "if (" << PYTHON_RETURN_VAR << ".isNull()) {\n" << indent - << "PyErr_Print();\n" << returnStatement << '\n' << outdent - << "}\n"; + << "Shiboken::Errors::storeErrorOrPrint();\n" + << returnStatement.statement << "\n" << outdent + << "}\n"; + + if (invalidateReturn) { + s << "bool invalidateArg0 = Py_REFCNT(" << PYTHON_RETURN_VAR << ") == 1;\n" + << "if (invalidateArg0)\n" << indent + << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR + << ".object());\n" << outdent; + } if (!func->isVoid()) { - if (invalidateReturn) - s << "bool invalidateArg0 = " << PYTHON_RETURN_VAR << "->ob_refcnt == 1;\n"; - if (func->typeReplaced(0) != cPyObjectT()) { + if (func->modifiedTypeName() != cPyObjectT) { s << "// Check return type\n"; - if (func->typeReplaced(0).isEmpty()) { - s << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << " = " + + if (!func->isTypeModified()) { + + s << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' + << PYTHON_TO_CPP_VAR << " =\n" << indent << cpythonIsConvertibleFunction(func->type()) - << PYTHON_RETURN_VAR << ");\n" - << "if (!" << PYTHON_TO_CPP_VAR << ") {\n"; - { - Indentation indent(s); - s << "Shiboken::warning(PyExc_RuntimeWarning, 2, "\ - "\"Invalid return value in function %s, expected %s, got %s.\", \"" - << func->ownerClass()->name() << '.' << funcName << "\", " - << getVirtualFunctionReturnTypeName(func) - << ", Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);\n" - << returnStatement << '\n'; - } - s << "}\n"; + << PYTHON_RETURN_VAR << ");\n" << outdent + << "if (!" << PYTHON_TO_CPP_VAR << ") {\n" << indent + << "Shiboken::Warnings::warnInvalidReturnValue(\"" + << func->ownerClass()->name() << "\", funcName, " + << getVirtualFunctionReturnTypeName(func) << ", " + << "Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);\n" + << returnStatement.statement << '\n' << outdent + << "}\n"; } else { - s << "// Check return type\n" - << "bool typeIsValid = "; - writeTypeCheck(s, func->type(), QLatin1String(PYTHON_RETURN_VAR), - isNumber(func->type().typeEntry()), func->typeReplaced(0)); + s << "bool typeIsValid = "; + if (func->isTypeModified()) { + writeTypeCheck(s, func->modifiedTypeName(), PYTHON_RETURN_VAR); + } else { + const bool numberType = isNumber(func->type().typeEntry()); + writeTypeCheck(s, func->type(), PYTHON_RETURN_VAR, numberType); + } + s << ";\n"; s << "if (!typeIsValid"; if (func->type().isPointerToWrapperType()) s << " && " << PYTHON_RETURN_VAR << " != Py_None"; - s << ") {\n"; - { - Indentation indent(s); - s << "Shiboken::warning(PyExc_RuntimeWarning, 2, "\ - "\"Invalid return value in function %s, expected %s, got %s.\", \"" - << func->ownerClass()->name() << '.' << funcName << "\", " - << getVirtualFunctionReturnTypeName(func) - << ", Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);\n" - << returnStatement << '\n'; - } - s << "}\n"; + s << ") {\n" << indent + << "Shiboken::Warnings::warnInvalidReturnValue(\"" + << func->ownerClass()->name() << "\", funcName, " + << getVirtualFunctionReturnTypeName(func) << ", " + << "Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);\n" + << returnStatement.statement << '\n' << outdent + << "}\n"; } } - if (!func->conversionRule(TypeSystem::NativeCode, 0).isEmpty()) { - // Has conversion rule. - writeConversionRule(s, func, TypeSystem::NativeCode, QLatin1String(CPP_RETURN_VAR)); + if (func->hasConversionRule(TypeSystem::NativeCode, 0)) { + writeConversionRule(s, func, TypeSystem::NativeCode, CPP_RETURN_VAR); } else if (!func->injectedCodeHasReturnValueAttribution(TypeSystem::NativeCode)) { - writePythonToCppTypeConversion(s, func->type(), QLatin1String(PYTHON_RETURN_VAR), - QLatin1String(CPP_RETURN_VAR), func->implementingClass()); + returnIndirections = writePythonToCppTypeConversion( + s, func->type(), PYTHON_RETURN_VAR, + CPP_RETURN_VAR, func->implementingClass(), {}); } } } - if (invalidateReturn) { - s << "if (invalidateArg0)\n" << indent - << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR - << ".object());\n" << outdent; - } - for (int argIndex : qAsConst(invalidateArgs)) { - s << "if (invalidateArg" << argIndex << ")\n" << indent - << "Shiboken::Object::invalidate(PyTuple_GET_ITEM(" << PYTHON_ARGS - << ", " << (argIndex - 1) << "));\n" << outdent; - } - - for (const FunctionModification &funcMod : func->modifications()) { for (const ArgumentModification &argMod : funcMod.argument_mods()) { if (argMod.index() == 0 && argMod.nativeOwnership() == TypeSystem::CppOwnership) { - s << "if (Shiboken::Object::checkType(" << PYTHON_RETURN_VAR << "))\n"; - Indentation indent(s); - s << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR << ");\n"; + s << "if (Shiboken::Object::checkType(" << PYTHON_RETURN_VAR << "))\n" << indent + << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR << ");\n" + << outdent; } } } if (func->hasInjectedCode()) { s << '\n'; - const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast(); + const AbstractMetaArgument *lastArg = func->arguments().isEmpty() + ? nullptr : &func->arguments().constLast(); writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode, func, false, lastArg); } if (!func->isVoid()) { s << "return "; + TypeEntryCPtr retType = func->type().typeEntry(); if (avoidProtectedHack() && retType->isEnum()) { auto metaEnum = api().findAbstractMetaEnum(retType); bool isProtectedEnum = metaEnum.has_value() && metaEnum->isProtected(); if (isProtectedEnum) { QString typeCast; if (metaEnum->enclosingClass()) - typeCast += QLatin1String("::") + metaEnum->enclosingClass()->qualifiedCppName(); - typeCast += QLatin1String("::") + metaEnum->name(); + typeCast += getFullTypeName(metaEnum->enclosingClass()); + typeCast += u"::"_s + metaEnum->name(); s << '(' << typeCast << ')'; } } - if (func->type().isWrapperPassedByReference()) - s << " *"; + + if (returnIndirections > 0) + s << QByteArray(returnIndirections, '*'); s << CPP_RETURN_VAR << ";\n"; } s << outdent << "}\n\n"; } +void CppGenerator::writeUserAddedPythonOverride(TextStream &s, + const AbstractMetaFunctionCPtr &func) const +{ + TypeEntryCPtr retType = func->type().typeEntry(); + const QString funcName = func->isOperatorOverload() + ? pythonOperatorFunctionName(func) : func->definitionNames().constFirst(); + + const CodeSnipList snips = func->hasInjectedCode() + ? func->injectedCodeSnips() : CodeSnipList(); + + QString prefix = wrapperName(func->ownerClass()) + u"::"_s; + s << '\n' << functionSignature(func, prefix, QString(), Generator::SkipDefaultValues | + Generator::OriginalTypeDescription) + << "\n{\n" << indent << sbkUnusedVariableCast("gil"); + + writeFuncNameVar(s, func, funcName); + + const auto returnStatement = virtualMethodReturn(api(), func, + func->modifications()); + writeVirtualMethodPythonOverride(s, func, snips, returnStatement); +} + void CppGenerator::writeMetaObjectMethod(TextStream &s, const GeneratorContext &classContext) const { @@ -1281,19 +1474,21 @@ void CppGenerator::writeMetaObjectMethod(TextStream &s, 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" + s << indent << "if (QObject::d_ptr->metaObject != nullptr)\n" << indent << "return QObject::d_ptr->dynamicMetaObject();\n" << outdent << "SbkObject *pySelf = Shiboken::BindingManager::instance().retrieveWrapper(this);\n" << "if (pySelf == nullptr)\n" << indent << "return " << qualifiedCppName << "::metaObject();\n" << outdent - << "return PySide::SignalManager::retrieveMetaObject(reinterpret_cast<PyObject *>(pySelf));\n" + << "return PySide::SignalManager::retrieveMetaObject(" + "reinterpret_cast<PyObject *>(pySelf));\n" << outdent << "}\n\n"; // qt_metacall function - s << "int " << wrapperClassName << "::qt_metacall(QMetaObject::Call call, int id, void **args)\n"; + s << "int " << wrapperClassName + << "::qt_metacall(QMetaObject::Call call, int id, void **args)\n"; s << "{\n" << indent; - const auto list = classContext.metaClass()->queryFunctionsByName(QLatin1String("qt_metacall")); + const auto list = classContext.metaClass()->queryFunctionsByName(u"qt_metacall"_s); CodeSnipList snips; if (list.size() == 1) { @@ -1308,7 +1503,8 @@ void CppGenerator::writeMetaObjectMethod(TextStream &s, } s << "int result = " << qualifiedCppName << "::qt_metacall(call, id, args);\n" - << "return result < 0 ? result : PySide::SignalManager::qt_metacall(this, call, id, args);\n" + << "return result < 0 ? result : PySide::SignalManager::qt_metacall(" + "this, call, id, args);\n" << outdent << "}\n\n"; // qt_metacast function @@ -1321,108 +1517,117 @@ void CppGenerator::writeMetaCast(TextStream &s, const QString wrapperClassName = classContext.wrapperName(); const QString qualifiedCppName = classContext.metaClass()->qualifiedCppName(); s << "void *" << wrapperClassName << "::qt_metacast(const char *_clname)\n{\n" - << indent << "if (!_clname)\n" << indent << "return {};\n" << outdent + << indent << "if (_clname == nullptr)\n" << indent << "return {};\n" << outdent << "SbkObject *pySelf = Shiboken::BindingManager::instance().retrieveWrapper(this);\n" - << "if (pySelf && PySide::inherits(Py_TYPE(pySelf), _clname))\n" + << "if (pySelf != nullptr && PySide::inherits(Py_TYPE(pySelf), _clname))\n" << indent << "return static_cast<void *>(const_cast< " << wrapperClassName << " *>(this));\n" << outdent << "return " << qualifiedCppName << "::qt_metacast(_clname);\n" << outdent << "}\n\n"; } -void CppGenerator::writeEnumConverterFunctions(TextStream &s, const AbstractMetaEnum &metaEnum) const +static void generateDeprecatedValueWarnings(TextStream &c, + const AbstractMetaEnum &metaEnum, + bool useSurrogateName) { - if (metaEnum.isPrivate() || metaEnum.isAnonymous()) - return; - writeEnumConverterFunctions(s, metaEnum.typeEntry()); + EnumTypeEntryCPtr enumType = metaEnum.typeEntry(); + const QString prefix = enumType->qualifiedCppName() + u"::"_s; + c << "switch (value) {\n"; + const auto &deprecatedValues = metaEnum.deprecatedValues(); + for (const auto &v : deprecatedValues) { + c << "case "; + if (useSurrogateName) + c << v.value().toString(); // Protected, use int representation + else + c << prefix << v.name(); + c << ":\n" << indent + << "Shiboken::Warnings::warnDeprecatedEnumValue(\"" << enumType->name() + << "\", \"" << v.name() << "\");\nbreak;\n" << outdent; + } + if (deprecatedValues.size() < metaEnum.values().size()) + c << "default:\n" << indent << "break;\n" << outdent; + c << "}\n"; } -void CppGenerator::writeEnumConverterFunctions(TextStream &s, const TypeEntry *enumType) const +void CppGenerator::writeEnumConverterFunctions(TextStream &s, const AbstractMetaEnum &metaEnum) const { - if (!enumType) + if (metaEnum.isPrivate() || metaEnum.isAnonymous()) return; + EnumTypeEntryCPtr enumType = metaEnum.typeEntry(); + Q_ASSERT(enumType); QString typeName = fixedCppTypeName(enumType); QString enumPythonType = cpythonTypeNameExt(enumType); - QString cppTypeName = getFullTypeName(enumType).trimmed(); - if (avoidProtectedHack()) { - auto metaEnum = api().findAbstractMetaEnum(enumType); - if (metaEnum.has_value() && metaEnum->isProtected()) - cppTypeName = protectedEnumSurrogateName(metaEnum.value()); - } + const bool useSurrogateName = avoidProtectedHack() && metaEnum.isProtected(); + QString cppTypeName = useSurrogateName + ? protectedEnumSurrogateName(metaEnum) : getFullTypeName(enumType).trimmed(); + StringStream c(TextStream::Language::Cpp); - c << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n" - << " "; - 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"; + if (metaEnum.isDeprecated()) + c << "Shiboken::Warnings::warnDeprecatedEnum(\"" << enumType->name() << "\");\n"; + + c << "const auto value = static_cast<" << cppTypeName + << ">(Shiboken::Enum::getValue(pyIn));\n"; + + // Warn about deprecated values unless it is protected+scoped (inaccessible values) + const bool valuesAcccessible = !useSurrogateName || metaEnum.enumKind() != EnumClass; + if (valuesAcccessible && metaEnum.hasDeprecatedValues()) + generateDeprecatedValueWarnings(c, metaEnum, useSurrogateName); + + c << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) = value;\n"; + + ConfigurableScope configScope(s, enumType); writePythonToCppFunction(s, c.toString(), typeName, typeName); - QString pyTypeCheck = u"PyObject_TypeCheck(pyIn, "_qs + enumPythonType + u')'; + QString pyTypeCheck = u"PyObject_TypeCheck(pyIn, "_s + enumPythonType + u')'; writeIsPythonConvertibleToCppFunction(s, typeName, typeName, pyTypeCheck); c.clear(); c << "const int castCppIn = int(*reinterpret_cast<const " - << cppTypeName << " *>(cppIn));\n" << "return "; - if (enumType->isFlags()) { - c << "reinterpret_cast<PyObject *>(PySide::QFlags::newObject(castCppIn, " - << enumPythonType << "))"; - } else { - c << "Shiboken::Enum::newItem(" << enumPythonType << ", castCppIn)"; - } - c << ";\n"; + << cppTypeName << " *>(cppIn));\n" << "return " + << "Shiboken::Enum::newItem(" << enumPythonType << ", castCppIn);\n"; writeCppToPythonFunction(s, c.toString(), typeName, typeName); s << '\n'; +} - if (enumType->isFlags()) - return; +static void writePointerToPythonConverter(TextStream &c, + const AbstractMetaClassCPtr &metaClass, + const QString &typeName, + const QString &cpythonType) +{ + c << "auto *pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));\n" + << "if (pyOut) {\n" << indent + << "Py_INCREF(pyOut);\nreturn pyOut;\n" << outdent + << "}\n"; - auto flags = reinterpret_cast<const EnumTypeEntry *>(enumType)->flags(); - if (!flags) + const QString nameFunc = metaClass->typeEntry()->polymorphicNameFunction(); + if (nameFunc.isEmpty() && !metaClass->hasVirtualDestructor()) { + c << "return Shiboken::Object::newObjectWithHeuristics(" + << cpythonType << ", const_cast<void *>(cppIn), false);\n"; return; + } - // QFlags part. - - writeEnumConverterFunctions(s, flags); - - c.clear(); - cppTypeName = getFullTypeName(flags).trimmed(); - c << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n" - << " " << cppTypeName - << "(QFlag(int(Shiboken::Enum::getValue(pyIn))));\n"; - - QString flagsTypeName = fixedCppTypeName(flags); - writePythonToCppFunction(s, c.toString(), typeName, flagsTypeName); - writeIsPythonConvertibleToCppFunction(s, typeName, flagsTypeName, pyTypeCheck); + c << "auto *tCppIn = reinterpret_cast<const " << typeName << R"( *>(cppIn); +const char *typeName = )"; + if (nameFunc.isEmpty()) + c << "typeid(*tCppIn).name();\n"; + else + c << nameFunc << "(tCppIn);\n"; + c << "return Shiboken::Object::newObjectForPointer(" + << cpythonType << ", const_cast<void *>(cppIn), false, typeName);\n"; +} - c.clear(); - c << "Shiboken::AutoDecRef pyLong(PyNumber_Long(pyIn));\n" - << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n" - << " " << 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, c.toString(), QLatin1String("number"), flagsTypeName); - writeIsPythonConvertibleToCppFunction(s, QLatin1String("number"), flagsTypeName, numberCondition); -} - -void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClass *metaClass, +void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext) const { s << "// Type conversion functions.\n\n"; AbstractMetaEnumList classEnums = metaClass->enums(); + auto typeEntry = metaClass->typeEntry(); metaClass->getEnumsFromInvisibleNamespacesToBeGenerated(&classEnums); if (!classEnums.isEmpty()) s << "// Python to C++ enum conversion.\n"; - for (const AbstractMetaEnum &metaEnum : qAsConst(classEnums)) + for (const AbstractMetaEnum &metaEnum : std::as_const(classEnums)) writeEnumConverterFunctions(s, metaEnum); if (metaClass->isNamespace()) @@ -1440,123 +1645,102 @@ void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClas 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 targetTypeName = metaClass->name() + u"_PTR"_s; StringStream c(TextStream::Language::Cpp); c << "Shiboken::Conversions::pythonToCppPointer(" << cpythonType << ", pyIn, cppOut);"; writePythonToCppFunction(s, c.toString(), sourceTypeName, targetTypeName); // "Is convertible" function for the Python object to C++ pointer conversion. - const QString pyTypeCheck = QLatin1String("PyObject_TypeCheck(pyIn, reinterpret_cast<PyTypeObject *>(") - + cpythonType + QLatin1String("))"); + const QString pyTypeCheck = u"PyObject_TypeCheck(pyIn, "_s + cpythonType + u")"_s; writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, pyTypeCheck, QString(), true); s << '\n'; // C++ pointer to a Python wrapper, keeping identity. s << "// C++ to Python pointer conversion - tries to find the Python wrapper for the C++ object (keeps object identity).\n"; c.clear(); - if (usePySideExtensions() && metaClass->isQObject()) { + if (usePySideExtensions() && isQObject(metaClass)) { c << "return PySide::getWrapperForQObject(reinterpret_cast<" << typeName << " *>(const_cast<void *>(cppIn)), " << cpythonType << ");\n"; } else { - c << "auto pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));\n" - << "if (pyOut) {\n"; - { - Indentation indent(c); - c << "Py_INCREF(pyOut);\nreturn pyOut;\n"; - } - c << "}\n" - << "bool changedTypeName = false;\n" - << "auto tCppIn = reinterpret_cast<const " << typeName << R"( *>(cppIn); -const char *typeName = typeid(*tCppIn).name(); -auto sbkType = Shiboken::ObjectType::typeForTypeName(typeName); -if (sbkType && Shiboken::ObjectType::hasSpecialCastFunction(sbkType)) { - typeName = typeNameOf(tCppIn); - changedTypeName = true; -} -)" - << "PyObject *result = Shiboken::Object::newObject(" << cpythonType - << R"(, const_cast<void *>(cppIn), false, /* exactType */ changedTypeName, typeName); -if (changedTypeName) - delete [] typeName; -return result;)"; + writePointerToPythonConverter(c, metaClass, typeName, cpythonType); } std::swap(targetTypeName, sourceTypeName); writeCppToPythonFunction(s, c.toString(), sourceTypeName, targetTypeName); // The conversions for an Object Type end here. - if (!metaClass->typeEntry()->isValue() && !metaClass->typeEntry()->isSmartPointer()) { + if (!typeEntry->isValue() && !typeEntry->isSmartPointer()) { s << '\n'; return; } // Always copies C++ value (not pointer, and not reference) to a new Python wrapper. s << '\n' << "// C++ to Python copy conversion.\n"; - if (!classContext.forSmartPointer()) - targetTypeName = metaClass->name(); - else - targetTypeName = classContext.preciseType().name(); + targetTypeName = metaClass->name(); - sourceTypeName = targetTypeName + QLatin1String("_COPY"); + sourceTypeName = targetTypeName + u"_COPY"_s; c.clear(); - QString computedWrapperName; - if (!classContext.forSmartPointer()) { - computedWrapperName = classContext.useWrapper() - ? classContext.wrapperName() : metaClass->qualifiedCppName(); + const bool isUniquePointer = classContext.forSmartPointer() + && typeEntry->isUniquePointer(); + + if (isUniquePointer) { + c << "auto *source = reinterpret_cast<" << typeName + << " *>(const_cast<void *>(cppIn));\n"; } else { - computedWrapperName = classContext.smartPointerWrapperName(); + c << "auto *source = reinterpret_cast<const " << typeName << " *>(cppIn);\n"; } - c << "return Shiboken::Object::newObject(" << cpythonType - << ", new ::" << computedWrapperName << "(*reinterpret_cast<const " - << typeName << " *>(cppIn)), true, true);"; + << ", new " << globalScopePrefix(classContext) << classContext.effectiveClassName() << '(' + << (isUniquePointer ? "std::move(*source)" : "*source") + << "), true, true);"; writeCppToPythonFunction(s, c.toString(), sourceTypeName, targetTypeName); s << '\n'; // Python to C++ copy conversion. s << "// Python to C++ copy conversion.\n"; - if (!classContext.forSmartPointer()) - sourceTypeName = metaClass->name(); - else - sourceTypeName = classContext.preciseType().name(); + sourceTypeName = metaClass->name(); - targetTypeName = sourceTypeName + QStringLiteral("_COPY"); + targetTypeName = sourceTypeName + "_COPY"_L1; c.clear(); - QString pyInVariable = QLatin1String("pyIn"); - QString wrappedCPtrExpression; - if (!classContext.forSmartPointer()) - wrappedCPtrExpression = cpythonWrapperCPtr(metaClass->typeEntry(), pyInVariable); - else - wrappedCPtrExpression = cpythonWrapperCPtr(classContext.preciseType(), pyInVariable); + QString pyInVariable = u"pyIn"_s; + const QString outPtr = u"reinterpret_cast<"_s + typeName + u" *>(cppOut)"_s; + if (!classContext.forSmartPointer()) { + c << '*' << outPtr << " = *" + << cpythonWrapperCPtr(typeEntry, pyInVariable) << ';'; + } else { + auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(typeEntry); + const QString resetMethod = ste->resetMethod(); + c << "auto *ptr = " << outPtr << ";\n"; + c << "if (" << pyInVariable << " == Py_None)\n" << indent; + if (resetMethod.isEmpty()) + c << "*ptr = {};\n"; + else + c << "ptr->" << resetMethod << "();\n"; + const QString value = u'*' + cpythonWrapperCPtr(classContext.preciseType(), pyInVariable); + c << outdent << "else\n" << indent + << "*ptr = " << (isUniquePointer ? stdMove(value) : value) << ';'; + } - c << "*reinterpret_cast<" << typeName << " *>(cppOut) = *" - << wrappedCPtrExpression << ';'; writePythonToCppFunction(s, c.toString(), sourceTypeName, targetTypeName); // "Is convertible" function for the Python object to C++ value copy conversion. - writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, pyTypeCheck); + QString copyTypeCheck = pyTypeCheck; + if (classContext.forSmartPointer()) + copyTypeCheck.prepend(pyInVariable + u" == Py_None || "_s); + writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, copyTypeCheck); s << '\n'; // User provided implicit conversions. - CustomConversion *customConversion = metaClass->typeEntry()->customConversion(); - // Implicit conversions. - AbstractMetaFunctionCList implicitConvs; - if (!customConversion || !customConversion->replaceOriginalTargetToNativeConversions()) { - const auto &allImplicitConvs = api().implicitConversions(metaClass->typeEntry()); - for (const auto &func : allImplicitConvs) { - if (!func->isUserAdded()) - implicitConvs << func; - } - } + const AbstractMetaFunctionCList implicitConvs = implicitConversions(typeEntry); if (!implicitConvs.isEmpty()) s << "// Implicit conversions.\n"; AbstractMetaType targetType = AbstractMetaType::fromAbstractMetaClass(metaClass); - for (const auto &conv : qAsConst(implicitConvs)) { + for (const auto &conv : std::as_const(implicitConvs)) { if (conv->isModifiedRemoved()) continue; @@ -1564,36 +1748,30 @@ return result;)"; QString toCppConv; QString toCppPreConv; if (conv->isConversionOperator()) { - const AbstractMetaClass *sourceClass = conv->ownerClass(); - typeCheck = u"PyObject_TypeCheck(pyIn, "_qs + const auto sourceClass = conv->ownerClass(); + typeCheck = u"PyObject_TypeCheck(pyIn, "_s + cpythonTypeNameExt(sourceClass->typeEntry()) + u')'; - toCppConv = QLatin1Char('*') + cpythonWrapperCPtr(sourceClass->typeEntry(), QLatin1String("pyIn")); + toCppConv = u'*' + cpythonWrapperCPtr(sourceClass->typeEntry(), + pyInVariable); } else { // Constructor that does implicit conversion. - if (!conv->typeReplaced(1).isEmpty() || conv->isModifiedToArray(1)) + const auto &firstArg = conv->arguments().constFirst(); + if (firstArg.isTypeModified() || conv->isModifiedToArray(1)) continue; - const AbstractMetaType sourceType = conv->arguments().constFirst().type(); - typeCheck = cpythonCheckFunction(sourceType); - bool isUserPrimitiveWithoutTargetLangName = sourceType.isUserPrimitive() - && sourceType.typeEntry()->targetLangApiName() == sourceType.typeEntry()->name(); - if (!sourceType.isWrapperType() - && !isUserPrimitiveWithoutTargetLangName - && !sourceType.typeEntry()->isEnum() - && !sourceType.typeEntry()->isFlags() - && !sourceType.typeEntry()->isContainer()) { - typeCheck += QLatin1Char('('); - } + const AbstractMetaType &sourceType = firstArg.type(); if (sourceType.isWrapperType()) { - typeCheck += QLatin1String("pyIn)"); - toCppConv = (sourceType.referenceType() == LValueReference - || !sourceType.isPointerToWrapperType()) - ? 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 (sourceType.referenceType() == LValueReference + || !sourceType.isPointerToWrapperType()) { + toCppConv = u" *"_s; + } + toCppConv += cpythonWrapperCPtr(sourceType.typeEntry(), pyInVariable); + } + + typeCheck = cpythonCheckFunction(sourceType); + if (typeCheck.endsWith(u", ")) { + typeCheck += pyInVariable + u')'; + } else if (typeCheck != u"true" && typeCheck != u"false") { + typeCheck += u'(' + pyInVariable + u')'; } if (sourceType.isUserPrimitive() @@ -1604,13 +1782,15 @@ return result;)"; StringStream pc(TextStream::Language::Cpp); pc << getFullTypeNameWithoutModifiers(sourceType) << " cppIn" << minimalConstructorExpression(api(), sourceType) << ";\n"; - writeToCppConversion(pc, sourceType, nullptr, QLatin1String("pyIn"), QLatin1String("cppIn")); + writeToCppConversion(pc, sourceType, pyInVariable, + u"cppIn"_s); pc << ';'; toCppPreConv = pc.toString(); - toCppConv.append(QLatin1String("cppIn")); + toCppConv.append(u"cppIn"_s); } else if (!sourceType.isWrapperType()) { StringStream tcc(TextStream::Language::Cpp); - writeToCppConversion(tcc, sourceType, metaClass, QLatin1String("pyIn"), QLatin1String("/*BOZO-1061*/")); + writeToCppConversion(tcc, sourceType, pyInVariable, + u"/*BOZO-1061*/"_s); toCppConv = tcc.toString(); } } @@ -1620,62 +1800,61 @@ return result;)"; writePythonToCppConversionFunctions(s, sourceType, targetType, typeCheck, toCppConv, toCppPreConv); } - writeCustomConverterFunctions(s, customConversion); + if (typeEntry->isValue()) { + auto vte = std::static_pointer_cast<const ValueTypeEntry>(typeEntry); + writeCustomConverterFunctions(s, vte->customConversion()); + } } void CppGenerator::writeCustomConverterFunctions(TextStream &s, - const CustomConversion *customConversion) const + const CustomConversionPtr &customConversion) const { if (!customConversion) return; - const CustomConversion::TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions(); + const 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()); + auto ownerType = customConversion->ownerType(); + s << "// Python to C++ conversions for type '" << ownerType->qualifiedCppName() << "'.\n"; + for (const auto &toNative : toCppConversions) + writePythonToCppConversionFunctions(s, toNative, ownerType); s << '\n'; } -void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClass *metaClass, +void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext) const { - if (metaClass->isNamespace()) + const auto typeEntry = metaClass->typeEntry(); + if (typeEntry->isNamespace()) return; s << "// Register Converter\n" - << "SbkConverter *converter = Shiboken::Conversions::createConverter(" - << cpythonTypeName(metaClass) << ',' << '\n'; - { - Indentation indent(s); - QString sourceTypeName = metaClass->name(); - QString targetTypeName = sourceTypeName + QLatin1String("_PTR"); - s << pythonToCppFunctionName(sourceTypeName, targetTypeName) << ',' << '\n' - << convertibleToCppFunctionName(sourceTypeName, targetTypeName) << ',' << '\n'; - std::swap(targetTypeName, sourceTypeName); + << "SbkConverter *converter = Shiboken::Conversions::createConverter(pyType,\n" + << indent; + QString sourceTypeName = metaClass->name(); + QString targetTypeName = sourceTypeName + u"_PTR"_s; + s << pythonToCppFunctionName(sourceTypeName, targetTypeName) << ',' << '\n' + << convertibleToCppFunctionName(sourceTypeName, targetTypeName) << ',' << '\n'; + std::swap(targetTypeName, sourceTypeName); + s << cppToPythonFunctionName(sourceTypeName, targetTypeName); + if (typeEntry->isValue() || typeEntry->isSmartPointer()) { + s << ',' << '\n'; + sourceTypeName = metaClass->name() + u"_COPY"_s; s << cppToPythonFunctionName(sourceTypeName, targetTypeName); - if (metaClass->typeEntry()->isValue() || metaClass->typeEntry()->isSmartPointer()) { - s << ',' << '\n'; - sourceTypeName = metaClass->name() + QLatin1String("_COPY"); - s << cppToPythonFunctionName(sourceTypeName, targetTypeName); - } } - s << ");\n"; - - s << '\n'; + s << outdent << ");\n\n"; auto writeConversions = [&s](const QString &signature) { - s << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "\");\n" - << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "*\");\n" - << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "&\");\n"; + s << registerConverterName(signature) << registerConverterName(signature + u'*') + << registerConverterName(signature + u'&'); }; auto writeConversionsForType = [writeConversions](const QString &fullTypeName) { - QStringList lst = fullTypeName.split(QLatin1String("::"), + QStringList lst = fullTypeName.split(u"::"_s, Qt::SkipEmptyParts); while (!lst.isEmpty()) { - QString signature = lst.join(QLatin1String("::")); + QString signature = lst.join(u"::"_s); writeConversions(signature); lst.removeFirst(); } @@ -1688,18 +1867,18 @@ void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClass const QString &smartPointerType = classContext.preciseType().instantiations().at(0).cppSignature(); const QString &smartPointerName = classContext.preciseType().typeEntry()->name(); - QStringList lst = smartPointerType.split(QLatin1String("::"), + QStringList lst = smartPointerType.split(u"::"_s, Qt::SkipEmptyParts); while (!lst.isEmpty()) { - QString signature = lst.join(QLatin1String("::")); - writeConversions(smartPointerName + u'<' + signature + u" >"_qs); + QString signature = lst.join(u"::"_s); + writeConversions(smartPointerName + u'<' + signature + u'>'); lst.removeFirst(); } writeConversionsForType(smartPointerType); } - s << "Shiboken::Conversions::registerConverterName(converter, typeid(::"; + s << "Shiboken::Conversions::registerConverterName(converter, typeid(" << m_gsp; QString qualifiedCppNameInvocation; if (!classContext.forSmartPointer()) qualifiedCppNameInvocation = metaClass->qualifiedCppName(); @@ -1709,41 +1888,31 @@ void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClass s << qualifiedCppNameInvocation << ").name());\n"; if (classContext.useWrapper()) { - s << "Shiboken::Conversions::registerConverterName(converter, typeid(::" + s << "Shiboken::Conversions::registerConverterName(converter, typeid(" << classContext.wrapperName() << ").name());\n"; } - s << '\n'; - - if (!metaClass->typeEntry()->isValue() && !metaClass->typeEntry()->isSmartPointer()) + if (!typeEntry->isValue() && !typeEntry->isSmartPointer()) return; // Python to C++ copy (value, not pointer neither reference) conversion. - s << "// Add Python to C++ copy (value, not pointer neither reference) conversion to type converter.\n"; - QString sourceTypeName = metaClass->name(); - QString targetTypeName = sourceTypeName + QLatin1String("_COPY"); + s << "\n// Add Python to C++ copy (value, not pointer neither reference) conversion to type converter.\n"; + sourceTypeName = metaClass->name(); + targetTypeName = sourceTypeName + u"_COPY"_s; QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName); - writeAddPythonToCppConversion(s, QLatin1String("converter"), toCpp, isConv); + writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv); // User provided implicit conversions. - CustomConversion *customConversion = metaClass->typeEntry()->customConversion(); // Add implicit conversions. - AbstractMetaFunctionCList implicitConvs; - if (!customConversion || !customConversion->replaceOriginalTargetToNativeConversions()) { - const auto &allImplicitConvs = api().implicitConversions(metaClass->typeEntry()); - for (const auto &func : allImplicitConvs) { - if (!func->isUserAdded()) - implicitConvs << func; - } - } + const AbstractMetaFunctionCList implicitConvs = implicitConversions(typeEntry); if (!implicitConvs.isEmpty()) s << "// Add implicit conversions to type converter.\n"; AbstractMetaType targetType = AbstractMetaType::fromAbstractMetaClass(metaClass); - for (const auto &conv : qAsConst(implicitConvs)) { + for (const auto &conv : std::as_const(implicitConvs)) { if (conv->isModifiedRemoved()) continue; AbstractMetaType sourceType; @@ -1751,28 +1920,34 @@ void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClass sourceType = AbstractMetaType::fromAbstractMetaClass(conv->ownerClass()); } else { // Constructor that does implicit conversion. - if (!conv->typeReplaced(1).isEmpty() || conv->isModifiedToArray(1)) + const auto &firstArg = conv->arguments().constFirst(); + if (firstArg.isTypeModified() || conv->isModifiedToArray(1)) continue; - sourceType = conv->arguments().constFirst().type(); + sourceType = firstArg.type(); } QString toCpp = pythonToCppFunctionName(sourceType, targetType); QString isConv = convertibleToCppFunctionName(sourceType, targetType); - writeAddPythonToCppConversion(s, QLatin1String("converter"), toCpp, isConv); + writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv); } - writeCustomConverterRegister(s, customConversion, QLatin1String("converter")); + if (typeEntry->isValue()) { + auto vte = std::static_pointer_cast<const ValueTypeEntry>(typeEntry); + writeCustomConverterRegister(s, vte->customConversion(), u"converter"_s); + } } -void CppGenerator::writeCustomConverterRegister(TextStream &s, const CustomConversion *customConversion, +void CppGenerator::writeCustomConverterRegister(TextStream &s, + const CustomConversionPtr &customConversion, const QString &converterVar) { if (!customConversion) return; - const CustomConversion::TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions(); + const TargetToNativeConversions &toCppConversions = + customConversion->targetToNativeConversions(); if (toCppConversions.isEmpty()) return; s << "// Add user defined implicit conversions to type converter.\n"; - for (CustomConversion::TargetToNativeConversion *toNative : toCppConversions) { + for (const auto &toNative : toCppConversions) { QString toCpp = pythonToCppFunctionName(toNative, customConversion->ownerType()); QString isConv = convertibleToCppFunctionName(toNative, customConversion->ownerType()); writeAddPythonToCppConversion(s, converterVar, toCpp, isConv); @@ -1786,38 +1961,25 @@ void CppGenerator::writeContainerConverterFunctions(TextStream &s, writePythonToCppConversionFunctions(s, containerType); } -void CppGenerator::writeSmartPointerConverterFunctions(TextStream &s, - const AbstractMetaType &smartPointerType) const +bool CppGenerator::needsArgumentErrorHandling(const OverloadData &overloadData) { - auto targetClass = AbstractMetaClass::findClass(api().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 = targetClass->typeSystemBaseClasses(); - for (auto k : classes) { - if (smartPointerTypeEntry->matchesInstantiation(k->typeEntry())) { - if (auto smartTargetType = findSmartPointerInstantiation(k->typeEntry())) { - s << "// SmartPointer derived class: " - << smartTargetType->cppSignature() << "\n"; - writePythonToCppConversionFunctions(s, smartPointerType, smartTargetType.value(), {}, {}, {}); - } - } - } - } + if (overloadData.maxArgs() > 0) + return true; + // QObject constructors need error handling when passing properties as kwarg. + if (!usePySideExtensions()) + return false; + auto rfunc = overloadData.referenceFunction(); + return rfunc->functionType() == AbstractMetaFunction::ConstructorFunction + && isQObject(rfunc->ownerClass()); } -void CppGenerator::writeMethodWrapperPreamble(TextStream &s,const OverloadData &overloadData, - const GeneratorContext &context) const +void CppGenerator::writeMethodWrapperPreamble(TextStream &s, + const OverloadData &overloadData, + const GeneratorContext &context, + ErrorReturn errorReturn) { const auto rfunc = overloadData.referenceFunction(); - const AbstractMetaClass *ownerClass = rfunc->targetLangOwner(); + const auto ownerClass = rfunc->targetLangOwner(); Q_ASSERT(ownerClass == context.metaClass()); int minArgs = overloadData.minArgs(); int maxArgs = overloadData.maxArgs(); @@ -1827,33 +1989,31 @@ void CppGenerator::writeMethodWrapperPreamble(TextStream &s,const OverloadData & if (rfunc->isConstructor()) { // Check if the right constructor was called. if (!ownerClass->hasPrivateDestructor()) { - s << "if (Shiboken::Object::isUserType(self) && !Shiboken::ObjectType::canCallConstructor(self->ob_type, Shiboken::SbkType< ::"; + s << "if (Shiboken::Object::isUserType(self) && " + << "!Shiboken::ObjectType::canCallConstructor(self->ob_type, Shiboken::SbkType< " + << m_gsp; QString qualifiedCppName; if (!context.forSmartPointer()) qualifiedCppName = ownerClass->qualifiedCppName(); else qualifiedCppName = context.preciseType().cppSignature(); - s << qualifiedCppName << " >()))\n"; - Indentation indent(s); - s << returnStatement(m_currentErrorCode) << '\n' << '\n'; + s << qualifiedCppName << " >()))\n" << indent << errorReturn << outdent << '\n'; } // Declare pointer for the underlying C++ object. - s << "::"; - if (!context.forSmartPointer()) { - s << (context.useWrapper() ? context.wrapperName() : ownerClass->qualifiedCppName()); - } else { - s << context.smartPointerWrapperName(); - } - s << " *cptr{};\n"; + s << globalScopePrefix(context) << context.effectiveClassName() << " *cptr{};\n"; initPythonArguments = maxArgs > 0; } else { if (rfunc->implementingClass() && (!rfunc->implementingClass()->isNamespace() && overloadData.hasInstanceFunction())) { - writeCppSelfDefinition(s, rfunc, context, overloadData.hasStaticFunction(), - overloadData.hasClassMethod()); + CppSelfDefinitionFlags flags; + if (overloadData.hasStaticFunction()) + flags.setFlag(CppSelfDefinitionFlag::HasStaticOverload); + if (overloadData.hasClassMethod()) + flags.setFlag(CppSelfDefinitionFlag::HasClassMethodOverload); + writeCppSelfDefinition(s, rfunc, context, errorReturn, flags); } if (!rfunc->isInplaceOperator() && overloadData.hasNonVoidReturnType()) s << "PyObject *" << PYTHON_RETURN_VAR << "{};\n"; @@ -1861,21 +2021,24 @@ void CppGenerator::writeMethodWrapperPreamble(TextStream &s,const OverloadData & initPythonArguments = minArgs != maxArgs || maxArgs > 1; } - s << R"(Shiboken::AutoDecRef errInfo{}; -static const char *fullName = ")" << fullPythonFunctionName(rfunc, true) - << "\";\nSBK_UNUSED(fullName)\n"; + if (needsArgumentErrorHandling(overloadData)) + s << "Shiboken::AutoDecRef errInfo{};\n"; + + s << "static const char fullName[] = \"" << fullPythonFunctionName(rfunc, true) + << "\";\nSBK_UNUSED(fullName)\n" + << "Shiboken::PythonContextMarker pcm;\n"; + // PYSIDE-2335: Mark blocking calls like `exec` or `run` as such. + bool isBlockingFunction = rfunc->name() == u"exec"_s || rfunc->name() == u"exec_"_s + || rfunc->name() == u"run"_s; + if (isBlockingFunction) + s << "pcm.setBlocking();\n"; + if (maxArgs > 0) { s << "int overloadId = -1;\n" - << "PythonToCppFunc " << PYTHON_TO_CPP_VAR; - if (overloadData.pythonFunctionWrapperUsesListOfArguments()) { - 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)); + << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR; + if (overloadData.pythonFunctionWrapperUsesListOfArguments()) + s << '[' << maxArgs << ']'; + s << ";\n" << sbkUnusedVariableCast(PYTHON_TO_CPP_VAR); } if (initPythonArguments) { @@ -1884,7 +2047,7 @@ static const char *fullName = ")" << fullPythonFunctionName(rfunc, true) && !overloadData.pythonFunctionWrapperUsesListOfArguments()) { s << "(" << PYTHON_ARG << " == 0 ? 0 : 1);\n"; } else { - writeArgumentsInitializer(s, overloadData); + writeArgumentsInitializer(s, overloadData, errorReturn); } } } @@ -1892,20 +2055,23 @@ static const char *fullName = ")" << fullPythonFunctionName(rfunc, true) void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &overloadData, const GeneratorContext &classContext) const { - ErrorCode errorCode(-1); + const ErrorReturn errorReturn = ErrorReturn::MinusOne; const auto rfunc = overloadData.referenceFunction(); - const AbstractMetaClass *metaClass = rfunc->ownerClass(); + const auto metaClass = rfunc->ownerClass(); s << "static int\n"; s << cpythonFunctionName(rfunc) << "(PyObject *self, PyObject *args, PyObject *kwds)\n{\n" << indent; + if (overloadData.maxArgs() == 0 || metaClass->isAbstract()) + s << sbkUnusedVariableCast("args"); + s << sbkUnusedVariableCast("kwds"); - const bool needsMetaObject = usePySideExtensions() && metaClass->isQObject(); + const bool needsMetaObject = usePySideExtensions() && isQObject(metaClass); if (needsMetaObject) s << "const QMetaObject *metaObject;\n"; - s << "SbkObject *sbkSelf = reinterpret_cast<SbkObject *>(self);\n"; + s << "auto *sbkSelf = reinterpret_cast<SbkObject *>(self);\n"; if (metaClass->isAbstract() || metaClass->baseClassNames().size() > 1) { s << "PyTypeObject *type = self->ob_type;\n" @@ -1916,26 +2082,22 @@ void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &ov if (metaClass->isAbstract()) { // C++ Wrapper disabled: Abstract C++ class cannot be instantiated. if (metaClass->typeEntry()->typeFlags().testFlag(ComplexTypeEntry::DisableWrapper)) { - writeUnusedVariableCast(s, QStringLiteral("sbkSelf")); - writeUnusedVariableCast(s, QStringLiteral("type")); - writeUnusedVariableCast(s, QStringLiteral("myType")); + s << sbkUnusedVariableCast("sbkSelf") + << sbkUnusedVariableCast("type") + << sbkUnusedVariableCast("myType"); if (needsMetaObject) - writeUnusedVariableCast(s, QStringLiteral("metaObject")); - s << "PyErr_SetString(PyExc_NotImplementedError,\n" << indent - << "\"Abstract class '" << metaClass->qualifiedCppName() - << "' cannot be instantiated since the wrapper has been disabled.\");\n" << outdent - << returnStatement(m_currentErrorCode) << outdent - << "\n}\n\n"; + s << sbkUnusedVariableCast("metaObject"); + s << "Shiboken::Errors::setInstantiateAbstractClassDisabledWrapper(\"" + << metaClass->qualifiedCppName() << "\");\n" << errorReturn << outdent + << "}\n\n"; return; } // Refuse to instantiate Abstract C++ class (via C++ Wrapper) unless it is // a Python-derived class for which type != myType. s << "if (type == myType) {\n" << indent - << "PyErr_SetString(PyExc_NotImplementedError,\n" << indent - << "\"'" << metaClass->qualifiedCppName() - << "' represents a C++ abstract class and cannot be instantiated\");\n" << outdent - << returnStatement(m_currentErrorCode) << '\n' << outdent + << "Shiboken::Errors::setInstantiateAbstractClass(\"" << metaClass->qualifiedCppName() + << "\");\n" << errorReturn << outdent << "}\n\n"; } @@ -1948,29 +2110,37 @@ void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &ov } // PYSIDE-1478: Switching must also happen at object creation time. - if (usePySideExtensions()) + if (usePySideExtensions() && !classContext.forSmartPointer()) s << "PySide::Feature::Select(self);\n"; - writeMethodWrapperPreamble(s, overloadData, classContext); + writeMethodWrapperPreamble(s, overloadData, classContext, errorReturn); s << '\n'; if (overloadData.maxArgs() > 0) - writeOverloadedFunctionDecisor(s, overloadData); + writeOverloadedFunctionDecisor(s, overloadData, errorReturn); - writeFunctionCalls(s, overloadData, classContext); + // Handles Python Multiple Inheritance + QString pre = needsMetaObject ? u"bool usesPyMI = "_s : u""_s; + s << "\n// PyMI support\n" + << pre << "Shiboken::callInheritedInit(self, args, kwds, fullName);\n" + << "if (" << shibokenErrorsOccurred << ")\n" + << indent << errorReturn << outdent << "\n"; + + writeFunctionCalls(s, overloadData, classContext, errorReturn); s << '\n'; - s << "if (PyErr_Occurred() || !Shiboken::Object::setCppPointer(sbkSelf, Shiboken::SbkType< ::" - << metaClass->qualifiedCppName() << " >(), cptr)) {\n"; - { - Indentation indent(s); - s << "delete cptr;\n"; - s << returnStatement(m_currentErrorCode) << '\n'; - } - s << "}\n"; + const QString typeName = classContext.forSmartPointer() + ? classContext.preciseType().cppSignature() : metaClass->qualifiedCppName(); + s << "if (" << shibokenErrorsOccurred + << " || !Shiboken::Object::setCppPointer(sbkSelf, Shiboken::SbkType< " + << globalScopePrefix(classContext) << typeName << " >(), cptr)) {\n" + << indent << "delete cptr;\n" << errorReturn << outdent + << "}\n"; if (overloadData.maxArgs() > 0) - s << "if (!cptr) goto " << cpythonFunctionName(rfunc) << "_TypeError;\n\n"; + s << "if (cptr == nullptr)\n" << indent + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n\n" + << outdent; s << "Shiboken::Object::setValidCpp(sbkSelf, true);\n"; // If the created C++ object has a C++ wrapper the ownership is assigned to Python @@ -1981,24 +2151,20 @@ void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &ov s << "Shiboken::Object::setHasCppWrapper(sbkSelf, true);\n"; // Need to check if a wrapper for same pointer is already registered // Caused by bug PYSIDE-217, where deleted objects' wrappers are not released - s << "if (Shiboken::BindingManager::instance().hasWrapper(cptr)) {\n"; - { - Indentation indent(s); - s << "Shiboken::BindingManager::instance().releaseWrapper(" - "Shiboken::BindingManager::instance().retrieveWrapper(cptr));\n"; - } - s << "}\nShiboken::BindingManager::instance().registerWrapper(sbkSelf, cptr);\n"; + s << "if (Shiboken::BindingManager::instance().hasWrapper(cptr)) {\n" << indent + << "Shiboken::BindingManager::instance().releaseWrapper(" + "Shiboken::BindingManager::instance().retrieveWrapper(cptr));\n" << outdent + << "}\nShiboken::BindingManager::instance().registerWrapper(sbkSelf, cptr);\n"; // Create metaObject and register signal/slot - bool errHandlerNeeded = overloadData.maxArgs() > 0; if (needsMetaObject) { - errHandlerNeeded = true; s << "\n// QObject setup\n" << "PySide::Signal::updateSourceObject(self);\n" << "metaObject = cptr->metaObject(); // <- init python qt properties\n" << "if (!errInfo.isNull() && PyDict_Check(errInfo.object())) {\n" << indent - << "if (!PySide::fillQtProperties(self, metaObject, errInfo))\n" << indent - << "goto " << cpythonFunctionName(rfunc) << "_TypeError;\n" << outdent << outdent + << "if (!PySide::fillQtProperties(self, metaObject, errInfo, usesPyMI))\n" << indent + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" + << outdent << outdent << "};\n"; } @@ -2015,31 +2181,27 @@ void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &ov } if (hasCodeInjectionsAtEnd) { // FIXME: C++ arguments are not available in code injection on constructor when position = end. - s <<"switch (overloadId) {\n"; + s << "switch (overloadId) {\n"; for (const auto &func : overloadData.overloads()) { - Indentation indent(s); + s << indent; const CodeSnipList &injectedCodeSnips = func->injectedCodeSnips(); for (const CodeSnip &cs : injectedCodeSnips) { if (cs.position == TypeSystem::CodeSnipPositionEnd) { s << "case " << metaClass->functions().indexOf(func) << ':' << '\n' - << "{\n"; - { - Indentation indent(s); - writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionEnd, - TypeSystem::TargetLangCode, func, - true /* usesPyArgs */, nullptr); - } - s << "}\nbreak;\n"; + << "{\n" << indent; + writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionEnd, + TypeSystem::TargetLangCode, func, + true /* usesPyArgs */, nullptr); + s << outdent << "}\nbreak;\n"; break; } } + s << outdent; } s << "}\n"; } s << "\n\nreturn 1;\n"; - if (errHandlerNeeded) - writeErrorSection(s, overloadData); s<< outdent << "}\n\n"; } @@ -2052,89 +2214,71 @@ void CppGenerator::writeMethodWrapper(TextStream &s, const OverloadData &overloa s << "static PyObject *"; s << cpythonFunctionName(rfunc) << "(PyObject *self"; + bool hasKwdArgs = false; if (maxArgs > 0) { - s << ", PyObject *" << (overloadData.pythonFunctionWrapperUsesListOfArguments() ? "args" : PYTHON_ARG); - if (overloadData.hasArgumentWithDefaultValue() || rfunc->isCallOperator()) + s << ", PyObject *" + << (overloadData.pythonFunctionWrapperUsesListOfArguments() ? u"args"_s : PYTHON_ARG); + hasKwdArgs = overloadData.hasArgumentWithDefaultValue() || rfunc->isCallOperator(); + if (hasKwdArgs) s << ", PyObject *kwds"; } s << ")\n{\n" << indent; + if (rfunc->ownerClass() == nullptr || overloadData.hasStaticFunction()) + s << sbkUnusedVariableCast(PYTHON_SELF_VAR); + if (hasKwdArgs) + s << sbkUnusedVariableCast("kwds"); writeMethodWrapperPreamble(s, overloadData, classContext); s << '\n'; - /* - * This code is intended for shift operations only: - * Make sure reverse <</>> operators defined in other classes (specially from other modules) - * are called. A proper and generic solution would require an reengineering in the operator - * system like the extended converters. - * - * Solves #119 - QDataStream <</>> operators not working for QPixmap - * 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')); + // This code is intended for shift operations only: Make sure reverse <</>> + // operators defined in other classes (specially from other modules) + // are called. A proper and generic solution would require an reengineering + // in the operator system like the extended converters. + // Solves #119 - QDataStream <</>> operators not working for QPixmap. + const bool hasReturnValue = overloadData.hasNonVoidReturnType(); + + if (hasReturnValue && rfunc->functionType() == AbstractMetaFunction::ShiftOperator + && rfunc->isBinaryOperator()) { // For custom classes, operations like __radd__ and __rmul__ // will enter an infinite loop. - if (rfunc->isBinaryOperator() && revOpName.contains(QLatin1String("shift"))) { - s << "Shiboken::AutoDecRef attrName(Py_BuildValue(\"s\", \"" << revOpName << "\"));\n"; - s << "if (!isReverse\n"; - { - Indentation indent(s); - s << "&& Shiboken::Object::checkType(" << PYTHON_ARG << ")\n" - << "&& !PyObject_TypeCheck(" << PYTHON_ARG << ", self->ob_type)\n" - << "&& 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 << "PyObject *revOpMethod = PyObject_GetAttr(" << PYTHON_ARG << ", attrName);\n"; - s << "if (revOpMethod && PyCallable_Check(revOpMethod)) {\n"; - { - Indentation indent(s); - s << PYTHON_RETURN_VAR << " = PyObject_CallFunction(revOpMethod, \"O\", self);\n" - << "if (PyErr_Occurred() && (PyErr_ExceptionMatches(PyExc_NotImplementedError)" - << " || PyErr_ExceptionMatches(PyExc_AttributeError))) {\n"; - { - Indentation indent(s); - s << "PyErr_Clear();\n" - << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");\n" - << PYTHON_RETURN_VAR << " = " << NULL_PTR << ";\n"; - } - s << "}\n"; - } - s << "}\n" - << "Py_XDECREF(revOpMethod);\n\n"; - } // - s << "}\n\n" - << "// Do not enter here if other object has implemented a reverse operator.\n" - << "if (!" << PYTHON_RETURN_VAR << ") {\n"; - reverseIndent.reset(new Indentation(s)); - } // binary shift operator - } - - if (maxArgs > 0) - writeOverloadedFunctionDecisor(s, overloadData); - - writeFunctionCalls(s, overloadData, classContext); - - if (!reverseIndent.isNull()) { // binary shift operator - reverseIndent.reset(); - s << '\n' << "} // End of \"if (!" << PYTHON_RETURN_VAR << ")\"\n"; + const QString pythonOp = ShibokenGenerator::pythonOperatorFunctionName(rfunc); + s << "static PyObject *attrName = Shiboken::PyMagicName::r" + << pythonOp.mid(2, pythonOp.size() -4) << "();\n" // Strip __ + << "if (!isReverse\n" << indent + << "&& Shiboken::Object::checkType(" << PYTHON_ARG << ")\n" + << "&& !PyObject_TypeCheck(" << PYTHON_ARG << ", self->ob_type)\n" + << "&& PyObject_HasAttr(" << PYTHON_ARG << ", attrName)) {\n" + << "PyObject *revOpMethod = PyObject_GetAttr(" << PYTHON_ARG << ", attrName);\n" + << "if (revOpMethod && PyCallable_Check(revOpMethod)) {\n" << indent + << PYTHON_RETURN_VAR << " = PyObject_CallFunction(revOpMethod, \"O\", self);\n" + << "if (" << shibokenErrorsOccurred + << " && (PyErr_ExceptionMatches(PyExc_NotImplementedError)" + << " || PyErr_ExceptionMatches(PyExc_AttributeError))) {\n" << indent + << "PyErr_Clear();\n" + << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");\n" + << PYTHON_RETURN_VAR << " = " << NULL_PTR << ";\n" + << outdent << "}\n" + << outdent << "}\n" + << "Py_XDECREF(revOpMethod);\n\n" + << outdent << "}\n\n" + << "// Do not enter here if other object has implemented a reverse operator.\n" + << "if (" << PYTHON_RETURN_VAR << " == nullptr) {\n" << indent; + if (maxArgs > 0) + writeOverloadedFunctionDecisor(s, overloadData, ErrorReturn::Default); + writeFunctionCalls(s, overloadData, classContext, ErrorReturn::Default); + s << outdent << '\n' << "} // End of \"if (!" << PYTHON_RETURN_VAR << ")\"\n"; + } else { // binary shift operator + if (maxArgs > 0) + writeOverloadedFunctionDecisor(s, overloadData, ErrorReturn::Default); + writeFunctionCalls(s, overloadData, classContext, ErrorReturn::Default); } s << '\n'; - writeFunctionReturnErrorCheckSection(s, hasReturnValue && !rfunc->isInplaceOperator()); + writeFunctionReturnErrorCheckSection(s, ErrorReturn::Default, + hasReturnValue && !rfunc->isInplaceOperator()); if (hasReturnValue) { if (rfunc->isInplaceOperator()) { @@ -2146,24 +2290,21 @@ void CppGenerator::writeMethodWrapper(TextStream &s, const OverloadData &overloa s << "Py_RETURN_NONE;\n"; } - if (maxArgs > 0) - writeErrorSection(s, overloadData); - s<< outdent << "}\n\n"; } -void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData &overloadData) +void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData &overloadData, + ErrorReturn errorReturn) { const auto rfunc = overloadData.referenceFunction(); - s << "PyTuple_GET_SIZE(args);\n"; - writeUnusedVariableCast(s, QLatin1String("numArgs")); + s << "PyTuple_GET_SIZE(args);\n" << sbkUnusedVariableCast("numArgs"); int minArgs = overloadData.minArgs(); int maxArgs = overloadData.maxArgs(); s << "PyObject *"; s << PYTHON_ARGS << "[] = {" - << QString(maxArgs, QLatin1Char('0')).split(QLatin1String(""), Qt::SkipEmptyParts).join(QLatin1String(", ")) + << QByteArrayList(maxArgs, "nullptr").join(", ") << "};\n\n"; if (overloadData.hasVarargs()) { @@ -2182,47 +2323,34 @@ void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData & bool usesNamedArguments = overloadData.hasArgumentWithDefaultValue(); s << "// invalid argument lengths\n"; - bool ownerClassIsQObject = rfunc->ownerClass() && rfunc->ownerClass()->isQObject() && rfunc->isConstructor(); - if (usesNamedArguments) { - if (!ownerClassIsQObject) { - s << "if (numArgs > " << maxArgs << ") {\n"; - { - Indentation indent(s); - s << "static PyObject *const too_many = " - "Shiboken::String::createStaticString(\">\");\n" - << "errInfo.reset(too_many);\n" - << "Py_INCREF(errInfo.object());\n" - << "goto " << cpythonFunctionName(rfunc) << "_TypeError;\n"; - } - s << '}'; - } - if (minArgs > 0) { - if (!ownerClassIsQObject) - s << " else "; - s << "if (numArgs < " << minArgs << ") {\n"; - { - Indentation indent(s); - s << "static PyObject *const too_few = " - "Shiboken::String::createStaticString(\"<\");\n" - << "errInfo.reset(too_few);\n" - << "Py_INCREF(errInfo.object());\n" - << "goto " << cpythonFunctionName(rfunc) << "_TypeError;\n"; - } - s << '}'; - } + + // Disable argument count checks for QObject constructors to allow for + // passing properties as KW args. + const auto owner = rfunc->ownerClass(); + bool isQObjectConstructor = owner && isQObject(owner) + && rfunc->functionType() == AbstractMetaFunction::ConstructorFunction; + + if (usesNamedArguments && !isQObjectConstructor) { + s << "errInfo.reset(Shiboken::checkInvalidArgumentCount(numArgs, " + << minArgs << ", " << maxArgs << "));\n" + << "if (!errInfo.isNull())\n" << indent + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" + << outdent; } + const QList<int> invalidArgsLength = overloadData.invalidArgumentLengths(); if (!invalidArgsLength.isEmpty()) { - QStringList invArgsLen; - for (int i : qAsConst(invalidArgsLength)) - invArgsLen << u"numArgs == "_qs + QString::number(i); - if (usesNamedArguments && (!ownerClassIsQObject || minArgs > 0)) - s << " else "; - s << "if (" << invArgsLen.join(QLatin1String(" || ")) << ")\n"; - Indentation indent(s); - s << "goto " << cpythonFunctionName(rfunc) << "_TypeError;"; + s << "if ("; + for (qsizetype i = 0, size = invalidArgsLength.size(); i < size; ++i) { + if (i) + s << " || "; + s << "numArgs == " << invalidArgsLength.at(i); + } + s << ")\n" << indent + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" + << outdent; } - s << "\n\n"; + s << '\n'; QString funcName; if (rfunc->isOperatorOverload()) @@ -2230,8 +2358,8 @@ void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData & else funcName = rfunc->name(); - QString argsVar = overloadData.hasVarargs() ? QLatin1String("nonvarargs") : QLatin1String("args"); - s << "if (!"; + QString argsVar = overloadData.hasVarargs() ? u"nonvarargs"_s : u"args"_s; + s << "if ("; if (usesNamedArguments) { s << "PyArg_ParseTuple(" << argsVar << ", \"|" << QByteArray(maxArgs, 'O') << ':' << funcName << '"'; @@ -2241,190 +2369,187 @@ void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData & } for (int i = 0; i < maxArgs; i++) s << ", &(" << PYTHON_ARGS << '[' << i << "])"; - s << "))\n"; - { - Indentation indent(s); - s << returnStatement(m_currentErrorCode) << '\n'; - } - s << '\n'; + s << ") == 0)\n" << indent << errorReturn << outdent << '\n'; } void CppGenerator::writeCppSelfConversion(TextStream &s, const GeneratorContext &context, const QString &className, bool useWrapperClass) { - static const QString pythonSelfVar = QLatin1String("self"); + if (context.forSmartPointer()) { + writeSmartPointerCppSelfConversion(s, context); + return; + } + if (useWrapperClass) s << "static_cast<" << className << " *>("; - if (!context.forSmartPointer()) - s << cpythonWrapperCPtr(context.metaClass(), pythonSelfVar); - else - s << cpythonWrapperCPtr(context.preciseType(), pythonSelfVar); + s << cpythonWrapperCPtr(context.metaClass(), PYTHON_SELF_VAR); if (useWrapperClass) s << ')'; } +void CppGenerator::writeCppSelfVarDef(TextStream &s, + CppSelfDefinitionFlags flags) +{ + if (flags.testFlag(CppGenerator::CppSelfAsReference)) + s << "auto &" << CPP_SELF_VAR << " = *"; + else + s << "auto *" << CPP_SELF_VAR << " = "; +} + void CppGenerator::writeCppSelfDefinition(TextStream &s, const GeneratorContext &context, - bool hasStaticOverload, - bool hasClassMethodOverload, - bool cppSelfAsReference) const + ErrorReturn errorReturn, + CppSelfDefinitionFlags flags) { - Q_ASSERT(!(cppSelfAsReference && hasStaticOverload)); + Q_ASSERT(!(flags.testFlag(CppSelfAsReference) && flags.testFlag(HasStaticOverload))); + if (context.forSmartPointer()) { + writeSmartPointerCppSelfDefinition(s, context, errorReturn, flags); + return; + } - const AbstractMetaClass *metaClass = context.metaClass(); + AbstractMetaClassCPtr metaClass = context.metaClass(); const auto cppWrapper = context.metaClass()->cppWrapper(); // In the Python method, use the wrapper to access the protected // functions. const bool useWrapperClass = avoidProtectedHack() && cppWrapper.testFlag(AbstractMetaClass::CppProtectedHackWrapper); Q_ASSERT(!useWrapperClass || context.useWrapper()); - QString className; - if (!context.forSmartPointer()) { - className = useWrapperClass - ? context.wrapperName() - : (QLatin1String("::") + metaClass->qualifiedCppName()); - } else { - className = context.smartPointerWrapperName(); - } + const QString className = useWrapperClass + ? context.wrapperName() : getFullTypeName(metaClass); - writeInvalidPyObjectCheck(s, QLatin1String("self")); + writeInvalidPyObjectCheck(s, PYTHON_SELF_VAR, errorReturn); - if (cppSelfAsReference) { - s << "auto &" << CPP_SELF_VAR << " = *"; + if (flags.testFlag(CppSelfAsReference)) { + writeCppSelfVarDef(s, flags); writeCppSelfConversion(s, context, className, useWrapperClass); s << ";\n"; return; } - if (!hasStaticOverload) { - if (!hasClassMethodOverload) { + if (!flags.testFlag(HasStaticOverload)) { + if (!flags.testFlag(HasClassMethodOverload)) { // PYSIDE-131: The single case of a class method for now: tr(). - s << "auto " << CPP_SELF_VAR << " = "; + writeCppSelfVarDef(s, flags); writeCppSelfConversion(s, context, className, useWrapperClass); - s << ";\n"; - writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR)); + s << ";\n" << sbkUnusedVariableCast(CPP_SELF_VAR); } return; } - s << className << " *" << CPP_SELF_VAR << " = nullptr;\n"; - writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR)); + s << className << " *" << CPP_SELF_VAR << " = nullptr;\n" + << sbkUnusedVariableCast(CPP_SELF_VAR); // Checks if the underlying C++ object is valid. - s << "if (self)\n"; - { - Indentation indent(s); - s << CPP_SELF_VAR << " = "; - writeCppSelfConversion(s, context, className, useWrapperClass); - s << ";\n"; - } + s << "if (self)\n" << indent + << CPP_SELF_VAR << " = "; + writeCppSelfConversion(s, context, className, useWrapperClass); + s << ";\n"<< outdent; } void CppGenerator::writeCppSelfDefinition(TextStream &s, const AbstractMetaFunctionCPtr &func, const GeneratorContext &context, - bool hasStaticOverload, - bool hasClassMethodOverload) const + ErrorReturn errorReturn, + CppSelfDefinitionFlags flags) { if (!func->ownerClass() || func->isConstructor()) return; if (func->isOperatorOverload() && func->isBinaryOperator()) { QString checkFunc = cpythonCheckFunction(func->ownerClass()->typeEntry()); - s << "bool isReverse = " << checkFunc << PYTHON_ARG << ")\n"; - { - Indentation indent1(s, 4); - s << "&& !" << checkFunc << "self);\n"; - } - s << "if (isReverse)\n"; - Indentation indent(s); - s << "std::swap(self, " << PYTHON_ARG << ");\n"; + s << "bool isReverse = " << checkFunc << PYTHON_ARG << ")\n" + << " && !" << checkFunc << "self);\n" + << "if (isReverse)\n" << indent + << "std::swap(self, " << PYTHON_ARG << ");\n" << outdent; } - writeCppSelfDefinition(s, context, hasStaticOverload, hasClassMethodOverload); + writeCppSelfDefinition(s, context, errorReturn, flags); } -void CppGenerator::writeErrorSection(TextStream &s, const OverloadData &overloadData) +QString CppGenerator::returnErrorWrongArguments(const OverloadData &overloadData, + ErrorReturn errorReturn) { const auto rfunc = overloadData.referenceFunction(); - s << '\n' << cpythonFunctionName(rfunc) << "_TypeError:\n"; - Indentation indentation(s); QString argsVar = overloadData.pythonFunctionWrapperUsesListOfArguments() - ? QLatin1String("args") : QLatin1String(PYTHON_ARG); - s << "Shiboken::setErrorAboutWrongArguments(" << argsVar << ", fullName, errInfo);\n" - << "return " << m_currentErrorCode << ";\n"; + ? u"args"_s : PYTHON_ARG; + switch (errorReturn) { + case ErrorReturn::Default: + return u"Shiboken::returnWrongArguments("_s + argsVar + u", fullName, errInfo)"_s; + case ErrorReturn::Zero: + return u"Shiboken::returnWrongArguments_Zero("_s + argsVar + u", fullName, errInfo)"_s; + case ErrorReturn::MinusOne: + return u"Shiboken::returnWrongArguments_MinusOne("_s + argsVar + u", fullName, errInfo)"_s; + case ErrorReturn::Void: + Q_ASSERT(false); + } + return {}; } -void CppGenerator::writeFunctionReturnErrorCheckSection(TextStream &s, bool hasReturnValue) +void CppGenerator::writeFunctionReturnErrorCheckSection(TextStream &s, + ErrorReturn errorReturn, + bool hasReturnValue) { - s << "if (PyErr_Occurred()"; + s << "if (" << shibokenErrorsOccurred; if (hasReturnValue) - s << " || !" << PYTHON_RETURN_VAR; - s << ") {\n"; - { - Indentation indent(s); - if (hasReturnValue) - s << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");\n"; - s << returnStatement(m_currentErrorCode) << '\n'; - } - s << "}\n"; + s << " || " << PYTHON_RETURN_VAR << " == nullptr"; + s << ") {\n" << indent; + if (hasReturnValue) + s << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");\n"; + s << errorReturn << outdent << "}\n"; } -void CppGenerator::writeInvalidPyObjectCheck(TextStream &s, const QString &pyObj) +void CppGenerator::writeInvalidPyObjectCheck(TextStream &s, const QString &pyObj, + ErrorReturn errorReturn) { - s << "if (!Shiboken::Object::isValid(" << pyObj << "))\n"; - Indentation indent(s); - s << returnStatement(m_currentErrorCode) << '\n'; + s << "if (!Shiboken::Object::isValid(" << pyObj << "))\n" + << indent << errorReturn << outdent; } static QString pythonToCppConverterForArgumentName(const QString &argumentName) { - static const QRegularExpression pyArgsRegex(QLatin1String(PYTHON_ARGS) - + QLatin1String(R"((\[\d+[-]?\d*\]))")); + static const QRegularExpression pyArgsRegex(PYTHON_ARGS + + uR"((\[\d+[-]?\d*\]))"_s); Q_ASSERT(pyArgsRegex.isValid()); const QRegularExpressionMatch match = pyArgsRegex.match(argumentName); - QString result = QLatin1String(PYTHON_TO_CPP_VAR); + QString result = PYTHON_TO_CPP_VAR; if (match.hasMatch()) result += match.captured(1); return result; } -void CppGenerator::writeTypeCheck(TextStream &s, AbstractMetaType argType, - const QString &argumentName, bool isNumber, - const QString &customType, bool rejectNull) const +void CppGenerator::writeTypeCheck(TextStream &s, const QString &customType, + const QString &argumentName) { - 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. - auto customCheckResult = guessCPythonCheckFunction(customType); - customCheck = customCheckResult.checkFunction; - if (customCheckResult.type.has_value()) - argType = customCheckResult.type.value(); - } + QString errorMessage; + const auto metaTypeOpt = AbstractMetaType::fromString(customType, &errorMessage); + if (!metaTypeOpt.has_value()) + throw Exception(errorMessage); + writeTypeCheck(s, metaTypeOpt.value(), argumentName, + ShibokenGenerator::isNumber(metaTypeOpt.value())); +} +void CppGenerator::writeTypeCheck(TextStream &s, const AbstractMetaType &argType, + const QString &argumentName, bool isNumber, + bool rejectNull) +{ // TODO-CONVERTER: merge this with the code below. - QString typeCheck; - if (customCheck.isEmpty()) - typeCheck = cpythonIsConvertibleFunction(argType); - else - typeCheck = customCheck; - typeCheck.append(u'(' +argumentName + u')'); + QString typeCheck = cpythonIsConvertibleFunction(argType); + if (typeCheck != u"true") // For PyObject, which is always true + typeCheck.append(u'(' +argumentName + u')'); // TODO-CONVERTER ----------------------------------------------------------------------- - if (customCheck.isEmpty() && !argType.typeEntry()->isCustom()) { + if (!argType.typeEntry()->isCustom()) { typeCheck = u'(' + pythonToCppConverterForArgumentName(argumentName) - + u" = "_qs + typeCheck + u"))"_qs; - if (!isNumber && argType.typeEntry()->isCppPrimitive()) { + + u" = "_s + typeCheck + u"))"_s; + if (!isNumber && isCppPrimitive(argType.typeEntry())) { typeCheck.prepend(cpythonCheckFunction(argType) + u'(' - + argumentName + u") && "_qs); + + argumentName + u") && "_s); } } // TODO-CONVERTER ----------------------------------------------------------------------- if (rejectNull) - typeCheck = u'(' + argumentName + u" != Py_None && "_qs + typeCheck + u')'; + typeCheck = u'(' + argumentName + u" != Py_None && "_s + typeCheck + u')'; s << typeCheck; } @@ -2432,14 +2557,20 @@ void CppGenerator::writeTypeCheck(TextStream &s, AbstractMetaType argType, static void checkTypeViability(const AbstractMetaFunctionCPtr &func, const AbstractMetaType &type, int argIdx) { + const bool modified = argIdx == 0 + ? func->isTypeModified() + : func->arguments().at(argIdx -1).isTypeModified(); + const bool isRemoved = argIdx == 0 + ? func->argumentRemoved(0) + : func->arguments().at(argIdx -1).isModifiedRemoved(); if (type.isVoid() || !type.typeEntry()->isPrimitive() || type.indirections() == 0 || (type.indirections() == 1 && type.typeUsagePattern() == AbstractMetaType::NativePointerAsArrayPattern) || type.isCString() - || func->argumentRemoved(argIdx) - || !func->typeReplaced(argIdx).isEmpty() - || !func->conversionRule(TypeSystem::All, argIdx).isEmpty() + || isRemoved + || modified + || func->hasConversionRule(TypeSystem::All, argIdx) || func->hasInjectedCode()) return; QString message; @@ -2463,20 +2594,20 @@ static void checkTypeViability(const AbstractMetaFunctionCPtr &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); + for (qsizetype i = 0; i < func->arguments().size(); ++i) + checkTypeViability(func, func->arguments().at(i).type(), int(i + 1)); } void CppGenerator::writeTypeCheck(TextStream &s, - const QSharedPointer<OverloadDataNode> &overloadData, - const QString &argumentName) const + const std::shared_ptr<OverloadDataNode> &overloadData, + const QString &argumentName) { - QSet<const TypeEntry *> numericTypes; + QSet<TypeEntryCPtr> numericTypes; const OverloadDataList &siblings = overloadData->parent()->children(); for (const auto &sibling : siblings) { for (const auto &func : sibling->overloads()) { checkTypeViability(func); - const AbstractMetaType &argType = sibling->argument(func)->type(); + const AbstractMetaType &argType = sibling->overloadArgument(func)->type(); if (!argType.isPrimitive()) continue; if (ShibokenGenerator::isNumber(argType.typeEntry())) @@ -2486,30 +2617,33 @@ void CppGenerator::writeTypeCheck(TextStream &s, // This condition trusts that the OverloadData object will arrange for // PyLong type to come after the more precise numeric types (e.g. float and bool) - AbstractMetaType argType = overloadData->argType(); + AbstractMetaType argType = overloadData->modifiedArgType(); if (auto viewOn = argType.viewOn()) argType = *viewOn; - bool numberType = numericTypes.count() == 1 || ShibokenGenerator::isPyInt(argType); - QString customType = (overloadData->hasArgumentTypeReplace() ? overloadData->argumentTypeReplaced() : QString()); + const bool numberType = numericTypes.size() == 1 || ShibokenGenerator::isPyInt(argType); bool rejectNull = shouldRejectNullPointerArgument(overloadData->referenceFunction(), overloadData->argPos()); - writeTypeCheck(s, argType, argumentName, numberType, customType, rejectNull); + writeTypeCheck(s, argType, argumentName, numberType, rejectNull); } -void CppGenerator::writeArgumentConversion(TextStream &s, - const AbstractMetaType &argType, - const QString &argName, const QString &pyArgName, - const AbstractMetaClass *context, - const QString &defaultValue, - bool castArgumentAsUnused) const +qsizetype CppGenerator::writeArgumentConversion(TextStream &s, + const AbstractMetaType &argType, + const QString &argName, + const QString &pyArgName, + ErrorReturn errorReturn, + const AbstractMetaClassCPtr &context, + const QString &defaultValue, + bool castArgumentAsUnused) const { + qsizetype result = 0; if (argType.typeEntry()->isCustom() || argType.typeEntry()->isVarargs()) - return; + return result; if (argType.isWrapperType()) - writeInvalidPyObjectCheck(s, pyArgName); - writePythonToCppTypeConversion(s, argType, pyArgName, argName, context, defaultValue); + writeInvalidPyObjectCheck(s, pyArgName, errorReturn); + result = writePythonToCppTypeConversion(s, argType, pyArgName, argName, context, defaultValue); if (castArgumentAsUnused) - writeUnusedVariableCast(s, argName); + s << sbkUnusedVariableCast(argName); + return result; } AbstractMetaType @@ -2521,31 +2655,22 @@ AbstractMetaType return {}; } - QString typeReplaced = func->typeReplaced(index + 1); - if (typeReplaced.isEmpty()) { - auto argType = func->arguments().at(index).type(); - return argType.viewOn() ? *argType.viewOn() : argType; - } - - auto argType = AbstractMetaType::fromString(typeReplaced); - if (!argType.has_value()) - throw Exception(msgUnknownTypeInArgumentTypeReplacement(typeReplaced, func.data())); - return argType.value(); + auto argType = func->arguments().at(index).modifiedType(); + return argType.viewOn() ? *argType.viewOn() : argType; } static inline QString arrayHandleType(const AbstractMetaTypeList &nestedArrayTypes) { switch (nestedArrayTypes.size()) { case 1: - return QStringLiteral("Shiboken::Conversions::ArrayHandle<") - + nestedArrayTypes.constLast().minimalSignature() - + QLatin1Char('>'); + return "Shiboken::Conversions::ArrayHandle<"_L1 + + nestedArrayTypes.constLast().minimalSignature() + u'>'; case 2: - return QStringLiteral("Shiboken::Conversions::Array2Handle<") + return "Shiboken::Conversions::Array2Handle<"_L1 + nestedArrayTypes.constLast().minimalSignature() - + QStringLiteral(", ") + + ", "_L1 + QString::number(nestedArrayTypes.constFirst().arrayElementCount()) - + QLatin1Char('>'); + + u'>'; } return QString(); } @@ -2578,66 +2703,52 @@ static void writeMinimalConstructorExpression(TextStream &s, s << '(' << defaultValue << ')'; } -void CppGenerator::writePythonToCppTypeConversion(TextStream &s, +qsizetype CppGenerator::writePythonToCppTypeConversion(TextStream &s, const AbstractMetaType &type, const QString &pyIn, const QString &cppOut, - const AbstractMetaClass *context, + const AbstractMetaClassCPtr &context, const QString &defaultValue) const { - const TypeEntry *typeEntry = type.typeEntry(); + TypeEntryCPtr typeEntry = type.typeEntry(); if (typeEntry->isCustom() || typeEntry->isVarargs()) - return; + return 0; + + const auto arg = GeneratorArgument::fromMetaType(type); + const bool isPrimitive = arg.type == GeneratorArgument::Type::Primitive; + + QString cppOutAux = cppOut + u"_local"_s; - QString cppOutAux = cppOut + QLatin1String("_local"); - - const bool isPrimitive = typeEntry->isPrimitive(); - const bool isEnum = typeEntry->isEnum(); - const bool isFlags = typeEntry->isFlags(); - const bool treatAsPointer = type.valueTypeWithCopyConstructorOnlyPassed(); - bool isPointerOrObjectType = (type.isObjectType() || type.isPointer()) - && !type.isUserPrimitive() && !type.isExtendedCppPrimitive() - && !isEnum && !isFlags; - const bool isNotContainerEnumOrFlags = !typeEntry->isContainer() - && !isEnum && !isFlags; - const bool mayHaveImplicitConversion = type.referenceType() == LValueReference - && !type.isUserPrimitive() - && !type.isExtendedCppPrimitive() - && isNotContainerEnumOrFlags - && !(treatAsPointer || isPointerOrObjectType); - - // For implicit conversions or containers, either value or pointer conversion - // may occur. An implicit conversion uses value conversion whereas the object - // itself uses pointer conversion. - const bool valueOrPointer = mayHaveImplicitConversion; - - const AbstractMetaTypeList &nestedArrayTypes = type.nestedArrayTypes(); - const bool isCppPrimitiveArray = !nestedArrayTypes.isEmpty() - && nestedArrayTypes.constLast().isCppPrimitive(); - QString typeName = isCppPrimitiveArray - ? arrayHandleType(nestedArrayTypes) - : getFullTypeNameWithoutModifiers(type); + QString typeName = arg.type == GeneratorArgument::Type::CppPrimitiveArray + ? arrayHandleType(type.nestedArrayTypes()) + : getFullTypeNameWithoutModifiers(type); bool isProtectedEnum = false; - if (isEnum && avoidProtectedHack()) { + if (arg.type == GeneratorArgument::Type::Enum && avoidProtectedHack()) { auto metaEnum = api().findAbstractMetaEnum(type.typeEntry()); if (metaEnum.has_value() && metaEnum->isProtected()) { - typeName = wrapperName(context) + QLatin1String("::") + typeName = wrapperName(context) + u"::"_s + metaEnum.value().name(); isProtectedEnum = true; } } s << typeName; - if (isCppPrimitiveArray) { + switch (arg.conversion) { + case GeneratorArgument::Conversion::CppPrimitiveArray: s << ' ' << cppOut; - } else if (valueOrPointer) { + break; + case GeneratorArgument::Conversion::ValueOrPointer: { // Generate either value conversion for &cppOutAux or pointer // conversion for &cppOut s << ' ' << cppOutAux; - writeMinimalConstructorExpression(s, api(), type, isPrimitive, defaultValue); + // No default value for containers which can also be passed by pointer. + if (arg.type != GeneratorArgument::Type::Container || type.indirections() == 0) + writeMinimalConstructorExpression(s, api(), type, isPrimitive, defaultValue); s << ";\n" << typeName << " *" << cppOut << " = &" << cppOutAux; - } else if (treatAsPointer || isPointerOrObjectType) { + } + break; + case GeneratorArgument::Conversion::Pointer: { s << " *" << cppOut; if (!defaultValue.isEmpty()) { const bool needsConstCast = !isNullPtr(defaultValue) @@ -2650,7 +2761,9 @@ void CppGenerator::writePythonToCppTypeConversion(TextStream &s, if (needsConstCast) s << ')'; } - } else { + } + break; + case GeneratorArgument::Conversion::Default: s << ' ' << cppOut; if (isProtectedEnum && avoidProtectedHack()) { s << " = "; @@ -2658,19 +2771,22 @@ void CppGenerator::writePythonToCppTypeConversion(TextStream &s, s << "{}"; else s << defaultValue; - } else if (type.isUserPrimitive() || isEnum || isFlags) { + } else if (type.isUserPrimitive() + || arg.type == GeneratorArgument::Type::Enum + || arg.type == GeneratorArgument::Type::Flags) { writeMinimalConstructorExpression(s, api(), typeEntry, isPrimitive, defaultValue); - } else if (!type.isContainer() && !type.isSmartPointer()) { + } else if ((!type.isContainer() || type.indirections() == 0) && !type.isSmartPointer()) { writeMinimalConstructorExpression(s, api(), type, isPrimitive, defaultValue); } + break; } s << ";\n"; QString pythonToCppFunc = pythonToCppConverterForArgumentName(pyIn); - QString pythonToCppCall = pythonToCppFunc + u'(' + pyIn + u", &"_qs + QString pythonToCppCall = pythonToCppFunc + u'(' + pyIn + u", &"_s + cppOut + u')'; - if (!mayHaveImplicitConversion) { + if (arg.conversion != GeneratorArgument::Conversion::ValueOrPointer) { // pythonToCppFunc may be 0 when less parameters are passed and // the defaultValue takes effect. if (!defaultValue.isEmpty()) @@ -2678,7 +2794,7 @@ void CppGenerator::writePythonToCppTypeConversion(TextStream &s, s << pythonToCppCall << ";\n"; if (!defaultValue.isEmpty()) s << outdent; - return; + return arg.indirections; } // pythonToCppFunc may be 0 when less parameters are passed and @@ -2686,8 +2802,7 @@ void CppGenerator::writePythonToCppTypeConversion(TextStream &s, if (!defaultValue.isEmpty()) s << "if (" << pythonToCppFunc << ") {\n" << indent; - s << "if (Shiboken::Conversions::isImplicitConversion(reinterpret_cast<PyTypeObject *>(" - << cpythonTypeNameExt(type) << "), " << pythonToCppFunc << "))\n" + s << "if (" << pythonToCppFunc << ".isValue())\n" << indent << pythonToCppFunc << '(' << pyIn << ", &" << cppOutAux << ");\n" << outdent << "else\n" << indent << pythonToCppCall << ";\n" << outdent; @@ -2696,6 +2811,8 @@ void CppGenerator::writePythonToCppTypeConversion(TextStream &s, s << '\n'; else s << "}\n" << outdent; + + return arg.indirections; } static void addConversionRuleCodeSnippet(CodeSnipList &snippetList, QString &rule, @@ -2707,10 +2824,10 @@ static void addConversionRuleCodeSnippet(CodeSnipList &snippetList, QString &rul if (rule.isEmpty()) return; if (snippetLanguage == TypeSystem::TargetLangCode) { - rule.replace(QLatin1String("%in"), inputName); - rule.replace(QLatin1String("%out"), outputName + QLatin1String("_out")); + rule.replace(u"%in"_s, inputName); + rule.replace(u"%out"_s, outputName + u"_out"_s); } else { - rule.replace(QLatin1String("%out"), outputName); + rule.replace(u"%out"_s, outputName); } CodeSnip snip(snippetLanguage); snip.position = (snippetLanguage == TypeSystem::NativeCode) ? TypeSystem::CodeSnipPositionAny : TypeSystem::CodeSnipPositionBeginning; @@ -2753,17 +2870,19 @@ void CppGenerator::writeNoneReturn(TextStream &s, const AbstractMetaFunctionCPtr } } -void CppGenerator::writeOverloadedFunctionDecisor(TextStream &s, const OverloadData &overloadData) const +void CppGenerator::writeOverloadedFunctionDecisor(TextStream &s, + const OverloadData &overloadData, + ErrorReturn errorReturn) const { s << "// Overloaded function decisor\n"; const auto rfunc = overloadData.referenceFunction(); const AbstractMetaFunctionCList &functionOverloads = overloadData.overloads(); - for (int i = 0; i < functionOverloads.count(); i++) { + for (qsizetype i = 0; i < functionOverloads.size(); ++i) { const auto func = functionOverloads.at(i); s << "// " << i << ": "; if (func->isStatic()) s << "static "; - if (const auto *decl = func->declaringClass()) + if (const auto &decl = func->declaringClass()) s << decl->name() << "::"; s << func->signatureComment() << '\n'; } @@ -2773,18 +2892,16 @@ void CppGenerator::writeOverloadedFunctionDecisor(TextStream &s, const OverloadD // Ensure that the direct overload that called this reverse // is called. if (rfunc->isOperatorOverload() && !rfunc->isCallOperator()) { - s << "if (isReverse && overloadId == -1) {\n"; - { - Indentation indent(s); - s << "PyErr_SetString(PyExc_NotImplementedError, \"reverse operator not implemented.\");\n" - << "return {};\n"; - } - s << "}\n\n"; + s << "if (isReverse && overloadId == -1) {\n" << indent + << "Shiboken::Errors::setReverseOperatorNotImplemented();\n" + << "return {};\n" << outdent + << "}\n\n"; } s << "// Function signature not found.\n" - << "if (overloadId == -1) goto " - << cpythonFunctionName(overloadData.referenceFunction()) << "_TypeError;\n\n"; + << "if (overloadId == -1)\n" << indent + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n\n" + << outdent; } void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, @@ -2846,21 +2963,17 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, if (hasDefaultCall) { isFirst = false; int numArgs = node->argPos() + 1; - s << "if (numArgs == " << numArgs << ") {\n"; - { - Indentation indent(s); - auto func = referenceFunction; - for (const auto &child : children) { - const auto defValFunc = child->getFunctionWithDefaultValue(); - if (!defValFunc.isNull()) { - func = defValFunc; - break; - } + s << "if (numArgs == " << numArgs << ") {\n" << indent; + auto func = referenceFunction; + for (const auto &child : children) { + const auto defValFunc = child->getFunctionWithDefaultValue(); + if (defValFunc) { + func = defValFunc; + break; } - s << "overloadId = " << overloadData.functionNumber(func) - << "; // " << func->minimalSignature() << '\n'; } - s << '}'; + s << "overloadId = " << overloadData.functionNumber(func) + << "; // " << func->minimalSignature() << '\n' << outdent << '}'; } for (auto child : children) { @@ -2874,26 +2987,26 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, QString pyArgName = (usePyArgs && maxArgs > 1) ? pythonArgsAt(child->argPos()) - : QLatin1String(PYTHON_ARG); + : PYTHON_ARG; auto od = child; int startArg = od->argPos(); int sequenceArgCount = 0; while (od && !od->argType().isVarargs()) { - bool typeReplacedByPyObject = od->argumentTypeReplaced() == cPyObjectT(); + const bool typeReplacedByPyObject = od->isTypeModified() + && od->modifiedArgType().name() == cPyObjectT; if (!typeReplacedByPyObject) { if (usePyArgs) pyArgName = pythonArgsAt(od->argPos()); StringStream tck(TextStream::Language::Cpp); auto func = od->referenceFunction(); - if (func->isConstructor() && func->arguments().count() == 1) { - const AbstractMetaClass *ownerClass = func->ownerClass(); - const ComplexTypeEntry *baseContainerType = ownerClass->typeEntry()->baseContainerType(); + if (func->isConstructor() && func->arguments().size() == 1) { + AbstractMetaClassCPtr ownerClass = func->ownerClass(); + ComplexTypeEntryCPtr baseContainerType = ownerClass->typeEntry()->baseContainerType(); if (baseContainerType && baseContainerType == func->arguments().constFirst().type().typeEntry() && ownerClass->isCopyable()) { - tck << '!' << cpythonCheckFunction(ownerClass->typeEntry()) << pyArgName << ")\n"; - Indentation indent(s); - tck << "&& "; + tck << '!' << cpythonCheckFunction(ownerClass->typeEntry()) + << pyArgName << ")\n" << indent << "&& " << outdent; } } writeTypeCheck(tck, od, pyArgName); @@ -2919,16 +3032,16 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, int numArgs = args.size() - OverloadData::numberOfRemovedArguments(refFunc); if (isVarargs) --numArgs; - QString check = (isVarargs ? u"numArgs >= "_qs : u"numArgs == "_qs) + QString check = (isVarargs ? u"numArgs >= "_s : u"numArgs == "_s) + QString::number(numArgs); typeChecks.prepend(check); } else if (usePyArgs && sequenceArgCount > 0) { - typeChecks.prepend(u"numArgs >= "_qs + QString::number(startArg + sequenceArgCount)); + typeChecks.prepend(u"numArgs >= "_s + QString::number(startArg + sequenceArgCount)); } else if (refFunc->isOperatorOverload() && !refFunc->isCallOperator()) { QString check; if (!refFunc->isReverseOperator()) - check.append(QLatin1Char('!')); - check.append(QLatin1String("isReverse")); + check.append(u'!'); + check.append(u"isReverse"_s); typeChecks.prepend(check); } @@ -2941,88 +3054,87 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, if (typeChecks.isEmpty()) { s << "true"; } else { - Indentation indent(s); - s << typeChecks.join(QLatin1String("\n&& ")); + s << indent << typeChecks.join(u"\n&& "_s) << outdent; } - s << ") {\n"; - { - Indentation indent(s); - writeOverloadedFunctionDecisorEngine(s, overloadData, child.data()); - } - s << "}"; + s << ") {\n" << indent; + writeOverloadedFunctionDecisorEngine(s, overloadData, child.get()); + s << outdent << '}'; } s << '\n'; } void CppGenerator::writeFunctionCalls(TextStream &s, const OverloadData &overloadData, - const GeneratorContext &context) const + const GeneratorContext &context, + ErrorReturn errorReturn) const { const AbstractMetaFunctionCList &overloads = overloadData.overloads(); s << "// Call function/method\n" - << (overloads.count() > 1 ? "switch (overloadId) " : "") << "{\n"; - { - Indentation indent(s); - if (overloads.count() == 1) { - writeSingleFunctionCall(s, overloadData, overloads.constFirst(), context); - } else { - for (int i = 0; i < overloads.count(); i++) { - const auto func = overloads.at(i); - s << "case " << i << ": // " << func->signature() << "\n{\n"; - { - Indentation indent(s); - writeSingleFunctionCall(s, overloadData, func, context); - if (func->attributes().testFlag(AbstractMetaFunction::Deprecated)) { - s << "PyErr_WarnEx(PyExc_DeprecationWarning, \""; - if (auto cls = context.metaClass()) - s << cls->name() << '.'; - s << func->signature() << " is deprecated\", 1);\n"; - } - s << "break;\n"; - } - s << "}\n"; - } + << (overloads.size() > 1 ? "switch (overloadId) " : "") << "{\n" << indent; + if (overloads.size() == 1) { + writeSingleFunctionCall(s, overloadData, overloads.constFirst(), context, + errorReturn); + } else { + for (qsizetype i = 0; i < overloads.size(); ++i) { + const auto func = overloads.at(i); + s << "case " << i << ": // " << func->signature() << "\n{\n" << indent; + writeSingleFunctionCall(s, overloadData, func, context, errorReturn); + s << "break;\n" << outdent << "}\n"; } } - s << "}\n"; + s << outdent << "}\n"; +} + +static void writeDeprecationWarning(TextStream &s, + const GeneratorContext &context, + const AbstractMetaFunctionCPtr &func, + CppGenerator::ErrorReturn errorReturn) +{ + s << "Shiboken::Warnings::warnDeprecated(\""; + if (const auto cls = context.metaClass()) + s << cls->name() << "\", "; + // Check error in case "warning-as-error" is set. + s << '"' << func->signature().replace(u"::"_s, u"."_s) << "\");\n" + << "if (" << shibokenErrorsOccurred << ")\n" + << indent << errorReturn << outdent; } void CppGenerator::writeSingleFunctionCall(TextStream &s, const OverloadData &overloadData, const AbstractMetaFunctionCPtr &func, - const GeneratorContext &context) const + const GeneratorContext &context, + ErrorReturn errorReturn) const { - if (func->isDeprecated()) { - s << "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->isDeprecated()) + writeDeprecationWarning(s, context, func, errorReturn); if (func->functionType() == AbstractMetaFunction::EmptyFunction) { - s << "PyErr_Format(PyExc_TypeError, \"%s is a private method.\", \"" - << func->signature().replace(QLatin1String("::"), QLatin1String(".")) - << "\");\n" - << returnStatement(m_currentErrorCode) << '\n'; + s << "Shiboken::Errors::setPrivateMethod(\"" + << func->signature().replace(u"::"_s, u"."_s) << "\");\n" + << errorReturn; return; } const bool usePyArgs = overloadData.pythonFunctionWrapperUsesListOfArguments(); // Handle named arguments. - writeNamedArgumentResolution(s, func, usePyArgs, overloadData); + writeNamedArgumentResolution(s, func, usePyArgs, overloadData, errorReturn); 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 auto argCount = func->arguments().size(); + QList<qsizetype> indirections(argCount, 0); + for (qsizetype argIdx = 0; argIdx < argCount; ++argIdx) { + const bool hasConversionRule = + func->hasConversionRule(TypeSystem::NativeCode, int(argIdx + 1)); const AbstractMetaArgument &arg = func->arguments().at(argIdx); - if (func->argumentRemoved(argIdx + 1)) { + if (arg.isModifiedRemoved()) { if (!arg.defaultValueExpression().isEmpty()) { - const QString cppArgRemoved = QLatin1String(CPP_ARG_REMOVED) - + QString::number(argIdx); + const QString cppArgRemoved = CPP_ARG_REMOVED(argIdx); s << getFullTypeName(arg.type()) << ' ' << cppArgRemoved; - s << " = " << guessScopeForDefaultValue(func, arg) << ";\n"; - writeUnusedVariableCast(s, cppArgRemoved); + s << " = " << arg.defaultValueExpression() << ";\n" + << sbkUnusedVariableCast(cppArgRemoved); } else if (!injectCodeCallsFunc && !func->isUserAdded() && !hasConversionRule) { // When an argument is removed from a method signature and no other means of calling // the method are provided (as with code injection) the generator must abort. @@ -3041,22 +3153,22 @@ void CppGenerator::writeSingleFunctionCall(TextStream &s, continue; auto argType = getArgumentType(func, argIdx); 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()); + QString pyArgName = usePyArgs ? pythonArgsAt(argPos) : PYTHON_ARG; + indirections[argIdx] = + writeArgumentConversion(s, argType, CPP_ARG_N(argPos), pyArgName, errorReturn, + func->implementingClass(), arg.defaultValueExpression(), + func->isUserAdded()); } s << '\n'; int numRemovedArgs = OverloadData::numberOfRemovedArguments(func); - s << "if (!PyErr_Occurred()) {\n" << indent; + s << "if (Shiboken::Errors::occurred() == nullptr) {\n" << indent; writeMethodCall(s, func, context, overloadData.pythonFunctionWrapperUsesListOfArguments(), - func->arguments().size() - numRemovedArgs); + func->arguments().size() - numRemovedArgs, indirections, errorReturn); + if (!func->isConstructor()) writeNoneReturn(s, func, overloadData.hasNonVoidReturnType()); s << outdent << "}\n"; @@ -3066,34 +3178,34 @@ QString CppGenerator::cppToPythonFunctionName(const QString &sourceTypeName, QSt { if (targetTypeName.isEmpty()) targetTypeName = sourceTypeName; - return sourceTypeName + QLatin1String("_CppToPython_") + targetTypeName; + return sourceTypeName + u"_CppToPython_"_s + targetTypeName; } QString CppGenerator::pythonToCppFunctionName(const QString &sourceTypeName, const QString &targetTypeName) { - return sourceTypeName + QLatin1String("_PythonToCpp_") + targetTypeName; + return sourceTypeName + u"_PythonToCpp_"_s + targetTypeName; } QString CppGenerator::pythonToCppFunctionName(const AbstractMetaType &sourceType, const AbstractMetaType &targetType) { return pythonToCppFunctionName(fixedCppTypeName(sourceType), fixedCppTypeName(targetType)); } -QString CppGenerator::pythonToCppFunctionName(const CustomConversion::TargetToNativeConversion *toNative, - const TypeEntry *targetType) +QString CppGenerator::pythonToCppFunctionName(const TargetToNativeConversion &toNative, + const TypeEntryCPtr &targetType) { return pythonToCppFunctionName(fixedCppTypeName(toNative), fixedCppTypeName(targetType)); } QString CppGenerator::convertibleToCppFunctionName(const QString &sourceTypeName, const QString &targetTypeName) { - return QLatin1String("is_") + sourceTypeName + QLatin1String("_PythonToCpp_") - + targetTypeName + QLatin1String("_Convertible"); + return u"is_"_s + sourceTypeName + u"_PythonToCpp_"_s + + targetTypeName + u"_Convertible"_s; } QString CppGenerator::convertibleToCppFunctionName(const AbstractMetaType &sourceType, const AbstractMetaType &targetType) { return convertibleToCppFunctionName(fixedCppTypeName(sourceType), fixedCppTypeName(targetType)); } -QString CppGenerator::convertibleToCppFunctionName(const CustomConversion::TargetToNativeConversion *toNative, - const TypeEntry *targetType) +QString CppGenerator::convertibleToCppFunctionName(const TargetToNativeConversion &toNative, + const TypeEntryCPtr &targetType) { return convertibleToCppFunctionName(fixedCppTypeName(toNative), fixedCppTypeName(targetType)); } @@ -3103,9 +3215,10 @@ void CppGenerator::writeCppToPythonFunction(TextStream &s, const QString &code, { QString prettyCode = code; - processCodeSnip(prettyCode); + const QString funcName = cppToPythonFunctionName(sourceTypeName, targetTypeName); + processCodeSnip(prettyCode, funcName); - s << "static PyObject *" << cppToPythonFunctionName(sourceTypeName, targetTypeName) + s << "static PyObject *" << funcName << "(const void *cppIn)\n{\n" << indent << prettyCode << ensureEndl << outdent << "}\n"; } @@ -3128,53 +3241,66 @@ static void replaceCppToPythonVariables(QString &code, const QString &typeName, bool constRef = false) { CodeSnipAbstract::prependCode(&code, writeCppInRef(typeName, constRef)); - code.replace(QLatin1String("%INTYPE"), typeName); - code.replace(QLatin1String("%OUTTYPE"), QLatin1String("PyObject *")); - code.replace(QLatin1String("%in"), QLatin1String("cppInRef")); - code.replace(QLatin1String("%out"), QLatin1String("pyOut")); + code.replace(u"%INTYPE"_s, typeName); + code.replace(u"%OUTTYPE"_s, u"PyObject *"_s); + code.replace(u"%in"_s, u"cppInRef"_s); + code.replace(u"%out"_s, u"pyOut"_s); } -void CppGenerator::writeCppToPythonFunction(TextStream &s, const CustomConversion *customConversion) const +void CppGenerator::writeCppToPythonFunction(TextStream &s, + const CustomConversionPtr &customConversion) const { QString code = customConversion->nativeToTargetConversion(); - auto *ownerType = customConversion->ownerType(); + auto ownerType = customConversion->ownerType(); const bool constRef = !ownerType->isPrimitive(); // PyCapsule needs a non-const ref replaceCppToPythonVariables(code, getFullTypeName(ownerType), constRef); writeCppToPythonFunction(s, code, fixedCppTypeName(customConversion->ownerType())); } + +QString CppGenerator::containerNativeToTargetTypeName(const ContainerTypeEntryCPtr &type) +{ + QString result = type->targetLangApiName(); + if (result != cPyObjectT) { + result = containerCpythonBaseName(type); + if (result == cPySequenceT) + result = cPyListT; + } + return result; +} + void CppGenerator::writeCppToPythonFunction(TextStream &s, const AbstractMetaType &containerType) const { - const CustomConversion *customConversion = containerType.typeEntry()->customConversion(); - if (!customConversion) { + Q_ASSERT(containerType.typeEntry()->isContainer()); + auto cte = std::static_pointer_cast<const ContainerTypeEntry>(containerType.typeEntry()); + if (!cte->hasCustomConversion()) { QString m; QTextStream(&m) << "Can't write the C++ to Python conversion function for container type '" << containerType.typeEntry()->qualifiedCppName() << "' - no conversion rule was defined for it in the type system."; throw Exception(m); } - if (!containerType.typeEntry()->isContainer()) { - writeCppToPythonFunction(s, customConversion); - return; - } + const auto customConversion = cte->customConversion(); QString code = customConversion->nativeToTargetConversion(); - for (int i = 0; i < containerType.instantiations().count(); ++i) { + for (qsizetype i = 0; i < containerType.instantiations().size(); ++i) { const AbstractMetaType &type = containerType.instantiations().at(i); QString typeName = getFullTypeName(type); if (type.isConstant()) - typeName = QLatin1String("const ") + typeName; - code.replace(u"%INTYPE_"_qs + QString::number(i), typeName); + typeName = u"const "_s + typeName; + code.replace(u"%INTYPE_"_s + QString::number(i), typeName); } replaceCppToPythonVariables(code, getFullTypeNameWithoutModifiers(containerType), true); - processCodeSnip(code); - writeCppToPythonFunction(s, code, fixedCppTypeName(containerType)); + processCodeSnip(code, containerType.typeEntry()->qualifiedCppName()); + writeCppToPythonFunction(s, code, fixedCppTypeName(containerType), + containerNativeToTargetTypeName(cte)); } void CppGenerator::writePythonToCppFunction(TextStream &s, const QString &code, const QString &sourceTypeName, const QString &targetTypeName) const { QString prettyCode = code; - processCodeSnip(prettyCode); - s << "static void " << pythonToCppFunctionName(sourceTypeName, targetTypeName) + const QString funcName = pythonToCppFunctionName(sourceTypeName, targetTypeName); + processCodeSnip(prettyCode, funcName); + s << "static void " << funcName << "(PyObject *pyIn, void *cppOut)\n{\n" << indent << prettyCode << ensureEndl << outdent << "}\n"; } @@ -3192,16 +3318,15 @@ void CppGenerator::writeIsPythonConvertibleToCppFunction(TextStream &s, s << "static PythonToCppFunc " << convertibleToCppFunctionName(sourceTypeName, targetTypeName); s << "(PyObject *pyIn)\n{\n" << indent; if (acceptNoneAsCppNull) { - s << "if (pyIn == Py_None)\n"; - Indentation indent(s); - s << "return Shiboken::Conversions::nonePythonToCppNullPtr;\n"; - } - s << "if (" << condition << ")\n"; - { - Indentation indent(s); - s << "return " << pythonToCppFuncName << ";\n"; + s << "if (pyIn == Py_None)\n" << indent + << "return Shiboken::Conversions::nonePythonToCppNullPtr;\n" << outdent; + } else { + if (!condition.contains(u"pyIn")) + s << sbkUnusedVariableCast("pyIn"); } - s << "return {};\n" << outdent << "}\n"; + s << "if (" << condition << ")\n" << indent + << "return " << pythonToCppFuncName << ";\n" << outdent + << "return {};\n" << outdent << "}\n"; } void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, @@ -3216,103 +3341,97 @@ void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, // Python to C++ conversion function. StringStream c(TextStream::Language::Cpp); if (conversion.isEmpty()) - conversion = QLatin1Char('*') + cpythonWrapperCPtr(sourceType, QLatin1String("pyIn")); + conversion = u'*' + cpythonWrapperCPtr(sourceType, u"pyIn"_s); if (!preConversion.isEmpty()) c << preConversion << '\n'; const QString fullTypeName = targetType.isSmartPointer() ? targetType.cppSignature() : getFullTypeName(targetType.typeEntry()); c << "*reinterpret_cast<" << fullTypeName << " *>(cppOut) = " - << fullTypeName << '(' << conversion << ");"; + << fullTypeName << '(' + << (sourceType.isUniquePointer() ? stdMove(conversion) : conversion) + << ");"; QString sourceTypeName = fixedCppTypeName(sourceType); QString targetTypeName = fixedCppTypeName(targetType); writePythonToCppFunction(s, c.toString(), sourceTypeName, targetTypeName); // Python to C++ convertible check function. if (typeCheck.isEmpty()) - typeCheck = u"PyObject_TypeCheck(pyIn, "_qs + sourcePyType + u')'; + typeCheck = u"PyObject_TypeCheck(pyIn, "_s + sourcePyType + u')'; writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, typeCheck); s << '\n'; } void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, - const CustomConversion::TargetToNativeConversion *toNative, - const TypeEntry *targetType) const + const TargetToNativeConversion &toNative, + const TypeEntryCPtr &targetType) const { // Python to C++ conversion function. - QString code = toNative->conversion(); + QString code = toNative.conversion(); QString inType; - if (toNative->sourceType()) - inType = cpythonTypeNameExt(toNative->sourceType()); + if (toNative.sourceType()) + inType = cpythonTypeNameExt(toNative.sourceType()); else - inType = u'(' + toNative->sourceTypeName() + u"_TypeF())"_qs; - 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)")); + inType = u'(' + toNative.sourceTypeName() + u"_TypeF())"_s; + code.replace(u"%INTYPE"_s, inType); + code.replace(u"%OUTTYPE"_s, targetType->qualifiedCppName()); + code.replace(u"%in"_s, u"pyIn"_s); + code.replace(u"%out"_s, + u"*reinterpret_cast<"_s + getFullTypeName(targetType) + u" *>(cppOut)"_s); QString sourceTypeName = fixedCppTypeName(toNative); QString targetTypeName = fixedCppTypeName(targetType); writePythonToCppFunction(s, code, sourceTypeName, targetTypeName); // Python to C++ convertible check function. - QString typeCheck = toNative->sourceTypeCheck(); + 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 == cPyTypeObjectT()) - typeCheck = QLatin1String("PyType_Check(%in)"); - else if (pyTypeName == cPyObjectT()) - typeCheck = QLatin1String("PyObject_TypeCheck(%in, &PyBaseObject_Type)"); - // PYSIDE-795: We abuse PySequence for iterables - else if (pyTypeName == cPySequenceT()) - typeCheck = QLatin1String("Shiboken::String::checkIterable(%in)"); - else if (pyTypeName.startsWith(QLatin1String("Py"))) - typeCheck = pyTypeName + QLatin1String("_Check(%in)"); + QString pyTypeName = toNative.sourceTypeName(); + if (pyTypeName == u"Py_None" || pyTypeName == u"PyNone") + typeCheck = u"%in == Py_None"_s; + else if (pyTypeName == u"SbkObject") + typeCheck = u"Shiboken::Object::checkType(%in)"_s; } if (typeCheck.isEmpty()) { - if (!toNative->sourceType() || toNative->sourceType()->isPrimitive()) { + if (!toNative.sourceType() || toNative.sourceType()->isPrimitive()) { QString m; QTextStream(&m) << "User added implicit conversion for C++ type '" << targetType->qualifiedCppName() << "' must provide either an input type check function or a non primitive type entry."; throw Exception(m); } - typeCheck = u"PyObject_TypeCheck(%in, "_qs - + cpythonTypeNameExt(toNative->sourceType()) + u')'; + typeCheck = u"PyObject_TypeCheck(%in, "_s + + cpythonTypeNameExt(toNative.sourceType()) + u')'; } - typeCheck.replace(QLatin1String("%in"), QLatin1String("pyIn")); - processCodeSnip(typeCheck); + typeCheck.replace(u"%in"_s, u"pyIn"_s); + processCodeSnip(typeCheck, targetType->qualifiedCppName()); writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, typeCheck); } void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, const AbstractMetaType &containerType) const { - const CustomConversion *customConversion = containerType.typeEntry()->customConversion(); - if (!customConversion) { - //qFatal - return; - } - const CustomConversion::TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions(); - if (toCppConversions.isEmpty()) { - //qFatal - return; - } + Q_ASSERT(containerType.typeEntry()->isContainer()); + const auto cte = std::static_pointer_cast<const ContainerTypeEntry>(containerType.typeEntry()); + const auto customConversion = cte->customConversion(); + for (const auto &conv : customConversion->targetToNativeConversions()) + writePythonToCppConversionFunction(s, containerType, conv); +} + +void CppGenerator::writePythonToCppConversionFunction(TextStream &s, + const AbstractMetaType &containerType, + const TargetToNativeConversion &conv) const +{ // Python to C++ conversion function. QString cppTypeName = getFullTypeNameWithoutModifiers(containerType); - QString code = toCppConversions.constFirst()->conversion(); - const QString line = QLatin1String("auto &cppOutRef = *reinterpret_cast<") - + cppTypeName + QLatin1String(" *>(cppOut);"); + QString code = conv.conversion(); + const QString line = u"auto &cppOutRef = *reinterpret_cast<"_s + + cppTypeName + u" *>(cppOut);"_s; CodeSnipAbstract::prependCode(&code, line); - for (int i = 0; i < containerType.instantiations().count(); ++i) { + for (qsizetype i = 0; i < containerType.instantiations().size(); ++i) { const AbstractMetaType &type = containerType.instantiations().at(i); QString typeName = getFullTypeName(type); - if (type.shouldDereferenceArgument()) { + // Containers of opaque containers are not handled here. + const auto generatorArg = GeneratorArgument::fromMetaType(type); + if (generatorArg.indirections > 0 && !type.generateOpaqueContainer()) { for (int pos = 0; ; ) { const QRegularExpressionMatch match = convertToCppRegEx().match(code, pos); if (!match.hasMatch()) @@ -3320,26 +3439,27 @@ void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, const Abst pos = match.capturedEnd(); const QString varName = match.captured(1); QString rightCode = code.mid(pos); - rightCode.replace(varName, QLatin1Char('*') + varName); + rightCode.replace(varName, u'*' + varName); code.replace(pos, code.size() - pos, rightCode); } - typeName.append(QLatin1String(" *")); + typeName.append(u" *"_s); } - code.replace(u"%OUTTYPE_"_qs + QString::number(i), typeName); + code.replace(u"%OUTTYPE_"_s + QString::number(i), typeName); } - code.replace(QLatin1String("%OUTTYPE"), cppTypeName); - code.replace(QLatin1String("%in"), QLatin1String("pyIn")); - code.replace(QLatin1String("%out"), QLatin1String("cppOutRef")); + code.replace(u"%OUTTYPE"_s, cppTypeName); + code.replace(u"%in"_s, u"pyIn"_s); + code.replace(u"%out"_s, u"cppOutRef"_s); QString typeName = fixedCppTypeName(containerType); - writePythonToCppFunction(s, code, typeName, typeName); + const QString &sourceTypeName = conv.sourceTypeName(); + writePythonToCppFunction(s, code, sourceTypeName, typeName); // Python to C++ convertible check function. QString typeCheck = cpythonCheckFunction(containerType); if (typeCheck.isEmpty()) - typeCheck = QLatin1String("false"); + typeCheck = u"false"_s; else - typeCheck = typeCheck + QLatin1String("pyIn)"); - writeIsPythonConvertibleToCppFunction(s, typeName, typeName, typeCheck); + typeCheck = typeCheck + u"pyIn)"_s; + writeIsPythonConvertibleToCppFunction(s, sourceTypeName, typeName, typeCheck); s << '\n'; } @@ -3371,139 +3491,168 @@ void CppGenerator::writeSetPythonToCppPointerConversion(TextStream &s, converterVar, pythonToCppFunc, isConvertibleFunc); } -void CppGenerator::writeNamedArgumentResolution(TextStream &s, const AbstractMetaFunctionCPtr &func, - bool usePyArgs, const OverloadData &overloadData) const +// PYSIDE-1986: Some QObject derived classes, (QVBoxLayout) do not have default +// arguments, which breaks setting properties by named arguments. Force the +// handling code to be generated nevertheless for applicable widget classes, +// so that the mechanism of falling through to the error handling to set +// the properties works nevertheless. +static bool forceQObjectNamedArguments(const AbstractMetaFunctionCPtr &func) +{ + if (func->functionType() != AbstractMetaFunction::ConstructorFunction) + return false; + const auto owner = func->ownerClass(); + Q_ASSERT(owner); + if (!isQObject(owner)) + return false; + const QString &name = owner->name(); + return name == u"QVBoxLayout" || name == u"QHBoxLayout" + || name == u"QSplitterHandle" || name == u"QSizeGrip"; +} + +void CppGenerator::writeNamedArgumentResolution(TextStream &s, + const AbstractMetaFunctionCPtr &func, + bool usePyArgs, + const OverloadData &overloadData, + ErrorReturn errorReturn) { const AbstractMetaArgumentList &args = OverloadData::getArgumentsWithDefaultValues(func); - if (args.isEmpty()) { + const bool hasDefaultArguments = !args.isEmpty(); + const bool force = !hasDefaultArguments && usePySideExtensions() + && forceQObjectNamedArguments(func); + if (!hasDefaultArguments && !force) { if (overloadData.hasArgumentWithDefaultValue()) { // PySide-535: Allow for empty dict instead of nullptr in PyPy - s << "if (kwds && PyDict_Size(kwds) > 0) {\n"; - { - Indentation indent(s); - s << "errInfo.reset(kwds);\n" - << "Py_INCREF(errInfo.object());\n" - << "goto " << cpythonFunctionName(func) << "_TypeError;\n"; - } - s << "}\n"; + s << "if (kwds != nullptr && PyDict_Size(kwds) > 0) {\n" << indent + << "errInfo.reset(kwds);\n" + << "Py_INCREF(errInfo.object());\n" + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" + << outdent << "}\n"; } return; } // PySide-535: Allow for empty dict instead of nullptr in PyPy - s << "if (kwds && PyDict_Size(kwds) > 0) {\n"; - { - Indentation indent(s); - s << "PyObject *value{};\n" - << "PyObject *kwds_dup = PyDict_Copy(kwds);\n"; - for (const AbstractMetaArgument &arg : args) { - const int pyArgIndex = arg.argumentIndex() - - OverloadData::numberOfRemovedArguments(func, arg.argumentIndex()); - QString pyArgName = usePyArgs ? pythonArgsAt(pyArgIndex) : QLatin1String(PYTHON_ARG); - QString pyKeyName = QLatin1String("key_") + arg.name(); - s << "static PyObject *const " << pyKeyName - << " = Shiboken::String::createStaticString(\"" << arg.name() << "\");\n" - << "if (PyDict_Contains(kwds, " << pyKeyName << ")) {\n"; - { - Indentation indent(s); - s << "value = PyDict_GetItem(kwds, " << pyKeyName << ");\n" - << "if (value && " << pyArgName << ") {\n"; - { - Indentation indent(s); - s << "errInfo.reset(" << pyKeyName << ");\n" - << "Py_INCREF(errInfo.object());\n" - << "goto " << cpythonFunctionName(func) << "_TypeError;\n"; - } - s << "}\nif (value) {\n"; - { - Indentation indent(s); - s << pyArgName << " = value;\nif (!"; - writeTypeCheck(s, arg.type(), pyArgName, isNumber(arg.type().typeEntry()), - func->typeReplaced(arg.argumentIndex() + 1)); - s << ")\n"; - { - Indentation indent(s); - s << "goto " << cpythonFunctionName(func) << "_TypeError;\n"; - } - } - s << "}\nPyDict_DelItem(kwds_dup, " << pyKeyName << ");\n"; - } - s << "}\n"; - } - // PYSIDE-1305: Handle keyword args correctly. - // Normal functions handle their parameters immediately. - // For constructors that are QObject, we need to delay that - // until extra keyword signals and properties are handled. - s << "if (PyDict_Size(kwds_dup) > 0) {\n"; - { - Indentation indent(s); - s << "errInfo.reset(kwds_dup);\n"; - if (!(func->isConstructor() && func->ownerClass()->isQObject())) - s << "goto " << cpythonFunctionName(func) << "_TypeError;\n"; - else - s << "// fall through to handle extra keyword signals and properties\n"; - } - s << "}\n"; - } - s << "}\n"; + s << "if (kwds && PyDict_Size(kwds) > 0) {\n" << indent; + if (!force) + s << "PyObject *value{};\n"; + s << "Shiboken::AutoDecRef kwds_dup(PyDict_Copy(kwds));\n"; + for (const AbstractMetaArgument &arg : args) { + const int pyArgIndex = arg.argumentIndex() + - OverloadData::numberOfRemovedArguments(func, arg.argumentIndex()); + QString pyArgName = usePyArgs ? pythonArgsAt(pyArgIndex) + : PYTHON_ARG; + QString pyKeyName = u"key_"_s + arg.name(); + s << "static PyObject *const " << pyKeyName + << " = Shiboken::String::createStaticString(\"" << arg.name() << "\");\n" + << "if (PyDict_Contains(kwds, " << pyKeyName << ") != 0) {\n" << indent + << "value = PyDict_GetItem(kwds, " << pyKeyName << ");\n" + << "if (value != nullptr && " << pyArgName << " != nullptr ) {\n" + << indent << "errInfo.reset(" << pyKeyName << ");\n" + << "Py_INCREF(errInfo.object());\n" + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" + << outdent << "}\nif (value != nullptr) {\n" << indent + << pyArgName << " = value;\nif (!"; + const auto &type = arg.modifiedType(); + writeTypeCheck(s, type, pyArgName, isNumber(type.typeEntry()), {}); + s << ")\n" << indent + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" + << outdent << outdent + << "}\nPyDict_DelItem(kwds_dup, " << pyKeyName << ");\n" + << outdent << "}\n"; + } + // PYSIDE-1305: Handle keyword args correctly. + // Normal functions handle their parameters immediately. + // For constructors that are QObject, we need to delay that + // until extra keyword signals and properties are handled. + s << "if (PyDict_Size(kwds_dup) > 0) {\n" << indent + << "errInfo.reset(kwds_dup.release());\n"; + if (!(func->isConstructor() && isQObject(func->ownerClass()))) + s << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"; + else + s << "// fall through to handle extra keyword signals and properties\n"; + s << outdent << "}\n" + << outdent << "}\n"; } QString CppGenerator::argumentNameFromIndex(const ApiExtractorResult &api, - const AbstractMetaFunctionCPtr &func, int argIndex, - const AbstractMetaClass **wrappedClass, - QString *errorMessage) -{ - if (errorMessage != nullptr) - errorMessage->clear(); - *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(api.classes(), returnType.typeEntry()); - if (*wrappedClass == nullptr && errorMessage != nullptr) - *errorMessage = msgClassNotFound(returnType.typeEntry()); - } else { - if (errorMessage != nullptr) { - QTextStream str(errorMessage); - str << "Invalid Argument index (0, return value) on function modification: " - << funcType.name() << ' '; - if (const AbstractMetaClass *declaringClass = func->declaringClass()) - str << declaringClass->name() << "::"; - str << func->name() << "()"; - } - } + const AbstractMetaFunctionCPtr &func, int argIndex) +{ + switch (argIndex) { + case -1: + return PYTHON_SELF_VAR; + case 0: + return PYTHON_RETURN_VAR; + case 1: { // Single argument? + OverloadData data(getFunctionGroups(func->implementingClass()).value(func->name()), api); + if (!data.pythonFunctionWrapperUsesListOfArguments()) + return PYTHON_ARG; + break; + } + } + return pythonArgsAt(argIndex - 1); +} + +AbstractMetaClassCPtr +CppGenerator::argumentClassFromIndex(const ApiExtractorResult &api, + const AbstractMetaFunctionCPtr &func, int argIndex) +{ + if (argIndex == -1) + return func->implementingClass(); + + AbstractMetaType type; + if (argIndex == 0) { + type = func->type(); } else { - int realIndex = argIndex - 1 - OverloadData::numberOfRemovedArguments(func, argIndex - 1); - AbstractMetaType argType = getTypeWithoutContainer(func->arguments().at(realIndex).type()); - *wrappedClass = AbstractMetaClass::findClass(api.classes(), argType.typeEntry()); - if (*wrappedClass == nullptr && errorMessage != nullptr) - *errorMessage = msgClassNotFound(argType.typeEntry()); - if (argIndex == 1 - && !func->isConstructor() - && OverloadData::isSingleArgument(getFunctionGroups(func->implementingClass()).value(func->name()))) - pyArgName = QLatin1String(PYTHON_ARG); - else - pyArgName = pythonArgsAt(argIndex - 1); + const int arg = argIndex - 1; + const int realIndex = arg - OverloadData::numberOfRemovedArguments(func, arg); + type = func->arguments().at(realIndex).type(); } - return pyArgName; + + if (type.typeEntry()->isContainer()) { + // only support containers with 1 type + if (type.instantiations().size() == 1) + type = type.instantiations().constFirst(); + } + + auto te = type.typeEntry(); + if (type.isVoid() || !te->isComplex()) + throw Exception(msgInvalidArgumentModification(func, argIndex)); + const auto result = AbstractMetaClass::findClass(api.classes(), te); + if (!result) + throw Exception(msgClassNotFound(te)); + return result; } +const char tryBlock[] = R"( +PyObject *errorType{}; +PyObject *errorString{}; +try { +)"; + const char defaultExceptionHandling[] = R"(} catch (const std::exception &e) { - PyErr_SetString(PyExc_RuntimeError, e.what()); + errorType = PyExc_RuntimeError; + errorString = Shiboken::String::fromCString(e.what()); } catch (...) { - PyErr_SetString(PyExc_RuntimeError, "An unknown exception was caught"); + errorType = PyExc_RuntimeError; + errorString = Shiboken::Messages::unknownException(); } )"; +const char propagateException[] = R"( +if (errorType != nullptr) + PyErr_SetObject(errorType, errorString); +)"; + +static QString explicitConversion(const QString &v, const AbstractMetaType &t) +{ + return t.plainType().cppSignature() + u'(' + v + u')'; +} + void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr &func, const GeneratorContext &context, bool usesPyArgs, - int maxArgs) const + int maxArgs, + const QList<qsizetype> &argumentIndirections, + ErrorReturn errorReturn) const { s << "// " << func->minimalSignature() << (func->isReverseOperator() ? " [reverse operator]": "") << '\n'; if (func->isConstructor()) { @@ -3520,14 +3669,10 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr } if (func->isAbstract()) { - s << "if (Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(self))) {\n"; - { - Indentation indent(s); - s << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '"; - s << func->ownerClass()->name() << '.' << func->name() << "()' not implemented.\");\n"; - s << returnStatement(m_currentErrorCode) << '\n'; - } - s << "}\n"; + s << "if (Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(self))) {\n" + << indent << "Shiboken::Errors::setPureVirtualMethodError(\"" + << func->ownerClass()->name() << '.' << func->name() << "\");\n" + << errorReturn << outdent << "}\n"; } // Used to provide contextual information to custom code writer function. @@ -3543,8 +3688,7 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr 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)) + if (func->arguments().at(i).isModifiedRemoved()) removedArgs++; } } else if (maxArgs != 0 && !func->arguments().isEmpty()) { @@ -3557,15 +3701,17 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr writeConversionRule(s, func, TypeSystem::NativeCode, usesPyArgs); + bool generateExceptionHandling = false; + if (!func->isUserAdded()) { QStringList userArgs; if (func->functionType() != AbstractMetaFunction::CopyConstructorFunction) { int removedArgs = 0; for (int i = 0; i < maxArgs + removedArgs; i++) { const AbstractMetaArgument &arg = func->arguments().at(i); - bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode, - arg.argumentIndex() + 1).isEmpty(); - if (func->argumentRemoved(i + 1)) { + const bool hasConversionRule = + func->hasConversionRule(TypeSystem::NativeCode, arg.argumentIndex() + 1); + if (arg.isModifiedRemoved()) { // If some argument with default value is removed from a // method signature, the said value must be explicitly // added to the method call. @@ -3573,22 +3719,26 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr // If have conversion rules I will use this for removed args if (hasConversionRule) - userArgs << arg.name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX); + userArgs << arg.name() + CONV_RULE_OUT_VAR_SUFFIX; else if (!arg.defaultValueExpression().isEmpty()) - userArgs.append(QLatin1String(CPP_ARG_REMOVED) + QString::number(i)); + userArgs.append(CPP_ARG_REMOVED(i)); } else { if (hasConversionRule) { - userArgs.append(arg.name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX)); + userArgs.append(arg.name() + CONV_RULE_OUT_VAR_SUFFIX); } else { const int idx = arg.argumentIndex() - removedArgs; - const bool deRef = arg.type().shouldDereferenceArgument(); - QString argName; - if (deRef) - argName += QLatin1Char('*'); - argName += QLatin1String(CPP_ARG) + QString::number(idx); + const auto deRef = argumentIndirections.at(i); + QString argName = AbstractMetaType::dereferencePrefix(deRef) + + CPP_ARG_N(idx); userArgs.append(argName); } } + // "Pass unique ptr by value" pattern: Apply std::move() + auto type = arg.type(); + if (type.useStdMove()) + userArgs.last() = stdMove(userArgs.constLast()); + else if (type.viewOn() != nullptr) + userArgs.last() = explicitConversion(userArgs.constLast(), type); } // If any argument's default value was modified the method must be called @@ -3598,19 +3748,19 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr QStringList otherArgs; bool otherArgsModified = false; bool argsClear = true; - for (int i = func->arguments().size() - 1; i >= maxArgs + removedArgs; i--) { + for (auto 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(); + const bool hasConversionRule = + func->hasConversionRule(TypeSystem::NativeCode, arg.argumentIndex() + 1); if (argsClear && !defValModified && !hasConversionRule) continue; argsClear = false; - otherArgsModified |= defValModified || hasConversionRule || func->argumentRemoved(i + 1); + otherArgsModified |= defValModified || hasConversionRule || arg.isModifiedRemoved(); if (hasConversionRule) - otherArgs.prepend(arg.name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX)); + otherArgs.prepend(arg.name() + CONV_RULE_OUT_VAR_SUFFIX); else - otherArgs.prepend(QLatin1String(CPP_ARG_REMOVED) + QString::number(i)); + otherArgs.prepend(CPP_ARG_REMOVED(i)); } if (otherArgsModified) userArgs << otherArgs; @@ -3621,16 +3771,14 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr StringStream uva(TextStream::Language::Cpp); if (func->isOperatorOverload() && !func->isCallOperator()) { - QString firstArg(QLatin1Char('(')); + QString firstArg(u'('); if (!func->isPointerOperator()) // no de-reference operator - firstArg += QLatin1Char('*'); - firstArg += QLatin1String(CPP_SELF_VAR); - firstArg += QLatin1Char(')'); - QString secondArg = QLatin1String(CPP_ARG0); - if (!func->isUnaryOperator() - && func->arguments().constFirst().type().shouldDereferenceArgument()) { - AbstractMetaType::dereference(&secondArg); - } + firstArg += u'*'; + firstArg += CPP_SELF_VAR; + firstArg += u')'; + QString secondArg = CPP_ARG0; + if (!func->isUnaryOperator()) + AbstractMetaType::applyDereference(&secondArg, argumentIndirections.at(0)); if (func->isUnaryOperator()) std::swap(firstArg, secondArg); @@ -3643,7 +3791,7 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr std::swap(firstArg, secondArg); // Emulate operator+=/-= (__iadd__, __isub__) by using ++/-- - if (((op == QLatin1String("++")) || (op == QLatin1String("--"))) && !func->isReverseOperator()) { + if (((op == u"++") || (op == u"--")) && !func->isReverseOperator()) { s << "\nfor (int i = 0; i < " << secondArg << "; ++i, " << op << firstArg << ");\n"; mc << firstArg; @@ -3658,31 +3806,23 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr 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 << ')'; + if (func->functionType() == AbstractMetaFunction::CopyConstructorFunction + && maxArgs == 1) { + mc << "new " << globalScopePrefix(context) << context.effectiveClassName() + << "(*" << CPP_ARG0 << ')'; } else { - QString ctorCall = className + QLatin1Char('(') + userArgs.join(QLatin1String(", ")) + QLatin1Char(')'); - if (usePySideExtensions() && owner->isQObject()) { + const QString ctorCall = context.effectiveClassName() + u'(' + + userArgs.join(u", "_s) + u')'; + if (usePySideExtensions() && isQObject(owner)) { s << "void *addr = PySide::nextQObjectMemoryAddr();\n"; - uva << "if (addr) {\n"; - { - Indentation indent(uva); - - uva << "cptr = new (addr) ::" << ctorCall << ";\n" - << "PySide::setNextQObjectMemoryAddr(0);" - << '\n'; - } - uva << "} else {\n"; - { - Indentation indent(uva); - uva << "cptr = new ::" << ctorCall << ";\n"; - } - uva << "}\n"; + uva << "if (addr != nullptr) {\n" << indent + << "cptr = new (addr) " << globalScopePrefix(context) << ctorCall + << ";\nPySide::setNextQObjectMemoryAddr(nullptr);\n" << outdent + << "} else {\n" << indent + << "cptr = new " << globalScopePrefix(context) << ctorCall << ";\n" + << outdent << "}\n"; } else { - mc << "new ::" << ctorCall; + mc << "new " << globalScopePrefix(context) << ctorCall; } } } else { @@ -3696,21 +3836,22 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr const bool hasWrapper = shouldGenerateCppWrapper(ownerClass); if (!avoidProtectedHack() || !func->isProtected() || !hasWrapper) { if (func->isStatic()) { - mc << "::" << methodCallClassName << "::"; + mc << m_gsp << methodCallClassName << "::"; } else { + const QString cppSelfVar = CPP_SELF_VAR; const QString selfVarCast = func->ownerClass() == func->implementingClass() - ? QLatin1String(CPP_SELF_VAR) - : QLatin1String("reinterpret_cast<") + methodCallClassName - + QLatin1String(" *>(") + QLatin1String(CPP_SELF_VAR) + QLatin1Char(')'); + ? cppSelfVar + : u"reinterpret_cast<"_s + methodCallClassName + + u" *>("_s + cppSelfVar + u')'; if (func->isConstant()) { if (avoidProtectedHack()) { - mc << "const_cast<const ::"; + mc << "const_cast<const " << globalScopePrefix(context); if (ownerClass->cppWrapper().testFlag(AbstractMetaClass::CppProtectedHackWrapper)) { // PYSIDE-500: Need a special wrapper cast when inherited const QString selfWrapCast = ownerClass == func->implementingClass() - ? QLatin1String(CPP_SELF_VAR) - : QLatin1String("reinterpret_cast<") + wrapperName(ownerClass) - + QLatin1String(" *>(") + QLatin1String(CPP_SELF_VAR) + QLatin1Char(')'); + ? cppSelfVar + : u"reinterpret_cast<"_s + wrapperName(ownerClass) + + u" *>("_s + cppSelfVar + u')'; mc << wrapperName(ownerClass); mc << " *>(" << selfWrapCast << ")->"; } @@ -3719,7 +3860,7 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr mc << " *>(" << selfVarCast << ")->"; } } else { - mc << "const_cast<const ::" << methodCallClassName; + mc << "const_cast<const " << m_gsp << methodCallClassName; mc << " *>(" << selfVarCast << ")->"; } } else { @@ -3735,26 +3876,26 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr if (!func->isStatic()) { const bool directInheritance = context.metaClass() == ownerClass; mc << (directInheritance ? "static_cast" : "reinterpret_cast") - << "<::" << wrapperName(ownerClass) << " *>(" << CPP_SELF_VAR << ")->"; + << '<' << wrapperName(ownerClass) << " *>(" + << CPP_SELF_VAR << ")->"; } if (!func->isAbstract()) mc << (func->isProtected() ? wrapperName(func->ownerClass()) : - QLatin1String("::") - + methodCallClassName) << "::"; + m_gsp + methodCallClassName) << "::"; mc << func->originalName() << "_protected"; } } else { mc << func->originalName(); } - mc << '(' << userArgs.join(QLatin1String(", ")) << ')'; + mc << '(' << userArgs.join(u", "_s) << ')'; if (!func->isAbstract() && func->isVirtual()) { if (!avoidProtectedHack() || !func->isProtected()) { QString virtualCall = mc; QString normalCall = virtualCall; - virtualCall.replace(QLatin1String("%CLASS_NAME"), + virtualCall.replace(u"%CLASS_NAME"_s, methodCallClassName); - normalCall.remove(QLatin1String("::%CLASS_NAME::")); + normalCall.remove(u"::%CLASS_NAME::"_s); mc.clear(); mc << "Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(self))\n" << " ? " << virtualCall << '\n' @@ -3766,9 +3907,9 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr if (!injectedCodeCallsCppFunction(context, func)) { const bool allowThread = func->allowThread(); - const bool generateExceptionHandling = func->generateExceptionHandling(); + generateExceptionHandling = func->generateExceptionHandling(); if (generateExceptionHandling) { - s << "try {\n" << indent; + s << tryBlock << indent; if (allowThread) { s << "Shiboken::ThreadStateSaver threadSaver;\n" << "threadSaver.save();\n"; @@ -3788,13 +3929,13 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr if (metaEnum.has_value()) { QString enumName; if (metaEnum->isProtected()) { - enumName = context.wrapperName() + QLatin1String("::") + enumName = context.wrapperName() + u"::"_s + metaEnum.value().name(); } else { enumName = func->type().cppSignature(); } - const QString methodCall = enumName + QLatin1Char('(') - + mc.toString() + QLatin1Char(')'); + const QString methodCall = enumName + u'(' + + mc.toString() + u')'; mc.clear(); mc << methodCall; s << enumName; @@ -3806,9 +3947,9 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr s << func->type().cppSignature(); if (func->type().isObjectTypeUsedAsValueType()) { s << '*'; - methodCall = QLatin1String("new ") + methodCall = u"new "_s + func->type().typeEntry()->qualifiedCppName() - + QLatin1Char('(') + mc.toString() + QLatin1Char(')'); + + u'(' + mc.toString() + u')'; } } s << " " << CPP_RETURN_VAR << " = " << methodCall << ";\n"; @@ -3818,21 +3959,29 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr if (allowThread) { s << (generateExceptionHandling - ? "threadSaver.restore();" : END_ALLOW_THREADS) << '\n'; + ? u"threadSaver.restore();"_s : END_ALLOW_THREADS) << '\n'; } // Convert result - if (!func->conversionRule(TypeSystem::TargetLangCode, 0).isEmpty()) { - writeConversionRule(s, func, TypeSystem::TargetLangCode, QLatin1String(PYTHON_RETURN_VAR)); + const auto funcType = func->type(); + if (func->hasConversionRule(TypeSystem::TargetLangCode, 0)) { + writeConversionRule(s, func, TypeSystem::TargetLangCode, + PYTHON_RETURN_VAR); } else if (!isCtor && !func->isInplaceOperator() && !func->isVoid() && !func->injectedCodeHasReturnValueAttribution(TypeSystem::TargetLangCode)) { - s << PYTHON_RETURN_VAR << " = "; if (func->type().isObjectTypeUsedAsValueType()) { - s << "Shiboken::Object::newObject(" + s << PYTHON_RETURN_VAR << " = Shiboken::Object::newObject(" << cpythonTypeNameExt(func->type().typeEntry()) << ", " << CPP_RETURN_VAR << ", true, true)"; + } else if (func->generateOpaqueContainerReturn()) { + const QString creationFunc = opaqueContainerCreationFunc(funcType); + writeOpaqueContainerCreationFuncDecl(s, creationFunc, funcType); + s << PYTHON_RETURN_VAR << " = " << creationFunc + << "(&" << CPP_RETURN_VAR << ");\n"; } else { - writeToPythonConversion(s, func->type(), func->ownerClass(), QLatin1String(CPP_RETURN_VAR)); + s << PYTHON_RETURN_VAR << " = "; + writeToPythonConversion(s, funcType, func->ownerClass(), + CPP_RETURN_VAR); } s << ";\n"; } @@ -3840,8 +3989,8 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr if (generateExceptionHandling) { // "catch" code s << outdent << defaultExceptionHandling; } - } - } + } // !injected code calls C++ function + } // !userAdded if (func->hasInjectedCode() && !func->isConstructor()) writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, @@ -3868,25 +4017,11 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr if (!ownership_mods.isEmpty()) { s << '\n' << "// Ownership transferences.\n"; - for (const ArgumentModification &arg_mod : qAsConst(ownership_mods)) { - const AbstractMetaClass *wrappedClass = nullptr; - QString errorMessage; - QString pyArgName = argumentNameFromIndex(api(), func, arg_mod.index(), - &wrappedClass, &errorMessage); - if (!wrappedClass) { - QString message; - QTextStream str(&message); - str << "Invalid ownership modification for argument " << arg_mod.index() - << " (" << pyArgName << ") of "; - if (const AbstractMetaClass *declaringClass = func->declaringClass()) - str << declaringClass->name() << "::"; - str << func->name() << "(): " << errorMessage; - qCWarning(lcShiboken, "%s", qPrintable(message)); - s << "#error " << message << '\n'; - break; - } + for (const ArgumentModification &arg_mod : std::as_const(ownership_mods)) { + const int argIndex = arg_mod.index(); + const QString pyArgName = argumentNameFromIndex(api(), func, argIndex); - if (arg_mod.index() == 0 || arg_mod.owner().index == 0) + if (argIndex == 0 || arg_mod.owner().index == 0) hasReturnPolicy = true; // The default ownership does nothing. This is useful to avoid automatic heuristically @@ -3898,11 +4033,9 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr s << "Shiboken::Object::"; if (ownership == TypeSystem::TargetLangOwnership) { s << "getOwnership(" << pyArgName << ");"; - } else if (wrappedClass->hasVirtualDestructor()) { - if (arg_mod.index() == 0) - s << "releaseOwnership(" << PYTHON_RETURN_VAR << ");"; - else - s << "releaseOwnership(" << pyArgName << ");"; + } else if (auto ac = argumentClassFromIndex(api(), func, argIndex); + ac && ac->hasVirtualDestructor()) { + s << "releaseOwnership(" << pyArgName << ");"; } else { s << "invalidate(" << pyArgName << ");"; } @@ -3910,7 +4043,7 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr } } else if (!refcount_mods.isEmpty()) { - for (const ArgumentModification &arg_mod : qAsConst(refcount_mods)) { + for (const ArgumentModification &arg_mod : std::as_const(refcount_mods)) { ReferenceCount refCount = arg_mod.referenceCounts().constFirst(); if (refCount.action != ReferenceCount::Set && refCount.action != ReferenceCount::Remove @@ -3918,28 +4051,9 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr 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 { - QString errorMessage; - pyArgName = argumentNameFromIndex(api(), func, arg_mod.index(), - &wrappedClass, &errorMessage); - if (pyArgName.isEmpty()) { - QString message; - QTextStream str(&message); - str << "Invalid reference count modification for argument " - << arg_mod.index() << " of "; - if (const AbstractMetaClass *declaringClass = func->declaringClass()) - str << declaringClass->name() << "::"; - str << func->name() << "(): " << errorMessage; - qCWarning(lcShiboken, "%s", qPrintable(message)); - s << "#error " << message << "\n\n"; - break; - } - } + const int argIndex = arg_mod.index(); + const QString pyArgName = refCount.action == ReferenceCount::Remove + ? u"Py_None"_s : argumentNameFromIndex(api(), func, argIndex); if (refCount.action == ReferenceCount::Add || refCount.action == ReferenceCount::Set) s << "Shiboken::Object::keepReference("; @@ -3949,25 +4063,28 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr s << "reinterpret_cast<SbkObject *>(self), \""; QString varName = arg_mod.referenceCounts().constFirst().varName; if (varName.isEmpty()) - varName = func->minimalSignature() + QString::number(arg_mod.index()); + varName = func->minimalSignature() + QString::number(argIndex); s << varName << "\", " << pyArgName << (refCount.action == ReferenceCount::Add ? ", true" : "") << ");\n"; - if (arg_mod.index() == 0) + if (argIndex == 0) hasReturnPolicy = true; } } writeParentChildManagement(s, func, usesPyArgs, !hasReturnPolicy); + + if (generateExceptionHandling) + s << propagateException; } -QStringList CppGenerator::getAncestorMultipleInheritance(const AbstractMetaClass *metaClass) +QStringList CppGenerator::getAncestorMultipleInheritance(const AbstractMetaClassCPtr &metaClass) { QStringList result; - const AbstractMetaClassList &baseClases = metaClass->typeSystemBaseClasses(); + const auto &baseClases = metaClass->typeSystemBaseClasses(); if (!baseClases.isEmpty()) { - for (const AbstractMetaClass *baseClass : baseClases) { + for (const auto &baseClass : baseClases) { QString offset; QTextStream(&offset) << "reinterpret_cast<uintptr_t>(static_cast<const " << baseClass->qualifiedCppName() << " *>(class_ptr)) - base"; @@ -3980,246 +4097,203 @@ QStringList CppGenerator::getAncestorMultipleInheritance(const AbstractMetaClass result.append(offset); } - for (const AbstractMetaClass *baseClass : baseClases) + for (const auto &baseClass : baseClases) result.append(getAncestorMultipleInheritance(baseClass)); } return result; } -void CppGenerator::writeMultipleInheritanceInitializerFunction(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeMultipleInheritanceInitializerFunction(TextStream &s, + const AbstractMetaClassCPtr &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" - << "int *\n" + s << "int *\n" << multipleInheritanceInitializerFunctionName(metaClass) << "(const void *cptr)\n" - << "{\n" << indent - << "if (mi_offsets[0] == -1) {\n"; - { - Indentation indent(s); - s << "std::set<int> offsets;\n" - << "const auto *class_ptr = reinterpret_cast<const " << className << " *>(cptr);\n" - << "const auto base = reinterpret_cast<uintptr_t>(class_ptr);\n"; - - for (const QString &ancestor : ancestors) - s << "offsets.insert(int(" << ancestor << "));\n"; - - s << "\noffsets.erase(0);\n\n" - << "std::copy(offsets.cbegin(), offsets.cend(), mi_offsets);\n"; - } - s << "}\nreturn mi_offsets;\n" << outdent << "}\n"; -} - -void CppGenerator::writeSpecialCastFunction(TextStream &s, const AbstractMetaClass *metaClass) + << "{\n" << indent; + s << "static int mi_offsets[] = {-2"; + for (qsizetype i = 0; i < ancestors.size(); i++) + s << ", 0"; + s << "};\n" + << "if (mi_offsets[0] == -2) {\n" << indent + << "const auto *class_ptr = reinterpret_cast<const " << className << " *>(cptr);\n" + << "const auto base = reinterpret_cast<uintptr_t>(class_ptr);\n" + << "int *p = mi_offsets;\n"; + + for (const QString &ancestor : ancestors) + s << "*p++ = int(" << ancestor << ");\n"; + s << "std::sort(mi_offsets, p);\n" + << "auto *end = std::unique(mi_offsets, p);\n" + << "*end++ = -1;\n" + << "if (mi_offsets[0] == 0)\n" + << indent + << "std::memmove(&mi_offsets[0], &mi_offsets[1], (end - mi_offsets - 1) * sizeof(int));\n" + << outdent << outdent + << "}\nreturn mi_offsets;\n" << outdent << "}\n"; +} + +void CppGenerator::writeSpecialCastFunction(TextStream &s, const AbstractMetaClassCPtr &metaClass) { QString className = metaClass->qualifiedCppName(); s << "static void * " << cpythonSpecialCastFunctionName(metaClass) << "(void *obj, PyTypeObject *desiredType)\n{\n" << indent - << "auto me = reinterpret_cast< ::" << className << " *>(obj);\n"; + << "auto me = reinterpret_cast< " << m_gsp << className << " *>(obj);\n"; bool firstClass = true; - const AbstractMetaClassList &allAncestors = metaClass->allTypeSystemAncestors(); - for (const AbstractMetaClass *baseClass : allAncestors) { + const auto &allAncestors = metaClass->allTypeSystemAncestors(); + for (const auto &baseClass : allAncestors) { if (!firstClass) s << "else "; - s << "if (desiredType == " << cpythonTypeNameExt(baseClass->typeEntry()) << ")\n"; - Indentation indent(s); - s << "return static_cast< ::" << baseClass->qualifiedCppName() << " *>(me);\n"; + s << "if (desiredType == " << cpythonTypeNameExt(baseClass->typeEntry()) + << ")\n" << indent + << "return static_cast< " << getFullTypeName(baseClass) << " *>(me);\n" + << outdent; firstClass = false; } s << "return me;\n" << outdent << "}\n\n"; } void CppGenerator::writePrimitiveConverterInitialization(TextStream &s, - const CustomConversion *customConversion) + const CustomConversionPtr &customConversion) { - const TypeEntry *type = customConversion->ownerType(); + TypeEntryCPtr type = customConversion->ownerType(); QString converter = converterObject(type); s << "// Register converter for type '" << type->qualifiedTargetLangName() << "'.\n" << converter << " = Shiboken::Conversions::createConverter("; - if (type->targetLangApiName() == type->name()) - s << '0'; - else if (type->targetLangApiName() == cPyObjectT()) + if (!type->hasTargetLangApiType()) + s << "nullptr"; + else if (type->targetLangApiName() == cPyObjectT) s << "&PyBaseObject_Type"; else s << '&' << type->targetLangApiName() << "_Type"; QString typeName = fixedCppTypeName(type); s << ", " << cppToPythonFunctionName(typeName, typeName) << ");\n" - << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" - << type->qualifiedCppName() << "\");\n"; + << registerConverterName(type->qualifiedCppName(), converter); writeCustomConverterRegister(s, customConversion, converter); } -void CppGenerator::writeEnumConverterInitialization(TextStream &s, const AbstractMetaEnum &metaEnum) +static void registerConverterInScopes(TextStream &s, QStringView signature, + QAnyStringView varName = converterVar) { - if (metaEnum.isPrivate() || metaEnum.isAnonymous()) - return; - writeEnumConverterInitialization(s, metaEnum.typeEntry()); + while (true) { + s << registerConverterName(signature, varName); + const auto qualifierPos = signature.indexOf("::"_L1); + if (qualifierPos == -1) + break; + signature = signature.sliced(qualifierPos + 2); + } } -void CppGenerator::writeEnumConverterInitialization(TextStream &s, const TypeEntry *enumType) +void CppGenerator::writeEnumConverterInitialization(TextStream &s, const AbstractMetaEnum &metaEnum) { - if (!enumType) + if (metaEnum.isPrivate() || metaEnum.isAnonymous()) 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); + EnumTypeEntryCPtr enumType = metaEnum.typeEntry(); + Q_ASSERT(enumType); - s << "// Register converter for " << enumFlagName << " '" << enumType->qualifiedCppName() - << "'.\n{\n"; - { - Indentation indent(s); - QString typeName = fixedCppTypeName(enumType); - s << "SbkConverter *converter = Shiboken::Conversions::createConverter(" - << enumPythonType << ',' << '\n'; - { - Indentation indent(s); - s << 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); - } + static const char enumPythonVar[] = "EType"; - QString toCpp = pythonToCppFunctionName(typeName, typeName); - QString isConv = convertibleToCppFunctionName(typeName, typeName); - writeAddPythonToCppConversion(s, QLatin1String("converter"), toCpp, isConv); + s << "// Register converter for enum '" << enumType->qualifiedCppName() + << "'.\n{\n" << indent; - if (flags) { - QString toCpp = pythonToCppFunctionName(QLatin1String("number"), typeName); - QString isConv = convertibleToCppFunctionName(QLatin1String("number"), typeName); - writeAddPythonToCppConversion(s, QLatin1String("converter"), toCpp, isConv); - } + const QString typeName = fixedCppTypeName(enumType); + s << "SbkConverter *converter = Shiboken::Conversions::createConverter(" + << enumPythonVar << ',' << '\n' << indent + << cppToPythonFunctionName(typeName, typeName) << ");\n" << outdent; - s << "Shiboken::Enum::setTypeConverter(" << enumPythonType - << ", converter, " << (enumType->isFlags() ? "true" : "false") << ");\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(); - } - } + const QString toCpp = pythonToCppFunctionName(typeName, typeName); + const QString isConv = convertibleToCppFunctionName(typeName, typeName); + writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv); + s << "Shiboken::Enum::setTypeConverter(" << enumPythonVar + << ", converter);\n"; - while (true) { - s << "Shiboken::Conversions::registerConverterName(converter, \"" - << signature << "\");\n"; - const int qualifierPos = signature.indexOf(QLatin1String("::")); - if (qualifierPos != -1) - signature.remove(0, qualifierPos + 2); - else - break; - } - } - s << "}\n"; + registerConverterInScopes(s, enumType->qualifiedCppName()); + if (auto flags = enumType->flags()) + s << "// Register converter for flag '" << flags->qualifiedCppName() << "'.\n" + << registerConverterName(flags->name()) // QMetaType + << registerConverterName(flags->originalName()); // Signals with flags - if (!flags) - writeEnumConverterInitialization(s, static_cast<const EnumTypeEntry *>(enumType)->flags()); + s << outdent << "}\n"; } -void CppGenerator::writeContainerConverterInitialization(TextStream &s, const AbstractMetaType &type) const +QString CppGenerator::writeContainerConverterInitialization(TextStream &s, + const AbstractMetaType &type, + const ApiExtractorResult &api) { - QByteArray cppSignature = QMetaObject::normalizedSignature(type.cppSignature().toUtf8()); + const auto cppSignature = + QString::fromUtf8(QMetaObject::normalizedSignature(type.cppSignature().toUtf8())); s << "// Register converter for type '" << cppSignature << "'.\n"; - QString converter = converterObject(type); + const QString converter = converterObject(type); s << converter << " = Shiboken::Conversions::createConverter("; - if (type.typeEntry()->targetLangApiName() == cPyObjectT()) { + + Q_ASSERT(type.typeEntry()->isContainer()); + const auto typeEntry = std::static_pointer_cast<const ContainerTypeEntry>(type.typeEntry()); + + const QString targetTypeName = containerNativeToTargetTypeName(typeEntry); + if (targetTypeName == cPyObjectT) { s << "&PyBaseObject_Type"; } else { - QString baseName = cpythonBaseName(type.typeEntry()); - if (baseName == cPySequenceT()) - baseName = cPyListT(); - s << '&' << baseName << "_Type"; + s << '&' << targetTypeName << "_Type"; } - QString typeName = fixedCppTypeName(type); - s << ", " << cppToPythonFunctionName(typeName, typeName) << ");\n"; - QString toCpp = pythonToCppFunctionName(typeName, typeName); - QString isConv = convertibleToCppFunctionName(typeName, typeName); - s << "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 << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << cppSignature << "\");\n"; - } - writeAddPythonToCppConversion(s, converterObject(type), toCpp, isConv); -} - -void CppGenerator::writeSmartPointerConverterInitialization(TextStream &s, const AbstractMetaType &type) const -{ - const QByteArray cppSignature = type.cppSignature().toUtf8(); - auto writeConversionRegister = [&s](const AbstractMetaType &sourceType, const QString &targetTypeName, const QString &targetConverter) - { - const QString sourceTypeName = fixedCppTypeName(sourceType); - const QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); - const QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName); - writeAddPythonToCppConversion(s, targetConverter, toCpp, isConv); - }; + const QString typeName = fixedCppTypeName(type); + s << ", " << cppToPythonFunctionName(typeName, targetTypeName) << ");\n"; - auto klass = AbstractMetaClass::findClass(api().classes(), type.instantiations().at(0).typeEntry()); - if (!klass) - return; + s << registerConverterName(cppSignature, converter); + if (usePySideExtensions() && cppSignature.startsWith("const "_L1) + && cppSignature.endsWith(u'&')) { + auto underlyingType = QStringView{cppSignature}.sliced(6, cppSignature.size() - 7); + s << registerConverterName(underlyingType, converter); + } - const auto classes = klass->typeSystemBaseClasses(); - if (classes.isEmpty()) - return; + for (const auto &conv : typeEntry->customConversion()->targetToNativeConversions()) { + const QString &sourceTypeName = conv.sourceTypeName(); + QString toCpp = pythonToCppFunctionName(sourceTypeName, typeName); + QString isConv = convertibleToCppFunctionName(sourceTypeName, typeName); + writeAddPythonToCppConversion(s, converter, toCpp, isConv); + } - s << "// Register SmartPointer converter for type '" << cppSignature << "'." << '\n' - << "///////////////////////////////////////////////////////////////////////////////////////\n\n"; - - for (auto k : classes) { - auto smartTargetType = findSmartPointerInstantiation(k->typeEntry()); - if (smartTargetType.has_value()) { - s << "// Convert to SmartPointer derived class: [" - << smartTargetType->cppSignature() << "]\n"; - const QString converter = u"Shiboken::Conversions::getConverter(\""_qs - + smartTargetType->cppSignature() + u"\")"_qs; - writeConversionRegister(type, fixedCppTypeName(smartTargetType.value()), converter); - } else { - s << "// Class not found:" << type.instantiations().at(0).cppSignature(); + auto typedefItPair = api.typedefTargetToName().equal_range(type.cppSignature()); + if (typedefItPair.first != typedefItPair.second) { + auto *typeDb = TypeDatabase::instance(); + s << "// Register converters for type aliases of " << cppSignature << "'.\n"; + for (auto it = typedefItPair.first; it != typedefItPair.second; ++it) { + if (!typeDb->findType(it.value())) + s << registerConverterName(it.value(), converter); } } - s << "///////////////////////////////////////////////////////////////////////////////////////" << '\n' << '\n'; + return converter; } -void CppGenerator::writeExtendedConverterInitialization(TextStream &s, const TypeEntry *externalType, +QString CppGenerator::typeInitStruct(const TypeEntryCPtr &te) +{ + return cppApiVariableName(te->targetLangPackage()) + u'[' + + getTypeIndexVariableName(te) + u']'; +} + +void CppGenerator::writeExtendedConverterInitialization(TextStream &s, + const TypeEntryCPtr &externalType, const AbstractMetaClassCList &conversions) { s << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName() << ".\n"; - for (const AbstractMetaClass *sourceClass : conversions) { - const QString converterVar = cppApiVariableName(externalType->targetLangPackage()) + QLatin1Char('[') - + getTypeIndexVariableName(externalType) + u']'; + for (const auto &sourceClass : conversions) { QString sourceTypeName = fixedCppTypeName(sourceClass->typeEntry()); QString targetTypeName = fixedCppTypeName(externalType); QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName); - writeAddPythonToCppConversion(s, converterVar, toCpp, isConv); + if (!externalType->isPrimitive()) + s << cpythonTypeNameExt(externalType) << ";\n"; + writeAddPythonToCppConversion(s, typeInitStruct(externalType), toCpp, isConv); } } -QString CppGenerator::multipleInheritanceInitializerFunctionName(const AbstractMetaClass *metaClass) +QString CppGenerator::multipleInheritanceInitializerFunctionName(const AbstractMetaClassCPtr &metaClass) { - return cpythonBaseName(metaClass->typeEntry()) + QLatin1String("_mi_init"); + return cpythonBaseName(metaClass->typeEntry()) + u"_mi_init"_s; } -bool CppGenerator::supportsMappingProtocol(const AbstractMetaClass *metaClass) +bool CppGenerator::supportsMappingProtocol(const AbstractMetaClassCPtr &metaClass) { for (const auto &m : mappingProtocols()) { if (metaClass->hasFunction(m.name)) @@ -4229,7 +4303,7 @@ bool CppGenerator::supportsMappingProtocol(const AbstractMetaClass *metaClass) return false; } -bool CppGenerator::supportsNumberProtocol(const AbstractMetaClass *metaClass) const +bool CppGenerator::supportsNumberProtocol(const AbstractMetaClassCPtr &metaClass) { return metaClass->hasArithmeticOperatorOverload() || metaClass->hasIncDecrementOperatorOverload() @@ -4238,18 +4312,18 @@ bool CppGenerator::supportsNumberProtocol(const AbstractMetaClass *metaClass) co || hasBoolCast(metaClass); } -bool CppGenerator::supportsSequenceProtocol(const AbstractMetaClass *metaClass) +bool CppGenerator::supportsSequenceProtocol(const AbstractMetaClassCPtr &metaClass) { for (const auto &seq : sequenceProtocols()) { if (metaClass->hasFunction(seq.name)) return true; } - const ComplexTypeEntry *baseType = metaClass->typeEntry()->baseContainerType(); + ComplexTypeEntryCPtr baseType = metaClass->typeEntry()->baseContainerType(); return baseType && baseType->isContainer(); } -bool CppGenerator::shouldGenerateGetSetList(const AbstractMetaClass *metaClass) const +bool CppGenerator::shouldGenerateGetSetList(const AbstractMetaClassCPtr &metaClass) { for (const AbstractMetaField &f : metaClass->fields()) { if (!f.isStatic()) @@ -4267,63 +4341,57 @@ bool CppGenerator::shouldGenerateGetSetList(const AbstractMetaClass *metaClass) struct pyTypeSlotEntry { - explicit pyTypeSlotEntry(const char *name, const QString &function) : + explicit pyTypeSlotEntry(QAnyStringView name, QAnyStringView function) : m_name(name), m_function(function) {} - const char *m_name; - const QString &m_function; + QAnyStringView m_name; + QAnyStringView m_function; }; TextStream &operator<<(TextStream &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"; + if (!e.m_function.isEmpty()) { + str << '{' << e.m_name << ',' << Pad(' ', qMax(0, 18 - e.m_name.size())) + << "reinterpret_cast<void *>(" << e.m_function << ")},\n"; + } return str; } void CppGenerator::writeClassDefinition(TextStream &s, - const AbstractMetaClass *metaClass, + const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext) { - QString tp_flags; QString tp_init; QString tp_new; QString tp_dealloc; QString tp_hash; QString tp_call; const QString className = chopType(cpythonTypeName(metaClass)); - QString baseClassName; AbstractMetaFunctionCList ctors; - const auto &allCtors = metaClass->queryFunctions(FunctionQueryOption::Constructors); + const auto &allCtors = metaClass->queryFunctions(FunctionQueryOption::AnyConstructor); for (const auto &f : allCtors) { - if (!f->isPrivate() && !f->isModifiedRemoved() && !classContext.forSmartPointer()) + if (!f->isPrivate() && !f->isModifiedRemoved() + && f->functionType() != AbstractMetaFunction::MoveConstructorFunction) { ctors.append(f); + } } - if (!metaClass->baseClass()) - baseClassName = QLatin1String("SbkObject_TypeF()"); - bool onlyPrivCtor = !metaClass->hasNonPrivateConstructor(); const bool isQApp = usePySideExtensions() - && metaClass->inheritsFrom(u"QCoreApplication"_qs); + && inheritsFrom(metaClass, u"QCoreApplication"_s); - tp_flags = QLatin1String("Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE"); + QString tp_flags = u"Py_TPFLAGS_DEFAULT"_s; + if (!metaClass->attributes().testFlag(AbstractMetaClass::FinalCppClass)) + tp_flags += u"|Py_TPFLAGS_BASETYPE"_s; if (metaClass->isNamespace() || metaClass->hasPrivateDestructor()) { tp_dealloc = metaClass->hasPrivateDestructor() ? - QLatin1String("SbkDeallocWrapperWithPrivateDtor") : - QLatin1String("Sbk_object_dealloc /* PYSIDE-832: Prevent replacement of \"0\" with subtype_dealloc. */"); + u"SbkDeallocWrapperWithPrivateDtor"_s : + u"Sbk_object_dealloc /* PYSIDE-832: Prevent replacement of \"0\" with subtype_dealloc. */"_s; tp_init.clear(); } else { tp_dealloc = isQApp - ? QLatin1String("&SbkDeallocQAppWrapper") : QLatin1String("&SbkDeallocWrapper"); + ? u"&SbkDeallocQAppWrapper"_s : u"&SbkDeallocWrapper"_s; if (!onlyPrivCtor && !ctors.isEmpty()) tp_init = cpythonFunctionName(ctors.constFirst()); } @@ -4335,53 +4403,59 @@ void CppGenerator::writeClassDefinition(TextStream &s, ? cpythonSetattroFunctionName(metaClass) : QString(); if (metaClass->hasPrivateDestructor() || onlyPrivCtor) { - // tp_flags = QLatin1String("Py_TPFLAGS_DEFAULT"); + // tp_flags = u"Py_TPFLAGS_DEFAULT"_s; // This is not generally possible, because PySide does not care about // privacy the same way. This worked before the heap types were used, // because inheritance is not really checked for static types. - // Instead, we check this at runtime, see SbkObjectTypeTpNew. - if (metaClass->fullName().startsWith(QLatin1String("PySide6.Qt"))) { + // Instead, we check this at runtime, see SbkObjectType_tp_new. + if (metaClass->fullName().startsWith(u"PySide6.Qt")) { // PYSIDE-595: No idea how to do non-inheritance correctly. // Since that is only relevant in shiboken, I used a shortcut for // PySide. - tp_new = QLatin1String("SbkObjectTpNew"); + tp_new = u"SbkObject_tp_new"_s; } else { - tp_new = QLatin1String("SbkDummyNew /* PYSIDE-595: Prevent replacement " - "of \"0\" with base->tp_new. */"); + tp_new = u"SbkDummyNew /* PYSIDE-595: Prevent replacement " + "of \"0\" with base->tp_new. */"_s; } } else if (isQApp) { - tp_new = QLatin1String("SbkQAppTpNew"); // PYSIDE-571: need singleton app + tp_new = u"SbkQApp_tp_new"_s; // PYSIDE-571: need singleton app } else { - tp_new = QLatin1String("SbkObjectTpNew"); + tp_new = u"SbkObject_tp_new"_s; } - tp_flags.append(QLatin1String("|Py_TPFLAGS_HAVE_GC")); + tp_flags.append(u"|Py_TPFLAGS_HAVE_GC"_s); QString tp_richcompare; - if (!metaClass->isNamespace() && metaClass->hasComparisonOperatorOverload()) - tp_richcompare = cpythonBaseName(metaClass) + QLatin1String("_richcompare"); + if (generateRichComparison(classContext)) + tp_richcompare = cpythonBaseName(metaClass) + u"_richcompare"_s; + const bool isSmartPointer = classContext.forSmartPointer(); QString tp_getset; - if (shouldGenerateGetSetList(metaClass) && !classContext.forSmartPointer()) + if (shouldGenerateGetSetList(metaClass) && !isSmartPointer) tp_getset = cpythonGettersSettersDefinitionName(metaClass); // search for special functions clearTpFuncs(); for (const auto &func : metaClass->functions()) { - if (m_tpFuncs.contains(func->name())) - m_tpFuncs[func->name()] = cpythonFunctionName(func); + // Special non-operator functions identified by name + auto it = m_tpFuncs.find(func->name()); + if (it != m_tpFuncs.end()) + it.value() = cpythonFunctionName(func); + else if ( it = m_nbFuncs.find(func->name()); it != m_nbFuncs.end() ) + it.value() = cpythonFunctionName(func); } - if (m_tpFuncs.value(reprFunction()).isEmpty() - && metaClass->hasToStringCapability()) { - m_tpFuncs[reprFunction()] = writeReprFunction(s, - classContext, - metaClass->toStringCapabilityIndirections()); + if (m_tpFuncs.value(REPR_FUNCTION).isEmpty() + && (isSmartPointer || metaClass->hasToStringCapability())) { + const QString name = isSmartPointer + ? writeSmartPointerReprFunction(s, classContext) + : writeReprFunction(s, classContext, metaClass->toStringCapabilityIndirections()); + m_tpFuncs[REPR_FUNCTION] = name; } // class or some ancestor has multiple inheritance - const AbstractMetaClass *miClass = getMultipleInheritingClass(metaClass); + const auto miClass = getMultipleInheritingClass(metaClass); if (miClass) { if (metaClass == miClass) writeMultipleInheritanceInitializerFunction(s, metaClass); @@ -4392,39 +4466,33 @@ void CppGenerator::writeClassDefinition(TextStream &s, s << "// Class Definition -----------------------------------------------\n" "extern \"C\" {\n"; - if (!metaClass->typeEntry()->hashFunction().isEmpty()) - tp_hash = QLatin1Char('&') + cpythonBaseName(metaClass) + QLatin1String("_HashFunc"); + if (hasHashFunction(metaClass)) + tp_hash = u'&' + cpythonBaseName(metaClass) + u"_HashFunc"_s; - const auto callOp = metaClass->findFunction(QLatin1String("operator()")); - if (!callOp.isNull() && !callOp->isModifiedRemoved()) - tp_call = QLatin1Char('&') + cpythonFunctionName(callOp); + const auto callOp = metaClass->findFunction("operator()"); + if (callOp && !callOp->isModifiedRemoved()) + tp_call = u'&' + cpythonFunctionName(callOp); - QString computedClassTargetFullName; - if (!classContext.forSmartPointer()) - computedClassTargetFullName = getClassTargetFullName(metaClass); - else - computedClassTargetFullName = getClassTargetFullName(classContext.preciseType()); - - const QString typePtr = QLatin1String("_") + className - + QLatin1String("_Type"); + const QString typePtr = u"_"_s + className + + u"_Type"_s; s << "static PyTypeObject *" << typePtr << " = nullptr;\n" << "static PyTypeObject *" << className << "_TypeF(void)\n" << "{\n" << indent << "return " << typePtr << ";\n" << outdent << "}\n\nstatic PyType_Slot " << className << "_slots[] = {\n" << indent << "{Py_tp_base, nullptr}, // inserted by introduceWrapperType\n" << pyTypeSlotEntry("Py_tp_dealloc", tp_dealloc) - << pyTypeSlotEntry("Py_tp_repr", m_tpFuncs.value(reprFunction())) + << pyTypeSlotEntry("Py_tp_repr", m_tpFuncs.value(REPR_FUNCTION)) << pyTypeSlotEntry("Py_tp_hash", tp_hash) << pyTypeSlotEntry("Py_tp_call", tp_call) - << pyTypeSlotEntry("Py_tp_str", m_tpFuncs.value(QLatin1String("__str__"))) + << pyTypeSlotEntry("Py_tp_str", m_tpFuncs.value(u"__str__"_s)) << pyTypeSlotEntry("Py_tp_getattro", tp_getattro) << pyTypeSlotEntry("Py_tp_setattro", tp_setattro) - << pyTypeSlotEntry("Py_tp_traverse", className + QLatin1String("_traverse")) - << pyTypeSlotEntry("Py_tp_clear", className + QLatin1String("_clear")) + << pyTypeSlotEntry("Py_tp_traverse", className + u"_traverse"_s) + << pyTypeSlotEntry("Py_tp_clear", className + u"_clear"_s) << pyTypeSlotEntry("Py_tp_richcompare", tp_richcompare) - << pyTypeSlotEntry("Py_tp_iter", m_tpFuncs.value(QLatin1String("__iter__"))) - << pyTypeSlotEntry("Py_tp_iternext", m_tpFuncs.value(QLatin1String("__next__"))) - << pyTypeSlotEntry("Py_tp_methods", className + QLatin1String("_methods")) + << pyTypeSlotEntry("Py_tp_iter", m_tpFuncs.value(u"__iter__"_s)) + << pyTypeSlotEntry("Py_tp_iternext", m_tpFuncs.value(u"__next__"_s)) + << pyTypeSlotEntry("Py_tp_methods", className + u"_methods"_s) << pyTypeSlotEntry("Py_tp_getset", tp_getset) << pyTypeSlotEntry("Py_tp_init", tp_init) << pyTypeSlotEntry("Py_tp_new", tp_new); @@ -4437,61 +4505,58 @@ void CppGenerator::writeClassDefinition(TextStream &s, writeTypeAsMappingDefinition(s, metaClass); } if (supportsNumberProtocol(metaClass)) { - // This one must come last. See the function itself. s << "// type supports number protocol\n"; writeTypeAsNumberDefinition(s, metaClass); } s << "{0, " << NULL_PTR << "}\n" << outdent << "};\n"; - int packageLevel = packageName().count(QLatin1Char('.')) + 1; + int packageLevel = packageName().count(u'.') + 1; s << "static PyType_Spec " << className << "_spec = {\n" << indent - << '"' << packageLevel << ':' << computedClassTargetFullName << "\",\n" + << '"' << packageLevel << ':' << getClassTargetFullName(metaClass) << "\",\n" << "sizeof(SbkObject),\n0,\n" << tp_flags << ",\n" << className << "_slots\n" << outdent << "};\n\n} //extern \"C\"\n"; } void CppGenerator::writeMappingMethods(TextStream &s, - const AbstractMetaClass *metaClass, + const AbstractMetaClassCPtr &metaClass, const GeneratorContext &context) const { for (const auto & m : mappingProtocols()) { const auto func = metaClass->findFunction(m.name); - if (func.isNull()) + if (!func) continue; QString funcName = cpythonFunctionName(func); CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode); - s << m.returnType << ' ' << funcName << '(' << m.arguments << ")\n{\n"; - writeInvalidPyObjectCheck(s, QLatin1String("self")); + s << m.returnType << ' ' << funcName << '(' << m.arguments << ")\n{\n" << indent; - writeCppSelfDefinition(s, func, context); + writeCppSelfDefinition(s, func, context, ErrorReturn::Default); const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast(); writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode, func, false, lastArg); - s<< "}\n\n"; + s << outdent << "}\n\n"; } } void CppGenerator::writeSequenceMethods(TextStream &s, - const AbstractMetaClass *metaClass, + const AbstractMetaClassCPtr &metaClass, const GeneratorContext &context) const { bool injectedCode = false; for (const auto &seq : sequenceProtocols()) { const auto func = metaClass->findFunction(seq.name); - if (func.isNull()) + if (!func) continue; injectedCode = true; QString funcName = cpythonFunctionName(func); CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode); s << seq.returnType << ' ' << funcName << '(' << seq.arguments << ")\n{\n" << indent; - writeInvalidPyObjectCheck(s, QLatin1String("self")); - writeCppSelfDefinition(s, func, context); + writeCppSelfDefinition(s, func, context, ErrorReturn::Default); const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast(); writeCodeSnips(s, snips,TypeSystem::CodeSnipPositionAny, @@ -4507,26 +4572,26 @@ void CppGenerator::writeSequenceMethods(TextStream &s, static const QHash<QString, QString> &sqFuncs() { static const QHash<QString, QString> result = { - {QLatin1String("__concat__"), QLatin1String("sq_concat")}, - {QLatin1String("__contains__"), QLatin1String("sq_contains")}, - {QLatin1String("__getitem__"), QLatin1String("sq_item")}, - {QLatin1String("__getslice__"), QLatin1String("sq_slice")}, - {QLatin1String("__len__"), QLatin1String("sq_length")}, - {QLatin1String("__setitem__"), QLatin1String("sq_ass_item")}, - {QLatin1String("__setslice__"), QLatin1String("sq_ass_slice")} + {u"__concat__"_s, u"Py_sq_concat"_s}, + {u"__contains__"_s, u"Py_sq_contains"_s}, + {u"__getitem__"_s, u"Py_sq_item"_s}, + {u"__getslice__"_s, u"Py_sq_slice"_s}, + {u"__len__"_s, u"Py_sq_length"_s}, + {u"__setitem__"_s, u"Py_sq_ass_item"_s}, + {u"__setslice__"_s, u"Py_sq_ass_slice"_s} }; return result; } void CppGenerator::writeTypeAsSequenceDefinition(TextStream &s, - const AbstractMetaClass *metaClass) + const AbstractMetaClassCPtr &metaClass) { bool hasFunctions = false; QMap<QString, QString> funcs; for (const auto &seq : sequenceProtocols()) { const auto func = metaClass->findFunction(seq.name); - if (!func.isNull()) { - funcs.insert(seq.name, QLatin1Char('&') + cpythonFunctionName(func)); + if (func) { + funcs.insert(seq.name, u'&' + cpythonFunctionName(func)); hasFunctions = true; } } @@ -4535,46 +4600,42 @@ void CppGenerator::writeTypeAsSequenceDefinition(TextStream &s, //use default implementation if (!hasFunctions) { - funcs[QLatin1String("__len__")] = baseName + QLatin1String("__len__"); - funcs[QLatin1String("__getitem__")] = baseName + QLatin1String("__getitem__"); - funcs[QLatin1String("__setitem__")] = baseName + QLatin1String("__setitem__"); + funcs[u"__len__"_s] = baseName + u"__len__"_s; + funcs[u"__getitem__"_s] = baseName + u"__getitem__"_s; + funcs[u"__setitem__"_s] = baseName + u"__setitem__"_s; } for (auto it = sqFuncs().cbegin(), end = sqFuncs().cend(); it != end; ++it) { const QString &sqName = it.key(); auto fit = funcs.constFind(sqName); - if (fit != funcs.constEnd()) { - s << "{Py_" << it.value() << ", reinterpret_cast<void *>(" - << fit.value() << ")},\n"; - } + if (fit != funcs.constEnd()) + s << pyTypeSlotEntry(it.value(), fit.value()); } } void CppGenerator::writeTypeAsMappingDefinition(TextStream &s, - const AbstractMetaClass *metaClass) + const AbstractMetaClassCPtr &metaClass) { // Sequence protocol structure members names static const QHash<QString, QString> mpFuncs{ - {QLatin1String("__mlen__"), QLatin1String("mp_length")}, - {QLatin1String("__mgetitem__"), QLatin1String("mp_subscript")}, - {QLatin1String("__msetitem__"), QLatin1String("mp_ass_subscript")}, + {u"__mlen__"_s, u"Py_mp_length"_s}, + {u"__mgetitem__"_s, u"Py_mp_subscript"_s}, + {u"__msetitem__"_s, u"Py_mp_ass_subscript"_s}, }; QMap<QString, QString> funcs; for (const auto &m : mappingProtocols()) { const auto func = metaClass->findFunction(m.name); - if (!func.isNull()) { - const QString entry = QLatin1String("reinterpret_cast<void *>(&") - + cpythonFunctionName(func) + QLatin1Char(')'); + if (func) { + const QString entry = u"reinterpret_cast<void *>(&"_s + + cpythonFunctionName(func) + u')'; funcs.insert(m.name, entry); - } else { - funcs.insert(m.name, QLatin1String(NULL_PTR)); } } for (auto it = mpFuncs.cbegin(), end = mpFuncs.cend(); it != end; ++it) { const auto fit = funcs.constFind(it.key()); if (fit != funcs.constEnd()) - s << "{Py_" << it.value() << ", " << fit.value() << "},\n"; + s << pyTypeSlotEntry(it.value(), fit.value()); } } @@ -4582,106 +4643,103 @@ void CppGenerator::writeTypeAsMappingDefinition(TextStream &s, static const QHash<QString, QString> &nbFuncs() { static const QHash<QString, QString> result = { - {QLatin1String("__add__"), QLatin1String("nb_add")}, - {QLatin1String("__sub__"), QLatin1String("nb_subtract")}, - {QLatin1String("__mul__"), QLatin1String("nb_multiply")}, - {QLatin1String("__div__"), QLatin1String("nb_divide")}, - {QLatin1String("__mod__"), QLatin1String("nb_remainder")}, - {QLatin1String("__neg__"), QLatin1String("nb_negative")}, - {QLatin1String("__pos__"), QLatin1String("nb_positive")}, - {QLatin1String("__invert__"), QLatin1String("nb_invert")}, - {QLatin1String("__lshift__"), QLatin1String("nb_lshift")}, - {QLatin1String("__rshift__"), QLatin1String("nb_rshift")}, - {QLatin1String("__and__"), QLatin1String("nb_and")}, - {QLatin1String("__xor__"), QLatin1String("nb_xor")}, - {QLatin1String("__or__"), QLatin1String("nb_or")}, - {QLatin1String("__iadd__"), QLatin1String("nb_inplace_add")}, - {QLatin1String("__isub__"), QLatin1String("nb_inplace_subtract")}, - {QLatin1String("__imul__"), QLatin1String("nb_inplace_multiply")}, - {QLatin1String("__idiv__"), QLatin1String("nb_inplace_divide")}, - {QLatin1String("__imod__"), QLatin1String("nb_inplace_remainder")}, - {QLatin1String("__ilshift__"), QLatin1String("nb_inplace_lshift")}, - {QLatin1String("__irshift__"), QLatin1String("nb_inplace_rshift")}, - {QLatin1String("__iand__"), QLatin1String("nb_inplace_and")}, - {QLatin1String("__ixor__"), QLatin1String("nb_inplace_xor")}, - {QLatin1String("__ior__"), QLatin1String("nb_inplace_or")}, - {boolT(), QLatin1String("nb_nonzero")} + {u"__abs__"_s, u"Py_nb_absolute"_s}, + {u"__add__"_s, u"Py_nb_add"_s}, + {u"__sub__"_s, u"Py_nb_subtract"_s}, + {u"__mul__"_s, u"Py_nb_multiply"_s}, + {u"__div__"_s, u"Py_nb_true_divide"_s}, + {u"__mod__"_s, u"Py_nb_remainder"_s}, + {u"__neg__"_s, u"Py_nb_negative"_s}, + {u"__pos__"_s, u"Py_nb_positive"_s}, + {u"__pow__"_s, u"Py_nb_power"_s}, + {u"__invert__"_s, u"Py_nb_invert"_s}, + {u"__lshift__"_s, u"Py_nb_lshift"_s}, + {u"__rshift__"_s, u"Py_nb_rshift"_s}, + {u"__and__"_s, u"Py_nb_and"_s}, + {u"__xor__"_s, u"Py_nb_xor"_s}, + {u"__or__"_s, u"Py_nb_or"_s}, + {u"__iadd__"_s, u"Py_nb_inplace_add"_s}, + {u"__isub__"_s, u"Py_nb_inplace_subtract"_s}, + {u"__imul__"_s, u"Py_nb_inplace_multiply"_s}, + {u"__imod__"_s, u"Py_nb_inplace_remainder"_s}, + {u"__ilshift__"_s, u"Py_nb_inplace_lshift"_s}, + {u"__irshift__"_s, u"Py_nb_inplace_rshift"_s}, + {u"__iand__"_s, u"Py_nb_inplace_and"_s}, + {u"__ixor__"_s, u"Py_nb_inplace_xor"_s}, + {u"__ior__"_s, u"Py_nb_inplace_or"_s}, + {u"__bool__"_s, u"Py_nb_bool"_s}, + {u"__int__"_s, u"Py_nb_int"_s}, + {u"__float__"_s, u"Py_nb_float"_s} }; return result; } -void CppGenerator::writeTypeAsNumberDefinition(TextStream &s, const AbstractMetaClass *metaClass) const +void CppGenerator::writeTypeAsNumberDefinition(TextStream &s, const AbstractMetaClassCPtr &metaClass) const { QMap<QString, QString> nb; - const QList<AbstractMetaFunctionCList> opOverloads = - filterGroupedOperatorFunctions(metaClass, - OperatorQueryOption::ArithmeticOp - | OperatorQueryOption::IncDecrementOp - | OperatorQueryOption::LogicalOp - | OperatorQueryOption::BitwiseOp); - - for (const AbstractMetaFunctionCList &opOverload : opOverloads) { + const QList<AbstractMetaFunctionCList> opOverloads = numberProtocolOperators(metaClass); + for (const auto &opOverload : opOverloads) { const auto rfunc = opOverload.at(0); QString opName = ShibokenGenerator::pythonOperatorFunctionName(rfunc); nb[opName] = cpythonFunctionName(rfunc); } + for (auto it = m_nbFuncs.cbegin(), end = m_nbFuncs.cend(); it != end; ++it) { + if (!it.value().isEmpty()) + nb.insert(it.key(), it.value()); + } + QString baseName = cpythonBaseName(metaClass); if (hasBoolCast(metaClass)) - nb.insert(boolT(), baseName + QLatin1String("___nb_bool")); + nb.insert(u"__bool__"_s, baseName + u"___nb_bool"_s); for (auto it = nbFuncs().cbegin(), end = nbFuncs().cend(); it != end; ++it) { const QString &nbName = it.key(); - if (nbName == QLatin1String("__div__") || nbName == QLatin1String("__idiv__")) - continue; // excludeFromPy3K const auto nbIt = nb.constFind(nbName); - if (nbIt != nb.constEnd()) { - const QString fixednbName = nbName == boolT() - ? QLatin1String("nb_bool") : it.value(); - s << "{Py_" << fixednbName << ", reinterpret_cast<void *>(" - << nbIt.value() << ")},\n"; - } - } - - auto nbIt = nb.constFind(QLatin1String("__div__")); - if (nbIt != nb.constEnd()) - s << "{Py_nb_true_divide, reinterpret_cast<void *>(" << nbIt.value() << ")},\n"; - - nbIt = nb.constFind(QLatin1String("__idiv__")); - if (nbIt != nb.constEnd()) { - s << "// This function is unused in Python 3. We reference it here.\n" - << "{0, reinterpret_cast<void *>(" << nbIt.value() << ")},\n" - << "// This list is ending at the first 0 entry.\n" - << "// Therefore, we need to put the unused functions at the very end.\n"; + if (nbIt != nb.constEnd()) + s << pyTypeSlotEntry(it.value(), nbIt.value()); } } -void CppGenerator::writeTpTraverseFunction(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeTpTraverseFunction(TextStream &s, const AbstractMetaClassCPtr &metaClass) { QString baseName = cpythonBaseName(metaClass); s << "static int " << baseName << "_traverse(PyObject *self, visitproc visit, void *arg)\n{\n" << indent - << "return SbkObject_TypeF()->tp_traverse(self, visit, arg);\n" + << "auto traverseProc = " + << pyTypeGetSlot("traverseproc", sbkObjectTypeF, "Py_tp_traverse") << ";\n" + << "return traverseProc(self, visit, arg);\n" << outdent << "}\n"; } -void CppGenerator::writeTpClearFunction(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeTpClearFunction(TextStream &s, const AbstractMetaClassCPtr &metaClass) { QString baseName = cpythonBaseName(metaClass); s << "static int " << baseName << "_clear(PyObject *self)\n{\n" << indent - << "return reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())->tp_clear(self);\n" + << "auto clearProc = " + << pyTypeGetSlot("inquiry", sbkObjectTypeF, "Py_tp_clear") << ";\n" + << "return clearProc(self);\n" << outdent << "}\n"; } -void CppGenerator::writeCopyFunction(TextStream &s, const GeneratorContext &context) const +QString CppGenerator::writeCopyFunction(TextStream &s, + TextStream &definitionStream, + TextStream &signatureStream, + const GeneratorContext &context) { - const AbstractMetaClass *metaClass = context.metaClass(); + const auto metaClass = context.metaClass(); const QString className = chopType(cpythonTypeName(metaClass)); - s << "static PyObject *" << className << "___copy__(PyObject *self)\n" - << "{\n" << indent; - writeCppSelfDefinition(s, context, false, false, true); + const QString funcName = className + u"__copy__"_s; + + signatureStream << fullPythonClassName(metaClass) << ".__copy__()\n"; + definitionStream << PyMethodDefEntry{u"__copy__"_s, funcName, {"METH_NOARGS"_ba}, {}} + << ",\n"; + + s << "static PyObject *" << funcName << "(PyObject *self)\n" + << "{\n" << indent; + writeCppSelfDefinition(s, context, ErrorReturn::Default, CppSelfDefinitionFlag::CppSelfAsReference); QString conversionCode; if (!context.forSmartPointer()) conversionCode = cpythonToPythonConversionFunction(metaClass); @@ -4690,19 +4748,21 @@ void CppGenerator::writeCopyFunction(TextStream &s, const GeneratorContext &cont s << "PyObject *" << PYTHON_RETURN_VAR << " = " << conversionCode << CPP_SELF_VAR << ");\n"; - writeFunctionReturnErrorCheckSection(s); + writeFunctionReturnErrorCheckSection(s, ErrorReturn::Default); s << "return " << PYTHON_RETURN_VAR << ";\n" << outdent << "}\n\n"; + + return funcName; } static inline void writeGetterFunctionStart(TextStream &s, const QString &funcName) { - s << "static PyObject *" << funcName << "(PyObject *self, void *)\n" + s << "static PyObject *" << funcName << "(PyObject *self, void * /* closure */)\n" << "{\n" << indent; } QString CppGenerator::cppFieldAccess(const AbstractMetaField &metaField, - const GeneratorContext &context) const + const GeneratorContext &context) { QString result; QTextStream str(&result); @@ -4716,9 +4776,8 @@ QString CppGenerator::cppFieldAccess(const AbstractMetaField &metaField, void CppGenerator::writeGetterFunction(TextStream &s, const AbstractMetaField &metaField, - const GeneratorContext &context) const + const GeneratorContext &context) { - ErrorCode errorCode(QString::fromLatin1(NULL_PTR)); writeGetterFunctionStart(s, cpythonGetterFunctionName(metaField)); writeCppSelfDefinition(s, context); @@ -4729,6 +4788,17 @@ void CppGenerator::writeGetterFunction(TextStream &s, && !fieldType.isPointer(); QString cppField = cppFieldAccess(metaField, context); + + if (metaField.generateOpaqueContainer() + && fieldType.generateOpaqueContainer()) { + const QString creationFunc = opaqueContainerCreationFunc(fieldType); + writeOpaqueContainerCreationFuncDecl(s, creationFunc, fieldType); + s << "PyObject *pyOut = " << creationFunc + << "(&" << cppField << ");\nPy_IncRef(pyOut);\n" + << "return pyOut;\n" << outdent << "}\n"; + return; + } + if (newWrapperSameObject) { cppField.prepend(u"&("); cppField.append(u')'); @@ -4736,41 +4806,38 @@ void CppGenerator::writeGetterFunction(TextStream &s, if (fieldType.isCppIntegralPrimitive() || fieldType.isEnum()) { s << getFullTypeNameWithoutModifiers(fieldType) << " cppOut_local = " << cppField << ";\n"; - cppField = QLatin1String("cppOut_local"); + cppField = u"cppOut_local"_s; } s << "PyObject *pyOut = {};\n"; if (newWrapperSameObject) { // Special case colocated field with same address (first field in a struct) s << "if (reinterpret_cast<void *>(" - << cppField - << ") == reinterpret_cast<void *>(" - << CPP_SELF_VAR << ")) {\n"; - { - Indentation indent(s); - s << "pyOut = reinterpret_cast<PyObject *>(Shiboken::Object::findColocatedChild(" - << "reinterpret_cast<SbkObject *>(self), " - << cpythonTypeNameExt(fieldType) - << "));\n"; - s << "if (pyOut) {\n"; - { - Indentation indent(s); - s << "Py_IncRef(pyOut);\n" - << "return pyOut;\n"; - } - s << "}\n"; - } + << cppField << ") == reinterpret_cast<void *>(" + << CPP_SELF_VAR << ")) {\n" << indent + << "pyOut = reinterpret_cast<PyObject *>(Shiboken::Object::findColocatedChild(" + << "reinterpret_cast<SbkObject *>(self), " + << cpythonTypeNameExt(fieldType) << "));\n" + << "if (pyOut != nullptr) {\n" << indent + << "Py_IncRef(pyOut);\n" + << "return pyOut;\n" + << outdent << "}\n"; // Check if field wrapper has already been created. - s << "} else if (Shiboken::BindingManager::instance().hasWrapper(" << cppField << ")) {" << "\n"; - { - Indentation indent(s); - s << "pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(" - << cppField << "));" << "\n" - << "Py_IncRef(pyOut);" << "\n" - << "return pyOut;" << "\n"; - } - s << "}\n"; - // Create and register new wrapper + s << outdent << "} else if (Shiboken::BindingManager::instance().hasWrapper(" + << cppField << ")) {" << "\n" << indent + << "pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(" + << cppField << "));" << "\n" + << "Py_IncRef(pyOut);" << "\n" + << "return pyOut;" << "\n" + << outdent << "}\n"; + // Create and register new wrapper. We force a pointer conversion also + // for wrapped value types so that they refer to the struct member, + // avoiding any trouble copying them. Add a parent relationship to + // properly notify if the struct is deleted (see protected_test.py, + // testProtectedValueTypeProperty()). Note that this has currently + // unsolved issues when using temporary Python lists of structs + // which can cause elements to be reported deleted in expressions like + // "foo.list_of_structs[2].field". s << "pyOut = " << "Shiboken::Object::newObject(" << cpythonTypeNameExt(fieldType) << ", " << cppField << ", false, true);\n" @@ -4783,57 +4850,52 @@ void CppGenerator::writeGetterFunction(TextStream &s, } // Write a getter for QPropertySpec -void CppGenerator::writeGetterFunction(TextStream &s, const QPropertySpec &property, - const GeneratorContext &context) const +void CppGenerator::writeGetterFunction(TextStream &s, + const QPropertySpec &property, + const GeneratorContext &context) { - ErrorCode errorCode(0); writeGetterFunctionStart(s, cpythonGetterFunctionName(property, context.metaClass())); writeCppSelfDefinition(s, context); - const QString value = QStringLiteral("value"); + const QString value = "value"_L1; s << "auto " << value << " = " << CPP_SELF_VAR << "->" << property.read() << "();\n" - << "auto pyResult = "; + << "auto *pyResult = "; writeToPythonConversion(s, property.type(), context.metaClass(), value); - s << ";\nif (PyErr_Occurred() || !pyResult) {\n"; - { - Indentation indent(s); - s << "Py_XDECREF(pyResult);\nreturn {};\n"; - } - s << "}\nreturn pyResult;\n" << outdent << "}\n\n"; + s << ";\nif (" << shibokenErrorsOccurred << " || pyResult == nullptr) {\n" + << indent << "Py_XDECREF(pyResult);\nreturn {};\n" << outdent + << "}\nreturn pyResult;\n" << outdent << "}\n\n"; } // Write setter function preamble (type checks on "pyIn") -void CppGenerator::writeSetterFunctionPreamble(TextStream &s, const QString &name, +void CppGenerator::writeSetterFunctionPreamble(TextStream &s, + const QString &name, const QString &funcName, const AbstractMetaType &type, - const GeneratorContext &context) const + const GeneratorContext &context) { - s << "static int " << funcName << "(PyObject *self, PyObject *pyIn, void *)\n" + s << "static int " << funcName << "(PyObject *self, PyObject *pyIn, void * /* closure */)\n" << "{\n" << indent; - writeCppSelfDefinition(s, context); + writeCppSelfDefinition(s, context, ErrorReturn::Zero); s << "if (pyIn == " << NULL_PTR << ") {\n" << indent - << "PyErr_SetString(PyExc_TypeError, \"'" - << name << "' may not be deleted\");\n" + << "Shiboken::Errors::setInvalidTypeDeletion(\"" << name << "\");\n" << "return -1;\n" << outdent << "}\n"; - s << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << "{nullptr};\n" + s << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR << ";\n" << "if (!"; - writeTypeCheck(s, type, QLatin1String("pyIn"), isNumber(type.typeEntry())); + writeTypeCheck(s, type, u"pyIn"_s, isNumber(type.typeEntry())); s << ") {\n" << indent - << "PyErr_SetString(PyExc_TypeError, \"wrong type attributed to '" - << name << "', '" << type.name() << "' or convertible type expected\");\n" + << "Shiboken::Errors::setSetterTypeError(\"" << name << "\", \"" + << type.name() << "\");\n" << "return -1;\n" << outdent << "}\n\n"; } void CppGenerator::writeSetterFunction(TextStream &s, const AbstractMetaField &metaField, - const GeneratorContext &context) const + const GeneratorContext &context) { - ErrorCode errorCode(0); - const AbstractMetaType &fieldType = metaField.type(); writeSetterFunctionPreamble(s, metaField.name(), cpythonSetterFunctionName(metaField), fieldType, context); @@ -4864,215 +4926,182 @@ void CppGenerator::writeSetterFunction(TextStream &s, } // Write a setter for QPropertySpec -void CppGenerator::writeSetterFunction(TextStream &s, const QPropertySpec &property, - const GeneratorContext &context) const +void CppGenerator::writeSetterFunction(TextStream &s, + const QPropertySpec &property, + const GeneratorContext &context) { - ErrorCode errorCode(0); - writeSetterFunctionPreamble(s, property.name(), + writeSetterFunctionPreamble(s, + property.name(), cpythonSetterFunctionName(property, context.metaClass()), property.type(), context); s << "auto cppOut = " << CPP_SELF_VAR << "->" << property.read() << "();\n" << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut);\n" - << "if (PyErr_Occurred())\n"; - { - Indentation indent(s); - s << "return -1;\n"; - } - s << CPP_SELF_VAR << "->" << property.write() << "(cppOut);\n" + << "if (" << shibokenErrorsOccurred << ")\n" << indent + << "return -1;\n" << outdent + << CPP_SELF_VAR << "->" << property.write() << "(cppOut);\n" << "return 0;\n" << outdent << "}\n\n"; } -void CppGenerator::writeRichCompareFunction(TextStream &s, - const GeneratorContext &context) const +void CppGenerator::writeRichCompareFunctionHeader(TextStream &s, + const QString &baseName, + 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" << indent; - writeCppSelfDefinition(s, context, false, false, true); - writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR)); - s << "PyObject *" << PYTHON_RETURN_VAR << "{};\n" - << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ";\n"; - writeUnusedVariableCast(s, QLatin1String(PYTHON_TO_CPP_VAR)); - s << '\n'; + writeCppSelfDefinition(s, context, ErrorReturn::Default, CppSelfDefinitionFlag::CppSelfAsReference); + s << sbkUnusedVariableCast(CPP_SELF_VAR) + << "PyObject *" << PYTHON_RETURN_VAR << "{};\n" + << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR << ";\n" + << sbkUnusedVariableCast(PYTHON_TO_CPP_VAR) << '\n'; +} - s << "switch (op) {\n"; - { - Indentation indent(s); - const QList<AbstractMetaFunctionCList> &groupedFuncs = - filterGroupedOperatorFunctions(metaClass, OperatorQueryOption::ComparisonOp); - for (const AbstractMetaFunctionCList &overloads : groupedFuncs) { - const auto rfunc = overloads[0]; +void CppGenerator::writeRichCompareFunction(TextStream &s, + const GeneratorContext &context) const +{ + const auto metaClass = context.metaClass(); + QString baseName = cpythonBaseName(metaClass); + writeRichCompareFunctionHeader(s, baseName, context); - QString operatorId = ShibokenGenerator::pythonRichCompareOperatorId(rfunc); - s << "case " << operatorId << ':' << '\n'; + s << "switch (op) {\n" << indent; + const QList<AbstractMetaFunctionCList> &groupedFuncs = + filterGroupedOperatorFunctions(metaClass, OperatorQueryOption::ComparisonOp); + for (const AbstractMetaFunctionCList &overloads : groupedFuncs) { + const auto rfunc = overloads[0]; - Indentation indent(s); + const auto op = rfunc->comparisonOperatorType().value(); + s << "case " << AbstractMetaFunction::pythonRichCompareOpCode(op) + << ":\n" << indent; - QString op = rfunc->originalName(); - op = op.right(op.size() - QLatin1String("operator").size()); + int alternativeNumericTypes = 0; + for (const auto &func : overloads) { + if (!func->isStatic() && + ShibokenGenerator::isNumber(func->arguments().at(0).type().typeEntry())) + alternativeNumericTypes++; + } - int alternativeNumericTypes = 0; - for (const auto &func : overloads) { - if (!func->isStatic() && - ShibokenGenerator::isNumber(func->arguments().at(0).type().typeEntry())) - alternativeNumericTypes++; + bool first = true; + OverloadData overloadData(overloads, api()); + const OverloadDataList &nextOverloads = overloadData.children(); + for (const auto &od : nextOverloads) { + const auto func = od->referenceFunction(); + if (func->isStatic()) + continue; + auto argType = getArgumentType(func, 0); + if (!first) { + s << " else "; + } else { + first = false; } - - bool first = true; - OverloadData overloadData(overloads, api()); - const OverloadDataList &nextOverloads = overloadData.children(); - for (const auto &od : nextOverloads) { - const auto func = od->referenceFunction(); - if (func->isStatic()) - continue; - auto argType = getArgumentType(func, 0); - if (!first) { - s << " else "; - } else { - first = false; - } - s << "if ("; - writeTypeCheck(s, argType, QLatin1String(PYTHON_ARG), alternativeNumericTypes == 1 || isPyInt(argType)); - s << ") {\n"; - { - Indentation indent(s); - s << "// " << func->signature() << '\n'; - 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, - false /* uses PyArgs */, &func->arguments().constLast()); - generateOperatorCode = false; - } - } - if (generateOperatorCode) { - if (!func->isVoid()) - s << func->type().cppSignature() << " " << CPP_RETURN_VAR << " = "; - // expression - if (func->isPointerOperator()) - s << '&'; - s << CPP_SELF_VAR << ' ' << op << '('; - if (argType.shouldDereferenceArgument()) - s << '*'; - s << CPP_ARG0 << ");\n" - << PYTHON_RETURN_VAR << " = "; - if (!func->isVoid()) - writeToPythonConversion(s, func->type(), metaClass, QLatin1String(CPP_RETURN_VAR)); - else - s << "Py_None;\n" << "Py_INCREF(Py_None)"; - s << ";\n"; - } + s << "if ("; + writeTypeCheck(s, argType, PYTHON_ARG, + alternativeNumericTypes == 1 || isPyInt(argType)); + s << ") {\n" << indent + << "// " << func->signature() << '\n'; + writeArgumentConversion(s, argType, CPP_ARG0, + PYTHON_ARG, ErrorReturn::Default, + metaClass, + QString(), func->isUserAdded()); + // If the function is user added, use the inject code + bool generateOperatorCode = true; + if (func->isUserAdded()) { + CodeSnipList snips = func->injectedCodeSnips(); + if (!snips.isEmpty()) { + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionAny, + TypeSystem::TargetLangCode, func, + false /* uses PyArgs */, &func->arguments().constLast()); + generateOperatorCode = false; } - s << '}'; } - - s << " else {\n"; - if (operatorId == QLatin1String("Py_EQ") || operatorId == QLatin1String("Py_NE")) { - Indentation indent(s); - s << PYTHON_RETURN_VAR << " = " - << (operatorId == QLatin1String("Py_EQ") ? "Py_False" : "Py_True") << ";\n" - << "Py_INCREF(" << PYTHON_RETURN_VAR << ");\n"; - } else { - Indentation indent(s); - s << "goto " << baseName << "_RichComparison_TypeError;\n"; + if (generateOperatorCode) { + if (!func->isVoid()) + s << func->type().cppSignature() << " " << CPP_RETURN_VAR << " = "; + // expression + if (func->isPointerOperator()) + s << '&'; + s << CPP_SELF_VAR << ' ' + << AbstractMetaFunction::cppComparisonOperator(op) << " ("; + auto generatorArg = GeneratorArgument::fromMetaType(argType); + if (generatorArg.indirections != 0) + s << QByteArray(generatorArg.indirections, '*'); + s << CPP_ARG0 << ");\n" + << PYTHON_RETURN_VAR << " = "; + if (!func->isVoid()) { + writeToPythonConversion(s, func->type(), metaClass, + CPP_RETURN_VAR); + } else { + s << "Py_None;\n" << "Py_INCREF(Py_None)"; + } + s << ";\n"; } - s << "}\n\n"; - - s << "break;\n"; + s << outdent << '}'; } - s << "default:\n"; - { - Indentation indent(s); - s << "// PYSIDE-74: By default, we redirect to object's tp_richcompare (which is `==`, `!=`).\n" - << "return FallbackRichCompare(self, " << PYTHON_ARG << ", op);\n" - << "goto " << baseName << "_RichComparison_TypeError;\n"; + + s << " else {\n"; + if (op == AbstractMetaFunction::OperatorEqual || + op == AbstractMetaFunction::OperatorNotEqual) { + s << indent << PYTHON_RETURN_VAR << " = " + << (op == AbstractMetaFunction::OperatorEqual ? "Py_False" : "Py_True") << ";\n" + << "Py_INCREF(" << PYTHON_RETURN_VAR << ");\n" << outdent; + } else { + s << indent << "return Shiboken::returnFromRichCompare(" + << PYTHON_RETURN_VAR << ");\n" << outdent; } - } - s << "}\n\n"; + s << "}\n\n"; - s << "if (" << PYTHON_RETURN_VAR << " && !PyErr_Occurred())\n"; - { - Indentation indent(s); - s << "return " << PYTHON_RETURN_VAR << ";\n"; + s << "break;\n" << outdent; } - s << baseName << "_RichComparison_TypeError:\n" - << "PyErr_SetString(PyExc_NotImplementedError, \"operator not implemented.\");\n" - << returnStatement(m_currentErrorCode) << '\n' << '\n' - << outdent << "}\n\n"; + s << "default:\n" << indent + << richCompareComment + << "return FallbackRichCompare(self, " << PYTHON_ARG << ", op);\n" + << outdent << outdent << "}\n\n" + << "return Shiboken::returnFromRichCompare(" << PYTHON_RETURN_VAR << ");\n" << outdent + << "}\n\n"; } -QString CppGenerator::methodDefinitionParameters(const OverloadData &overloadData) const +// Return a flag combination for PyMethodDef +QByteArrayList CppGenerator::methodDefinitionParameters(const OverloadData &overloadData) const { const bool usePyArgs = overloadData.pythonFunctionWrapperUsesListOfArguments(); - const auto func = overloadData.referenceFunction(); int min = overloadData.minArgs(); int max = overloadData.maxArgs(); - QString result; - QTextStream s(&result); - s << "reinterpret_cast<PyCFunction>(" - << cpythonFunctionName(func) << "), "; + QByteArrayList result; if ((min == max) && (max < 2) && !usePyArgs) { - if (max == 0) - s << "METH_NOARGS"; - else - s << "METH_O"; + result.append(max == 0 ? QByteArrayLiteral("METH_NOARGS") + : QByteArrayLiteral("METH_O")); } else { - s << "METH_VARARGS"; + result.append(QByteArrayLiteral("METH_VARARGS")); if (overloadData.hasArgumentWithDefaultValue()) - s << "|METH_KEYWORDS"; + result.append(QByteArrayLiteral("METH_KEYWORDS")); } // METH_STATIC causes a crash when used for global functions (also from // invisible namespaces). - auto ownerClass = func->ownerClass(); + const auto ownerClass = overloadData.referenceFunction()->ownerClass(); if (ownerClass - && !invisibleTopNamespaces().contains(const_cast<AbstractMetaClass *>(ownerClass))) { + && !invisibleTopNamespaces().contains(std::const_pointer_cast<AbstractMetaClass>(ownerClass))) { if (overloadData.hasStaticFunction()) - s << "|METH_STATIC"; + result.append(QByteArrayLiteral("METH_STATIC")); if (overloadData.hasClassMethod()) - s << "|METH_CLASS"; + result.append(QByteArrayLiteral("METH_CLASS")); } return result; } -void CppGenerator::writeMethodDefinitionEntries(TextStream &s, - const OverloadData &overloadData, - qsizetype maxEntries) const +QList<PyMethodDefEntry> + CppGenerator::methodDefinitionEntries(const OverloadData &overloadData) const { - const QStringList names = overloadData.referenceFunction()->definitionNames(); - const QString parameters = methodDefinitionParameters(overloadData); - const qsizetype count = maxEntries > 0 - ? qMin(names.size(), maxEntries) : names.size(); - for (qsizetype i = 0; i < count; ++i) { - if (i) - s << ",\n"; - s << "{\"" << names.at(i) << "\", " << parameters << '}'; - } -} -void CppGenerator::writeMethodDefinition(TextStream &s, - const OverloadData &overloadData) const -{ - const auto func = overloadData.referenceFunction(); - if (m_tpFuncs.contains(func->name())) - return; + const QStringList names = overloadData.referenceFunction()->definitionNames(); + const QString funcName = cpythonFunctionName(overloadData.referenceFunction()); + const QByteArrayList parameters = methodDefinitionParameters(overloadData); - if (OverloadData::hasStaticAndInstanceFunctions(overloadData.overloads())) { - s << cpythonMethodDefinitionName(func); - } else { - writeMethodDefinitionEntries(s, overloadData); - } - s << ',' << '\n'; + QList<PyMethodDefEntry> result; + result.reserve(names.size()); + for (const auto &name : names) + result.append({name, funcName, parameters, {}}); + return result; } // Format the type signature of a function parameter @@ -5093,10 +5122,15 @@ QString CppGenerator::signatureParameter(const AbstractMetaArgument &arg) const const AbstractMetaFunctionCList conversions = api().implicitConversions(metaType); for (const auto &f : conversions) { - if (f->isConstructor() && !f->arguments().isEmpty()) - signatures << f->arguments().constFirst().type().pythonSignature(); - else if (f->isConversionOperator()) + if (f->isConstructor() && !f->arguments().isEmpty()) { + // PYSIDE-2712: modified types from converting constructors are not always correct + // candidates if they are modified by the type system reference + if (!f->arguments().constFirst().isTypeModified()) { + signatures << f->arguments().constFirst().type().pythonSignature(); + } + } else if (f->isConversionOperator()) { signatures << f->ownerClass()->fullName(); + } } const qsizetype size = signatures.size(); @@ -5110,12 +5144,6 @@ QString CppGenerator::signatureParameter(const AbstractMetaArgument &arg) const if (size > 1) s << ']'; - if (!arg.defaultValueExpression().isEmpty()) { - s << '='; - QString e = arg.defaultValueExpression(); - e.replace(QLatin1String("::"), QLatin1String(".")); - s << e; - } return result; } @@ -5132,181 +5160,213 @@ void CppGenerator::writeSignatureInfo(TextStream &s, const OverloadData &overloa // PYSIDE-1328: `self`-ness cannot be computed in Python because there are mixed cases. // Toplevel functions like `PySide6.QtCore.QEnum` are always self-less. if (!(f->isStatic()) && f->ownerClass()) - args << QLatin1String("self"); + args << PYTHON_SELF_VAR; const auto &arguments = f->arguments(); for (qsizetype i = 0, size = arguments.size(); i < size; ++i) { - QString t = f->pyiTypeReplaced(i + 1); - if (t.isEmpty()) { - t = signatureParameter(arguments.at(i)); - } else { - t.prepend(u':'); - t.prepend(arguments.at(i).name()); + const auto n = i + 1; + const auto &arg = arguments.at(i); + if (!f->argumentRemoved(n)) { + QString t = f->pyiTypeReplaced(n); + if (t.isEmpty()) { + t = signatureParameter(arg); + } else { + t.prepend(u':'); + t.prepend(arg.name()); + } + QString defaultValue = arg.defaultValueExpression(); + if (!defaultValue.isEmpty()) + t += u'=' + defaultValue.replace(u"::"_s, u"."_s); + args.append(t); } - args.append(t); } // mark the multiple signatures as such, to make it easier to generate different code if (multiple) s << idx-- << ':'; - s << funcName << '(' << args.join(QLatin1Char(',')) << ')'; - if (!f->isVoid()) { - QString t = f->pyiTypeReplaced(0); - if (t.isEmpty()) - t = f->type().pythonSignature(); - s << "->" << t; - } + s << funcName << '(' << args.join(u',') << ')'; + + QString returnType = f->pyiTypeReplaced(0); // pyi or modified type + if (returnType.isEmpty() && !f->isVoid()) + returnType = f->type().pythonSignature(); + if (!returnType.isEmpty()) + s << "->" << returnType; + s << '\n'; } } -void CppGenerator::writeEnumsInitialization(TextStream &s, AbstractMetaEnumList &enums) const +void CppGenerator::writeEnumsInitialization(TextStream &s, AbstractMetaEnumList &enums) { if (enums.isEmpty()) return; - s << "// Initialization of enums.\n\n"; - for (const AbstractMetaEnum &cppEnum : qAsConst(enums)) { + bool preambleWritten = false; + bool etypeUsed = false; + + for (const AbstractMetaEnum &cppEnum : std::as_const(enums)) { if (cppEnum.isPrivate()) continue; - writeEnumInitialization(s, cppEnum); + if (!preambleWritten) { + s << "// Initialization of enums.\n" + << "Shiboken::AutoDecRef tpDict{};\n" + << "PyTypeObject *EType{};\n\n"; + preambleWritten = true; + } + ConfigurableScope configScope(s, cppEnum.typeEntry()); + etypeUsed |= writeEnumInitialization(s, cppEnum); } + if (preambleWritten && !etypeUsed) + s << sbkUnusedVariableCast("EType"); } -static QString mangleName(QString name) +static qsizetype maxLineLength(const QStringList &list) { - if ( name == QLatin1String("None") - || name == QLatin1String("False") - || name == QLatin1String("True")) - name += QLatin1Char('_'); - return name; + qsizetype result = 0; + for (const auto &s : list) { + if (auto len = s.size(); len > result) + result = len; + } + return result; } -void CppGenerator::writeEnumInitialization(TextStream &s, const AbstractMetaEnum &cppEnum) const +bool CppGenerator::writeEnumInitialization(TextStream &s, const AbstractMetaEnum &cppEnum) { - const AbstractMetaClass *enclosingClass = cppEnum.targetLangEnclosingClass(); - bool hasUpperEnclosingClass = enclosingClass && enclosingClass->targetLangEnclosingClass() != nullptr; - const EnumTypeEntry *enumTypeEntry = cppEnum.typeEntry(); + const auto enclosingClass = cppEnum.targetLangEnclosingClass(); + const bool hasUpperEnclosingClass = enclosingClass + && enclosingClass->targetLangEnclosingClass(); + EnumTypeEntryCPtr enumTypeEntry = cppEnum.typeEntry(); QString enclosingObjectVariable; if (enclosingClass) enclosingObjectVariable = cpythonTypeName(enclosingClass); else if (hasUpperEnclosingClass) - enclosingObjectVariable = QLatin1String("enclosingClass"); + enclosingObjectVariable = u"enclosingClass"_s; else - enclosingObjectVariable = QLatin1String("module"); + enclosingObjectVariable = u"module"_s; s << "// 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 << cpythonTypeNameExt(flags) << " = PySide::QFlags::create(\"" - << packageLevel << ':' << fullPath << flags->flagsName() << "\", " - << cpythonEnumName(cppEnum) << "_number_slots);\n"; - } + const QString userType = cppEnum.typeEntry()->cppType(); + const bool isSigned = cppEnum.isSigned() && !userType.contains(u"unsigned"_s); + const bool isAccessible = !avoidProtectedHack() || !cppEnum.isProtected(); + const auto enumValues = cppEnum.nonRejectedValues(); - enumVarTypeObj = cpythonTypeNameExt(enumTypeEntry); - - s << enumVarTypeObj << " = Shiboken::Enum::" - << ((enclosingClass || hasUpperEnclosingClass) ? "createScopedEnum" : "createGlobalEnum") - << '(' << enclosingObjectVariable << ',' << '\n'; - { - Indentation indent(s); - s << '"' << cppEnum.name() << "\",\n" - << '"' << packageLevel << ':' << getClassTargetFullName(cppEnum) << "\",\n" - << '"' << cppEnum.qualifiedCppName() << '"'; - if (flags) - s << ",\n" << cpythonTypeNameExt(flags); - s << ");\n"; - } - s << "if (!" << cpythonTypeNameExt(cppEnum.typeEntry()) << ")\n"; - { - Indentation indent(s); - s << returnStatement(m_currentErrorCode) << "\n\n"; + const QString prefix = cppEnum.name(); + + const QString intType = userType.isEmpty() ? cppEnum.underlyingType() : userType; + + // Create a list of values + const QString initializerValues = prefix + u"_InitializerValues"_s; + const QString initializerName = prefix + u"_Initializer"_s; + + // Build maybe array of enum names. + if (cppEnum.enumKind() != AnonymousEnum) { + s << "const char *" << initializerName << "[] = {\n" << indent; + for (const auto &enumValue : enumValues) { + QString name = mangleName(enumValue.name()); + s << '\"' << name << "\",\n"; } + s << "nullptr};\n" << outdent; } - for (const AbstractMetaEnumValue &enumValue : cppEnum.values()) { - if (enumTypeEntry->isEnumValueRejected(enumValue.name())) - continue; + int targetHexLen = 0; + QString usedIntType = userType; + if (usedIntType.isEmpty()) { + const int usedBits = cppEnum.usedBits(); + targetHexLen = usedBits / 4; + usedIntType = AbstractMetaEnum::intTypeForSize(usedBits, cppEnum.isSigned()); + } - 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(); - } + if (usedIntType != intType) + s << "// \"" << usedIntType << "\" used instead of \"" << intType << "\"\n"; - const QString mangledName = mangleName(enumValue.name()); - switch (cppEnum.enumKind()) { - case AnonymousEnum: + // Calculating formatting columns + QString enumValuePrefix; + if (isAccessible) { + if (cppEnum.enclosingClass()) + enumValuePrefix += cppEnum.enclosingClass()->qualifiedCppName() + u"::"_s; + if (!cppEnum.isAnonymous()) + enumValuePrefix += cppEnum.name() + u"::"_s; + } + + // Build array of enum values + if (enumValues.isEmpty()) { + s << "const " << usedIntType << " *" << initializerValues << "{};\n"; + } else { + QStringList values; + values.reserve(enumValues.size()); + s << "constexpr " << usedIntType << ' ' << initializerValues << "[] = {\n" << indent; + for (qsizetype idx = 0, last = enumValues.size() - 1; idx <= last; ++idx) { + const auto &enumValue = enumValues.at(idx); + QString line = usedIntType + u'(' + (isAccessible + ? enumValuePrefix + enumValue.name() + : enumValue.value().toString()) + u')'; + if (idx != last) + line += u','; + values.append(line); + } + + const auto len = maxLineLength(values) + 1; + for (qsizetype idx = 0, size = enumValues.size(); idx < size; ++idx) { + const auto &enumValue = enumValues.at(idx).value(); + const char *numberSpace = enumValue.isNegative() ? " " : " "; + s << values.at(idx) << Pad(' ', len - values.at(idx).size()) + << "//" << numberSpace << enumValue.toHex(targetHexLen) + << numberSpace << enumValue.toString() << '\n'; + } + s << "};\n" << outdent; + } + + // Build initialization of anonymous enums + if (cppEnum.enumKind() == AnonymousEnum) { + int idx = 0; + for (const auto &enumValue : enumValues) { + const QString mangledName = mangleName(enumValue.name()); + const QString pyValue = initializerValues + u'[' + QString::number(idx++) + u']'; if (enclosingClass || hasUpperEnclosingClass) { - s << "{\n"; - { - Indentation indent(s); - s << "PyObject *anonEnumItem = PyLong_FromLong(" << enumValueText << ");\n" - << "if (PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(" - << enclosingObjectVariable - << ")->tp_dict, \"" << mangledName << "\", anonEnumItem) < 0)\n"; - { - Indentation indent(s); - s << returnStatement(m_currentErrorCode) << '\n'; - } - s << "Py_DECREF(anonEnumItem);\n"; - } - s << "}\n"; + s << "tpDict.reset(PepType_GetDict(reinterpret_cast<PyTypeObject *>(" + << enclosingObjectVariable << ")));\n" + << "PyDict_SetItemString(tpDict.object(), \"" << mangledName << "\",\n" + << indent << (isSigned ? "PyLong_FromLongLong" : "PyLong_FromUnsignedLongLong") + << "(" << pyValue << "));\n" << outdent; } else { - s << "if (PyModule_AddIntConstant(module, \"" << mangledName << "\", "; - s << enumValueText << ") < 0)\n"; - { - Indentation indent(s); - s << returnStatement(m_currentErrorCode) << '\n'; - } + s << "PyModule_AddObject(module, \"" << mangledName << "\",\n" << indent + << (isSigned ? "PyLong_FromLongLong" : "PyLong_FromUnsignedLongLong") << "(" + << pyValue << "));\n" << outdent; } - break; - case CEnum: { - s << "if (!Shiboken::Enum::"; - s << ((enclosingClass || hasUpperEnclosingClass) ? "createScopedEnumItem" : "createGlobalEnumItem"); - s << '(' << enumVarTypeObj << ',' << '\n'; - Indentation indent(s); - s << enclosingObjectVariable << ", \"" << mangledName << "\", " - << enumValueText << "))\n" - << returnStatement(m_currentErrorCode) << '\n'; - } - break; - case EnumClass: { - s << "if (!Shiboken::Enum::createScopedEnumItem(" - << enumVarTypeObj << ',' << '\n'; - Indentation indent(s); - s << enumVarTypeObj<< ", \"" << mangledName << "\", " - << enumValueText << "))\n" - << returnStatement(m_currentErrorCode) << '\n'; - } - break; } } + bool etypeUsed = false; + + QString enumVarTypeObj = cpythonTypeNameExtSet(enumTypeEntry); + if (!cppEnum.isAnonymous()) { + int packageLevel = packageName().count(u'.') + 1; + s << "EType = Shiboken::Enum::" + << "createPythonEnum" + << '(' << enclosingObjectVariable << ",\n" << indent + << '"' << packageLevel << ':' << getClassTargetFullName(cppEnum) << "\",\n" + << initializerName << ", " << initializerValues << ");\n" << outdent + << enumVarTypeObj << " = EType;\n"; + etypeUsed = true; + } + + if (cppEnum.typeEntry()->flags()) { + s << "// PYSIDE-1735: Mapping the flags class to the same enum class.\n" + << cpythonTypeNameExtSet(cppEnum.typeEntry()->flags()) << " =\n" + << indent << "EType;\n" << outdent; + } writeEnumConverterInitialization(s, cppEnum); s << "// End of '" << cppEnum.name() << "' enum"; if (cppEnum.typeEntry()->flags()) s << "/flags"; s << ".\n\n"; + + return etypeUsed; } -void CppGenerator::writeSignalInitialization(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeSignalInitialization(TextStream &s, const AbstractMetaClassCPtr &metaClass) { // Try to check something and print some warnings const auto &signalFuncs = metaClass->cppSignalFunctions(); @@ -5328,155 +5388,31 @@ void CppGenerator::writeSignalInitialization(TextStream &s, const AbstractMetaCl } } - s << "PySide::Signal::registerSignals(" << cpythonTypeName(metaClass) << ", &::" + s << "PySide::Signal::registerSignals(pyType, &" << m_gsp << metaClass->qualifiedCppName() << "::staticMetaObject);\n"; } -void CppGenerator::writeFlagsToLong(TextStream &s, const AbstractMetaEnum &cppEnum) -{ - FlagsTypeEntry *flagsEntry = cppEnum.typeEntry()->flags(); - if (!flagsEntry) - return; - s << "static PyObject *" << cpythonEnumName(cppEnum) << "_long(PyObject *self)\n" - << "{\n" << indent - << "int val;\n"; - AbstractMetaType flagsType = AbstractMetaType::fromTypeEntry(flagsEntry); - s << cpythonToCppConversionFunction(flagsType) << "self, &val);\n" - << "return Shiboken::Conversions::copyToPython(Shiboken::Conversions::PrimitiveTypeConverter<int>(), &val);\n" - << outdent << "}\n"; -} - -void CppGenerator::writeFlagsNonZero(TextStream &s, const AbstractMetaEnum &cppEnum) -{ - FlagsTypeEntry *flagsEntry = cppEnum.typeEntry()->flags(); - if (!flagsEntry) - return; - s << "static int " << cpythonEnumName(cppEnum) << "__nonzero(PyObject *self)\n"; - s << "{\n" << indent << "int val;\n"; - AbstractMetaType flagsType = AbstractMetaType::fromTypeEntry(flagsEntry); - s << cpythonToCppConversionFunction(flagsType) << "self, &val);\n" - << "return val != 0;\n" - << outdent << "}\n"; -} - -void CppGenerator::writeFlagsMethods(TextStream &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 << '\n'; -} - -void CppGenerator::writeFlagsNumberMethodsDefinition(TextStream &s, const AbstractMetaEnum &cppEnum) -{ - QString cpythonName = cpythonEnumName(cppEnum); - - s << "static PyType_Slot " << cpythonName << "_number_slots[] = {\n" << indent - << "{Py_nb_bool, reinterpret_cast<void *>(" << cpythonName << "__nonzero)},\n" - << "{Py_nb_invert, reinterpret_cast<void *>(" << cpythonName << "___invert__)},\n" - << "{Py_nb_and, reinterpret_cast<void *>(" << cpythonName << "___and__)},\n" - << "{Py_nb_xor, reinterpret_cast<void *>(" << cpythonName << "___xor__)},\n" - << "{Py_nb_or, reinterpret_cast<void *>(" << cpythonName << "___or__)},\n" - << "{Py_nb_int, reinterpret_cast<void *>(" << cpythonName << "_long)},\n" - << "{Py_nb_index, reinterpret_cast<void *>(" << cpythonName << "_long)},\n" - << "{0, " << NULL_PTR << "} // sentinel\n" << outdent - << "};\n\n"; -} - -void CppGenerator::writeFlagsNumberMethodsDefinitions(TextStream &s, - const AbstractMetaEnumList &enums) -{ - for (const AbstractMetaEnum &e : enums) { - if (!e.isAnonymous() && !e.isPrivate() && e.typeEntry()->flags()) { - writeFlagsMethods(s, e); - writeFlagsNumberMethodsDefinition(s, e); - s << '\n'; - } - } -} - -void CppGenerator::writeFlagsBinaryOperator(TextStream &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" << indent; - - AbstractMetaType flagsType = AbstractMetaType::fromTypeEntry(flagsEntry); - s << "::" << flagsEntry->originalName() << " cppResult, " << CPP_SELF_VAR - << ", cppArg;\n" - << CPP_SELF_VAR << " = static_cast<::" << flagsEntry->originalName() - << ">(int(PyLong_AsLong(self)));\n" - // PYSIDE-1436: Need to error check self as well because operators are used - // sometimes with swapped args. - << "if (PyErr_Occurred())\n" << indent - << "return nullptr;\n" << outdent - << "cppArg = static_cast<" << flagsEntry->originalName() - << ">(int(PyLong_AsLong(" << PYTHON_ARG << ")));\n" - << "if (PyErr_Occurred())\n" << indent - << "return nullptr;\n" << outdent - << "cppResult = " << CPP_SELF_VAR << " " << cppOpName << " cppArg;\n" - << "return "; - writeToPythonConversion(s, flagsType, nullptr, QLatin1String("cppResult")); - s << ";\n" << outdent << "}\n\n"; -} - -void CppGenerator::writeFlagsUnaryOperator(TextStream &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" << indent; - - AbstractMetaType flagsType = AbstractMetaType::fromTypeEntry(flagsEntry); - s << "::" << flagsEntry->originalName() << " " << CPP_SELF_VAR << ";\n" - << cpythonToCppConversionFunction(flagsType) << "self, &" << CPP_SELF_VAR - << ");\n"; - if (boolResult) - s << "bool"; - else - s << "::" << flagsEntry->originalName(); - s << " cppResult = " << cppOpName << CPP_SELF_VAR << ";\n" - << "return "; - if (boolResult) - s << "PyBool_FromLong(cppResult)"; - else - writeToPythonConversion(s, flagsType, nullptr, QLatin1String("cppResult")); - s << ";\n" << outdent << "}\n\n"; -} - -QString CppGenerator::getSimpleClassInitFunctionName(const AbstractMetaClass *metaClass) +QString CppGenerator::getSimpleClassInitFunctionName(const AbstractMetaClassCPtr &metaClass) { QString initFunctionName; // Disambiguate namespaces per module to allow for extending them. if (metaClass->isNamespace()) initFunctionName += moduleName(); initFunctionName += metaClass->qualifiedCppName(); - initFunctionName.replace(QLatin1String("::"), QLatin1String("_")); + initFunctionName.replace(u"::"_s, u"_"_s); return initFunctionName; } -QString CppGenerator::getSimpleClassStaticFieldsInitFunctionName(const AbstractMetaClass *metaClass) +QString + CppGenerator::getSimpleClassStaticFieldsInitFunctionName(const AbstractMetaClassCPtr &metaClass) { - return QLatin1String("init_") + getSimpleClassInitFunctionName(metaClass) - + QLatin1String("StaticFields"); + return u"init_"_s + getSimpleClassInitFunctionName(metaClass) + + u"StaticFields"_s; } QString CppGenerator::getInitFunctionName(const GeneratorContext &context) { - return !context.forSmartPointer() - ? getSimpleClassInitFunctionName(context.metaClass()) - : getFilteredCppSignatureString(context.preciseType().cppSignature()); + return getSimpleClassInitFunctionName(context.metaClass()); } void CppGenerator::writeSignatureStrings(TextStream &s, @@ -5490,7 +5426,7 @@ void CppGenerator::writeSignatureStrings(TextStream &s, const auto lines = QStringView{signatures}.split(u'\n', Qt::SkipEmptyParts); for (auto line : lines) { // must anything be escaped? - if (line.contains(QLatin1Char('"')) || line.contains(QLatin1Char('\\'))) + if (line.contains(u'"') || line.contains(u'\\')) s << "R\"CPP(" << line << ")CPP\",\n"; else s << '"' << line << "\",\n"; @@ -5499,13 +5435,13 @@ void CppGenerator::writeSignatureStrings(TextStream &s, } // Return the class name for which to invoke the destructor -QString CppGenerator::destructorClassName(const AbstractMetaClass *metaClass, - const GeneratorContext &classContext) const +QString CppGenerator::destructorClassName(const AbstractMetaClassCPtr &metaClass, + const GeneratorContext &classContext) { if (metaClass->isNamespace() || metaClass->hasPrivateDestructor()) return {}; if (classContext.forSmartPointer()) - return classContext.smartPointerWrapperName(); + return classContext.effectiveClassName(); const bool isValue = metaClass->typeEntry()->isValue(); const bool hasProtectedDestructor = metaClass->hasProtectedDestructor(); if (((avoidProtectedHack() && hasProtectedDestructor) || isValue) @@ -5517,124 +5453,154 @@ QString CppGenerator::destructorClassName(const AbstractMetaClass *metaClass, return metaClass->qualifiedCppName(); } +// Return the base type entries for introduceWrapperType() +static ComplexTypeEntryCList pyBaseTypeEntries(const AbstractMetaClassCPtr &metaClass) +{ + ComplexTypeEntryCList result; + if (metaClass->isNamespace()) { + if (auto extended = metaClass->extendedNamespace()) + result.append(extended->typeEntry()); + return result; + } + + const auto &baseClasses = metaClass->typeSystemBaseClasses(); + for (auto base : baseClasses) { + for (; base != nullptr; base = base->baseClass()) { // Find a type that is not disabled. + const auto ct = base->typeEntry()->codeGeneration(); + if (ct == TypeEntry::GenerateCode || ct == TypeEntry::GenerateForSubclass) + break; + } + result.append(base->typeEntry()); + } + return result; +} + +// Return the base type strings for introduceWrapperType() +QStringList CppGenerator::pyBaseTypes(const AbstractMetaClassCPtr &metaClass) +{ + const auto &baseEntries = pyBaseTypeEntries(metaClass); + QStringList result; + for (const auto &baseEntry : baseEntries) + result.append("reinterpret_cast<PyObject *>("_L1 + cpythonTypeNameExt(baseEntry) + u')'); + if (result.isEmpty()) // no base classes -> SbkObjectType. + result.append(sbkObjectTypeF); + return result; +} + +void CppGenerator::writeInitInheritance(TextStream &s) const +{ + s << "static void " << initInheritanceFunction << "()\n{\n" << indent + << "auto &bm = Shiboken::BindingManager::instance();\n" + << sbkUnusedVariableCast("bm"); + for (const auto &cls : api().classes()){ + auto te = cls->typeEntry(); + if (shouldGenerate(te)) { + const auto &baseEntries = pyBaseTypeEntries(cls); + if (!baseEntries.isEmpty()) { + const QString childTypeInitStruct = typeInitStruct(cls->typeEntry()); + for (const auto &baseEntry : baseEntries) { + s << "bm.addClassInheritance(&" << typeInitStruct(baseEntry) << ",\n" + << Pad(' ', 23) << '&' << childTypeInitStruct << ");\n"; + } + } + } + } + s << outdent << "}\n\n"; +} + void CppGenerator::writeClassRegister(TextStream &s, - const AbstractMetaClass *metaClass, + const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext, const QString &signatures) const { - const ComplexTypeEntry *classTypeEntry = metaClass->typeEntry(); + ComplexTypeEntryCPtr classTypeEntry = metaClass->typeEntry(); - const AbstractMetaClass *enc = metaClass->targetLangEnclosingClass(); - QString enclosingObjectVariable = enc ? QLatin1String("enclosingClass") : QLatin1String("module"); + AbstractMetaClassCPtr enc = metaClass->targetLangEnclosingClass(); + QString enclosingObjectVariable = enc ? u"enclosingClass"_s : u"module"_s; QString pyTypeName = cpythonTypeName(metaClass); QString initFunctionName = getInitFunctionName(classContext); // PYSIDE-510: Create a signatures string for the introspection feature. writeSignatureStrings(s, signatures, initFunctionName, "functions"); - s << "void init_" << initFunctionName; - s << "(PyObject *" << enclosingObjectVariable << ")\n{\n" << indent; + s << "PyTypeObject *init_" << initFunctionName + << "(PyObject *" << enclosingObjectVariable << ")\n{\n" << indent; + + const QString globalTypeVarExpr = !classContext.forSmartPointer() + ? cpythonTypeNameExtSet(classTypeEntry) + : cpythonTypeNameExtSet(classContext.preciseType()); + s << "if (" << globalTypeVarExpr << " != nullptr)\n" << indent + << "return " << globalTypeVarExpr << ";\n\n" << outdent; // Multiple inheritance - QString pyTypeBasesVariable = chopType(pyTypeName) + QLatin1String("_Type_bases"); - const AbstractMetaClassList baseClasses = metaClass->typeSystemBaseClasses(); - if (metaClass->baseClassNames().size() > 1) { - s << "PyObject *" << pyTypeBasesVariable - << " = PyTuple_Pack(" << baseClasses.size() << ',' << '\n'; - Indentation indent(s); - for (int i = 0, size = baseClasses.size(); i < size; ++i) { - if (i) - s << ",\n"; - s << "reinterpret_cast<PyObject *>(" - << cpythonTypeNameExt(baseClasses.at(i)->typeEntry()) << ')'; - } - s << ");\n\n"; + QString pyTypeBasesVariable = chopType(pyTypeName) + u"_Type_bases"_s; + const QStringList pyBases = pyBaseTypes(metaClass); + s << "Shiboken::AutoDecRef " << pyTypeBasesVariable << "(PyTuple_Pack(" + << pyBases.size() << ",\n" << indent; + for (qsizetype i = 0, size = pyBases.size(); i < size; ++i) { + if (i) + s << ",\n"; + s << pyBases.at(i); } + s << "));\n\n" << outdent; // Create type and insert it in the module or enclosing class. - const QString typePtr = QLatin1String("_") + chopType(pyTypeName) - + QLatin1String("_Type"); + const QString typePtr = u"_"_s + chopType(pyTypeName) + + u"_Type"_s; - s << typePtr << " = Shiboken::ObjectType::introduceWrapperType(\n"; - { - Indentation indent(s); - // 1:enclosingObject - s << enclosingObjectVariable << ",\n"; - QString typeName; - if (!classContext.forSmartPointer()) - typeName = metaClass->name(); - else - typeName = classContext.preciseType().cppSignature(); + s << typePtr << " = Shiboken::ObjectType::introduceWrapperType(\n" << indent; + // 1:enclosingObject + s << enclosingObjectVariable << ",\n"; - // 2:typeName - s << "\"" << typeName << "\",\n"; + // 2:typeName + s << "\"" << metaClass->name() << "\",\n"; - // 3:originalName - s << "\""; - if (!classContext.forSmartPointer()) { - s << metaClass->qualifiedCppName(); - if (classTypeEntry->isObject()) - s << '*'; - } else { - s << classContext.preciseType().cppSignature(); - } + // 3:originalName + s << "\""; + if (!classContext.forSmartPointer()) { + s << metaClass->qualifiedCppName(); + if (classTypeEntry->isObject()) + s << '*'; + } else { + s << classContext.preciseType().cppSignature(); + } - s << "\",\n"; - // 4:typeSpec - s << '&' << chopType(pyTypeName) << "_spec,\n"; + s << "\",\n"; + // 4:typeSpec + s << '&' << chopType(pyTypeName) << "_spec,\n"; - // 5:cppObjDtor - QString dtorClassName = destructorClassName(metaClass, classContext); - if (dtorClassName.isEmpty()) - s << "nullptr,\n"; - else - s << "&Shiboken::callCppDestructor< ::" << dtorClassName << " >,\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 << cpythonTypeNameExt(base->typeEntry()) << ",\n"; - } else { - s << "0,\n"; - } + // 5:cppObjDtor + QString dtorClassName = destructorClassName(metaClass, classContext); + if (dtorClassName.isEmpty()) + s << "nullptr,\n"; + else + s << "&Shiboken::callCppDestructor< " << globalScopePrefix(classContext) + << dtorClassName << " >,\n"; + + // 7:baseTypes + s << pyTypeBasesVariable << ".object()," << '\n'; + + // 8:wrapperflags + QByteArrayList wrapperFlags; + if (enc) + wrapperFlags.append(QByteArrayLiteral("Shiboken::ObjectType::WrapperFlags::InnerClass")); + if (metaClass->deleteInMainThread()) + wrapperFlags.append(QByteArrayLiteral("Shiboken::ObjectType::WrapperFlags::DeleteInMainThread")); + if (classTypeEntry->isValue()) + wrapperFlags.append("Shiboken::ObjectType::WrapperFlags::Value"_ba); + if (wrapperFlags.isEmpty()) + s << '0'; + else + s << wrapperFlags.join(" | "); - // 7:baseTypes - if (metaClass->baseClassNames().size() > 1) - s << pyTypeBasesVariable << ',' << '\n'; - else - s << "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 << '0'; - else - s << wrapperFlags.join(" | "); - } - s << ");\nauto pyType = reinterpret_cast<PyTypeObject *>(" << typePtr << ");\n" + s << outdent << ");\nauto *pyType = " << pyTypeName << "; // references " + << typePtr << "\n" << "InitSignatureStrings(pyType, " << initFunctionName << "_SignatureStrings);\n"; - if (usePySideExtensions()) - s << "SbkObjectType_SetPropertyStrings(reinterpret_cast<PyTypeObject *>(" << typePtr << "), " + if (usePySideExtensions() && !classContext.forSmartPointer()) + s << "SbkObjectType_SetPropertyStrings(pyType, " << chopType(pyTypeName) << "_PropertyStrings);\n"; - - if (!classContext.forSmartPointer()) - s << cpythonTypeNameExt(classTypeEntry) << '\n'; - else - s << cpythonTypeNameExt(classContext.preciseType()) << '\n'; - s << " = " << pyTypeName << ";\n\n"; + s << globalTypeVarExpr << " = pyType;\n\n"; // Register conversions for the type. writeConverterRegister(s, metaClass, classContext); @@ -5649,7 +5615,7 @@ void CppGenerator::writeClassRegister(TextStream &s, } // Fill multiple inheritance data, if needed. - const AbstractMetaClass *miClass = getMultipleInheritingClass(metaClass); + const auto miClass = getMultipleInheritingClass(metaClass); if (miClass) { s << "MultipleInheritanceInitFunction func = "; if (miClass == metaClass) { @@ -5665,15 +5631,19 @@ void CppGenerator::writeClassRegister(TextStream &s, } // Set typediscovery struct or fill the struct of another one - if (metaClass->isPolymorphic() && metaClass->baseClass()) { - s << "Shiboken::ObjectType::setTypeDiscoveryFunctionV2(" << cpythonTypeName(metaClass) - << ", &" << cpythonBaseName(metaClass) << "_typeDiscovery);\n\n"; + if (needsTypeDiscoveryFunction(metaClass)) { + s << "Shiboken::ObjectType::setTypeDiscoveryFunctionV2(\n" << indent + << cpythonTypeName(metaClass) + << ", &" << cpythonBaseName(metaClass) << "_typeDiscovery);" << outdent << "\n\n"; } AbstractMetaEnumList classEnums = metaClass->enums(); metaClass->getEnumsFromInvisibleNamespacesToBeGenerated(&classEnums); - ErrorCode errorCode(QString::fromLatin1("")); + if (!classContext.forSmartPointer() && !classEnums.isEmpty()) + s << "// Pass the ..._EnumFlagInfo to the class.\n" + << "SbkObjectType_SetEnumFlagInfo(pyType, " << chopType(pyTypeName) + << "_EnumFlagInfo);\n\n"; writeEnumsInitialization(s, classEnums); if (metaClass->hasSignals()) @@ -5694,26 +5664,40 @@ void CppGenerator::writeClassRegister(TextStream &s, writeInitQtMetaTypeFunctionBody(s, classContext); } - if (usePySideExtensions() && metaClass->isQObject()) { - s << "Shiboken::ObjectType::setSubTypeInitHook(" << pyTypeName - << ", &PySide::initQObjectSubType);\n" - << "PySide::initDynamicMetaObject(" << pyTypeName << ", &::" - << metaClass->qualifiedCppName() << "::staticMetaObject, sizeof("; - if (shouldGenerateCppWrapper(metaClass)) - s << wrapperName(metaClass); - else - s << "::" << metaClass->qualifiedCppName(); - s << "));\n"; + if (usePySideExtensions() && isQObject(metaClass)) { + s << "Shiboken::ObjectType::setSubTypeInitHook(pyType, &PySide::initQObjectSubType);\n" + << "PySide::initDynamicMetaObject(pyType, &" << m_gsp + << metaClass->qualifiedCppName() << "::staticMetaObject, sizeof(" + << (shouldGenerateCppWrapper(metaClass) + ? wrapperName(metaClass) : getFullTypeName(metaClass)) + << "));\n"; } - s << outdent << "}\n"; + s << "\nreturn pyType;\n" << outdent << "}\n"; } -void CppGenerator::writeStaticFieldInitialization(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeStaticFieldInitialization(TextStream &s, + const AbstractMetaClassCPtr &metaClass) { - s << "\nvoid " << getSimpleClassStaticFieldsInitFunctionName(metaClass) - << "()\n{\n" << indent << "auto dict = reinterpret_cast<PyTypeObject *>(" - << cpythonTypeName(metaClass) << ")->tp_dict;\n"; + // cpythonTypeName == "Sbk_QRhiShaderResourceBinding_Data_TypeF" + QString name = cpythonTypeName(metaClass); + const auto parts = QStringView{name}.split(u'_', Qt::SkipEmptyParts); + if (parts.size() < 4) { + s << "\nPyTypeObject *" << getSimpleClassStaticFieldsInitFunctionName(metaClass) + << "(PyObject *module)\n{\n" << indent + << "auto *obType = PyObject_GetAttrString(module, \"" << metaClass->name() << "\");\n" + << "auto *type = reinterpret_cast<PyTypeObject *>(obType);\n" + << "Shiboken::AutoDecRef dict(PepType_GetDict(type));\n"; + } else { + s << "\nPyTypeObject *" << getSimpleClassStaticFieldsInitFunctionName(metaClass) + << "(PyObject *module)\n{\n" << indent + << "auto *obContainerType = PyObject_GetAttrString(module, \"" + << parts.at(1) << "\");\n" + << "auto *obType = PyObject_GetAttrString(obContainerType, \"" + << parts.at(2) << "\");\n" + << "auto *type = reinterpret_cast<PyTypeObject *>(obType);\n" + << "Shiboken::AutoDecRef dict(PepType_GetDict(type));\n"; + } for (const AbstractMetaField &field : metaClass->fields()) { if (field.isStatic()) { s << "PyDict_SetItemString(dict, \"" << field.name() @@ -5722,12 +5706,59 @@ void CppGenerator::writeStaticFieldInitialization(TextStream &s, const AbstractM s << ");\n"; } } - s << '\n' << outdent << "}\n"; + s << "return type;\n" << outdent << "}\n"; +} + +enum class QtRegisterMetaType +{ + None, Pointer, Value +}; + +static bool hasQtMetaTypeRegistrationSpec(const AbstractMetaClassCPtr &c) +{ + return c->typeEntry()->qtMetaTypeRegistration() != + TypeSystem::QtMetaTypeRegistration::Unspecified; +} + +// Returns if and how to register the Qt meta type. By default, "pointer" for +// non-QObject object types and "value" for non-abstract, default-constructible +// value types. +QtRegisterMetaType qtMetaTypeRegistration(const AbstractMetaClassCPtr &c) +{ + if (c->isNamespace()) + return QtRegisterMetaType::None; + + // Specified in type system? + const bool isObject = c->isObjectType(); + switch (c->typeEntry()->qtMetaTypeRegistration()) { + case TypeSystem::QtMetaTypeRegistration::Disabled: + return QtRegisterMetaType::None; + case TypeSystem::QtMetaTypeRegistration::Enabled: + case TypeSystem::QtMetaTypeRegistration::BaseEnabled: + return isObject ? QtRegisterMetaType::Pointer : QtRegisterMetaType::Value; + case TypeSystem::QtMetaTypeRegistration::Unspecified: + break; + } + + // Is there a "base" specification in some base class, meaning only the + // base class is to be registered? + if (auto base = recurseClassHierarchy(c, hasQtMetaTypeRegistrationSpec)) { + const auto baseSpec = base->typeEntry()->qtMetaTypeRegistration(); + if (baseSpec == TypeSystem::QtMetaTypeRegistration::BaseEnabled) + return QtRegisterMetaType::None; + } + + // Default. + if (isObject) + return isQObject(c) ? QtRegisterMetaType::None : QtRegisterMetaType::Pointer; + + return !c->isAbstract() && c->isDefaultConstructible() + ? QtRegisterMetaType::Value : QtRegisterMetaType::None; } void CppGenerator::writeInitQtMetaTypeFunctionBody(TextStream &s, const GeneratorContext &context) { - const AbstractMetaClass *metaClass = context.metaClass(); + const auto metaClass = context.metaClass(); // Gets all class name variants used on different possible scopes QStringList nameVariants; if (!context.forSmartPointer()) @@ -5735,10 +5766,10 @@ void CppGenerator::writeInitQtMetaTypeFunctionBody(TextStream &s, const Generato else nameVariants << context.preciseType().cppSignature(); - const AbstractMetaClass *enclosingClass = metaClass->enclosingClass(); + AbstractMetaClassCPtr enclosingClass = metaClass->enclosingClass(); while (enclosingClass) { if (enclosingClass->typeEntry()->generateCode()) - nameVariants << (enclosingClass->name() + QLatin1String("::") + nameVariants.constLast()); + nameVariants << (enclosingClass->name() + u"::"_s + nameVariants.constLast()); enclosingClass = enclosingClass->enclosingClass(); } @@ -5748,69 +5779,74 @@ void CppGenerator::writeInitQtMetaTypeFunctionBody(TextStream &s, const Generato 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 (!metaClass->isObjectType()) { - // check if there's a empty ctor - for (const auto &func : metaClass->functions()) { - if (func->isConstructor() && !func->arguments().count()) { - canBeValue = true; - break; - } - } - } - - if (canBeValue) { - for (const QString &name : qAsConst(nameVariants)) { - s << "qRegisterMetaType< ::" << className << " >(\"" << name << "\");\n"; - } - } + // Register meta types for signal/slot connections to work + // Qt metatypes are registered only on their first use, so we do this now. + switch (qtMetaTypeRegistration(metaClass)) { + case QtRegisterMetaType::None: + break; + case QtRegisterMetaType::Pointer: + s << "qRegisterMetaType< " << m_gsp << className << " *>();\n"; + break; + case QtRegisterMetaType::Value: + for (const QString &name : std::as_const(nameVariants)) + s << "qRegisterMetaType< " << m_gsp << className << " >(\"" << name << "\");\n"; + break; } for (const AbstractMetaEnum &metaEnum : metaClass->enums()) { if (!metaEnum.isPrivate() && !metaEnum.isAnonymous()) { - for (const QString &name : qAsConst(nameVariants)) { - s << "qRegisterMetaType< ::" + for (const QString &name : std::as_const(nameVariants)) { + s << "qRegisterMetaType< " << m_gsp << metaEnum.typeEntry()->qualifiedCppName() << " >(\"" << name << "::" << metaEnum.name() << "\");\n"; } - if (metaEnum.typeEntry()->flags()) { - QString n = metaEnum.typeEntry()->flags()->originalName(); - s << "qRegisterMetaType< ::" << n << " >(\"" << n << "\");\n"; - } } } } -void CppGenerator::writeTypeDiscoveryFunction(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::replacePolymorphicIdPlaceHolders(const AbstractMetaClassCPtr &metaClass, + QString *id) +{ + if (id->contains("%1"_L1)) { + QString replacement = " reinterpret_cast< "_L1 + m_gsp + metaClass->qualifiedCppName() + + " *>(cptr)"_L1; + id->replace("%1"_L1, replacement); + } + if (id->contains("%B"_L1)) { + auto baseClass = metaClass; + while (!baseClass->typeEntry()->isPolymorphicBase() && baseClass->baseClass()) + baseClass = baseClass->baseClass(); + QString replacement = " reinterpret_cast< "_L1 + m_gsp + baseClass->qualifiedCppName() + + " *>(cptr)"_L1; + id->replace("%B"_L1, replacement); + } +} + +void CppGenerator::writeTypeDiscoveryFunction(TextStream &s, + const AbstractMetaClassCPtr &metaClass) { QString polymorphicExpr = metaClass->typeEntry()->polymorphicIdValue(); s << "static void *" << cpythonBaseName(metaClass) - << "_typeDiscovery(void *cptr, PyTypeObject *instanceType)\n{\n" << indent; + << "_typeDiscovery(void *cptr, PyTypeObject *instanceType)\n{\n" << indent + << sbkUnusedVariableCast("cptr") + << sbkUnusedVariableCast("instanceType"); if (!polymorphicExpr.isEmpty()) { - polymorphicExpr = polymorphicExpr.replace(QLatin1String("%1"), - QLatin1String(" reinterpret_cast< ::") - + metaClass->qualifiedCppName() - + QLatin1String(" *>(cptr)")); - s << " if (" << polymorphicExpr << ")\n"; - { - Indentation indent(s); - s << "return cptr;\n"; - } + replacePolymorphicIdPlaceHolders(metaClass, &polymorphicExpr); + s << "if (" << polymorphicExpr << ")\n" << indent + << "return cptr;\n" << outdent; } else if (metaClass->isPolymorphic()) { - const AbstractMetaClassList &ancestors = metaClass->allTypeSystemAncestors(); - for (AbstractMetaClass *ancestor : ancestors) { - if (ancestor->baseClass()) + const auto &ancestors = metaClass->allTypeSystemAncestors(); + for (const auto &ancestor : ancestors) { + if (ancestor->baseClass() && !ancestor->typeEntry()->isPolymorphicBase()) continue; if (ancestor->isPolymorphic()) { - s << "if (instanceType == Shiboken::SbkType< ::" - << ancestor->qualifiedCppName() << " >())\n"; - Indentation indent(s); - s << "return dynamic_cast< ::" << metaClass->qualifiedCppName() - << " *>(reinterpret_cast< ::"<< ancestor->qualifiedCppName() << " *>(cptr));\n"; + s << "if (instanceType == Shiboken::SbkType< " << m_gsp + << ancestor->qualifiedCppName() << " >())\n" << indent + << "return dynamic_cast< " << getFullTypeName(metaClass) + << " *>(reinterpret_cast< "<< getFullTypeName(ancestor) + << " *>(cptr));\n" << outdent; } else { qCWarning(lcShiboken).noquote().nospace() << metaClass->qualifiedCppName() << " inherits from a non polymorphic type (" @@ -5823,7 +5859,8 @@ void CppGenerator::writeTypeDiscoveryFunction(TextStream &s, const AbstractMetaC s << "return {};\n" << outdent << "}\n\n"; } -void CppGenerator::writeSetattroDefinition(TextStream &s, const AbstractMetaClass *metaClass) const +void CppGenerator::writeSetattroDefinition(TextStream &s, + const AbstractMetaClassCPtr &metaClass) { s << "static int " << ShibokenGenerator::cpythonSetattroFunctionName(metaClass) << "(PyObject *self, PyObject *name, PyObject *value)\n{\n" << indent; @@ -5833,7 +5870,7 @@ void CppGenerator::writeSetattroDefinition(TextStream &s, const AbstractMetaClas } } -inline void CppGenerator::writeSetattroDefaultReturn(TextStream &s) +void CppGenerator::writeSetattroDefaultReturn(TextStream &s) { s << "return PyObject_GenericSetAttr(self, name, value);\n" << outdent << "}\n\n"; @@ -5843,7 +5880,7 @@ void CppGenerator::writeSetattroFunction(TextStream &s, AttroCheck attroCheck, const GeneratorContext &context) const { Q_ASSERT(!context.forSmartPointer()); - const AbstractMetaClass *metaClass = context.metaClass(); + const auto metaClass = context.metaClass(); writeSetattroDefinition(s, metaClass); // PYSIDE-1019: Switch tp_dict before doing tp_setattro. @@ -5853,62 +5890,36 @@ void CppGenerator::writeSetattroFunction(TextStream &s, AttroCheck attroCheck, // PYSIDE-803: Detect duck-punching; clear cache if a method is set. if (attroCheck.testFlag(AttroCheckFlag::SetattroMethodOverride) && context.useWrapper()) { - s << "if (value && PyCallable_Check(value)) {\n" << indent - << "auto plain_inst = " << cpythonWrapperCPtr(metaClass, QLatin1String("self")) << ";\n" - << "auto inst = dynamic_cast<" << context.wrapperName() << " *>(plain_inst);\n" - << "if (inst)\n" << indent + s << "if (value != nullptr && PyCallable_Check(value) != 0) {\n" << indent + << "auto plain_inst = " << cpythonWrapperCPtr(metaClass, PYTHON_SELF_VAR) << ";\n" + << "auto *inst = dynamic_cast<" << context.wrapperName() << " *>(plain_inst);\n" + << "if (inst != nullptr)\n" << indent << "inst->resetPyMethodCache();\n" << outdent << outdent << "}\n"; } if (attroCheck.testFlag(AttroCheckFlag::SetattroQObject)) { s << "Shiboken::AutoDecRef pp(reinterpret_cast<PyObject *>(PySide::Property::getObject(self, name)));\n" - << "if (!pp.isNull())\n"; - Indentation indent(s); - s << "return PySide::Property::setValue(reinterpret_cast<PySideProperty *>(pp.object()), self, value);\n"; + << "if (!pp.isNull())\n" << indent + << "return PySide::Property::setValue(reinterpret_cast<PySideProperty *>(pp.object()), self, value);\n" + << outdent; } if (attroCheck.testFlag(AttroCheckFlag::SetattroUser)) { auto func = AbstractMetaClass::queryFirstFunction(metaClass->functions(), FunctionQueryOption::SetAttroFunction); Q_ASSERT(func); - s << "{\n"; - { - Indentation indent(s); - s << "auto " << CPP_SELF_VAR << " = " - << cpythonWrapperCPtr(metaClass, QLatin1String("self")) << ";\n"; - writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny, - TypeSystem::TargetLangCode, context); - } - s << "}\n"; + s << "{\n" << indent + << "auto " << CPP_SELF_VAR << " = " + << cpythonWrapperCPtr(metaClass, PYTHON_SELF_VAR) << ";\n"; + writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny, + TypeSystem::TargetLangCode, context); + s << outdent << "}\n"; } writeSetattroDefaultReturn(s); } -void CppGenerator::writeSmartPointerSetattroFunction(TextStream &s, - const GeneratorContext &context) const -{ - Q_ASSERT(context.forSmartPointer()); - writeSetattroDefinition(s, context.metaClass()); - s << "// Try to find the 'name' attribute, by retrieving the PyObject for the corresponding C++ object held by the smart pointer.\n" - << "PyObject *rawObj = PyObject_CallMethod(self, " - << SMART_POINTER_GETTER << ", 0);\n"; - s << "if (rawObj) {\n"; - { - Indentation indent(s); - s << "int hasAttribute = PyObject_HasAttr(rawObj, name);\n" - << "if (hasAttribute) {\n"; - { - Indentation indent(s); - s << "return PyObject_GenericSetAttr(rawObj, name, value);\n"; - } - s << "}\nPy_DECREF(rawObj);\n"; - } - s << "}\n"; - writeSetattroDefaultReturn(s); -} - -void CppGenerator::writeGetattroDefinition(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeGetattroDefinition(TextStream &s, const AbstractMetaClassCPtr &metaClass) { s << "static PyObject *" << cpythonGetattroFunctionName(metaClass) << "(PyObject *self, PyObject *name)\n{\n" << indent; @@ -5918,11 +5929,11 @@ QString CppGenerator::qObjectGetAttroFunction() const { static QString result; if (result.isEmpty()) { - auto qobjectClass = AbstractMetaClass::findClass(api().classes(), qObjectT()); + auto qobjectClass = AbstractMetaClass::findClass(api().classes(), qObjectT); Q_ASSERT(qobjectClass); - result = QLatin1String("PySide::getMetaDataFromQObject(") - + cpythonWrapperCPtr(qobjectClass, QLatin1String("self")) - + QLatin1String(", self, name)"); + result = u"PySide::getHiddenDataFromQObject("_s + + cpythonWrapperCPtr(qobjectClass, PYTHON_SELF_VAR) + + u", self, name)"_s; } return result; } @@ -5931,68 +5942,49 @@ void CppGenerator::writeGetattroFunction(TextStream &s, AttroCheck attroCheck, const GeneratorContext &context) const { Q_ASSERT(!context.forSmartPointer()); - const AbstractMetaClass *metaClass = context.metaClass(); + const auto metaClass = context.metaClass(); writeGetattroDefinition(s, metaClass); // PYSIDE-1019: Switch tp_dict before doing tp_getattro. if (usePySideExtensions()) s << "PySide::Feature::Select(self);\n"; - const QString getattrFunc = usePySideExtensions() && metaClass->isQObject() - ? qObjectGetAttroFunction() : QLatin1String("PyObject_GenericGetAttr(self, name)"); + const QString getattrFunc = usePySideExtensions() && isQObject(metaClass) + ? qObjectGetAttroFunction() : u"PyObject_GenericGetAttr(self, name)"_s; if (attroCheck.testFlag(AttroCheckFlag::GetattroOverloads)) { s << "// Search the method in the instance dict\n" - << "if (auto ob_dict = reinterpret_cast<SbkObject *>(self)->ob_dict) {\n"; - { - Indentation indent(s); - s << "if (auto meth = PyDict_GetItem(ob_dict, name)) {\n"; - { - Indentation indent(s); - s << "Py_INCREF(meth);\n" - << "return meth;\n"; - } - s << "}\n"; - } - s << "}\n" + << "auto *ob_dict = SbkObject_GetDict_NoRef(self);\n"; + s << "if (auto *meth = PyDict_GetItem(ob_dict, name)) {\n" << indent + << "Py_INCREF(meth);\nreturn meth;\n" << outdent << "}\n" << "// Search the method in the type dict\n" - << "if (Shiboken::Object::isUserType(self)) {\n"; - { - Indentation indent(s); - // PYSIDE-772: Perform optimized name mangling. - s << "Shiboken::AutoDecRef tmp(_Pep_PrivateMangle(self, name));\n" - << "if (auto meth = PyDict_GetItem(Py_TYPE(self)->tp_dict, tmp)) {\n"; - { - Indentation indent(s); - // PYSIDE-1523: PyFunction_Check is not accepting compiled functions. - s << "if (strcmp(Py_TYPE(meth)->tp_name, \"compiled_function\") == 0)\n"; - { - Indentation indent(s); - s << "return Py_TYPE(meth)->tp_descr_get(meth, self, nullptr);\n"; - } - s << "return PyFunction_Check(meth) ? PyMethod_New(meth, self)\n" - << " : " << getattrFunc << ";\n"; - } - s << "}\n"; - } - s << "}\n"; + << "if (Shiboken::Object::isUserType(self)) {\n" << indent; + // PYSIDE-772: Perform optimized name mangling. + s << "Shiboken::AutoDecRef tmp(_Pep_PrivateMangle(self, name));\n" + << "Shiboken::AutoDecRef tpDict(PepType_GetDict(Py_TYPE(self)));\n" + << "if (auto *meth = PyDict_GetItem(tpDict.object(), tmp)) {\n" << indent; + // PYSIDE-1523: PyFunction_Check is not accepting compiled functions. + s << "if (std::strcmp(Py_TYPE(meth)->tp_name, \"compiled_function\") == 0) {\n" << indent + << "auto descrGetFunc = " + << pyTypeGetSlot("descrgetfunc", "Py_TYPE(meth)", "Py_tp_descr_get") << ";\n" + << "return descrGetFunc(meth, self, nullptr);\n" << outdent + << "}\nreturn PyFunction_Check(meth) ? PyMethod_New(meth, self)\n" + << " : " << getattrFunc << ";\n" << outdent + << "}\n" << outdent << "}\n"; const auto &funcs = getMethodsWithBothStaticAndNonStaticMethods(metaClass); for (const auto &func : funcs) { QString defName = cpythonMethodDefinitionName(func); - s << "static PyMethodDef non_static_" << defName << " = {\n"; - { - Indentation indent(s); - s << defName << ".ml_name,\n" - << defName << ".ml_meth,\n" - << defName << ".ml_flags & (~METH_STATIC),\n" - << defName << ".ml_doc,\n"; - } - s << "};\n" + s << "static PyMethodDef non_static_" << defName << " = {\n" << indent + << defName << ".ml_name,\n" + << defName << ".ml_meth,\n" + << defName << ".ml_flags & (~METH_STATIC),\n" + << defName << ".ml_doc,\n" << outdent + << "};\n" << "if (Shiboken::String::compare(name, \"" - << func->definitionNames().constFirst() << "\") == 0)\n"; - Indentation indent(s); - s << "return PyCFunction_NewEx(&non_static_" << defName << ", self, 0);\n"; + << func->definitionNames().constFirst() << "\") == 0)\n" << indent + << "return PyCFunction_NewEx(&non_static_" << defName << ", self, 0);\n" + << outdent; } } @@ -6000,93 +5992,104 @@ void CppGenerator::writeGetattroFunction(TextStream &s, AttroCheck attroCheck, auto func = AbstractMetaClass::queryFirstFunction(metaClass->functions(), FunctionQueryOption::GetAttroFunction); Q_ASSERT(func); - s << "{\n"; - { - Indentation indent(s); - s << "auto " << CPP_SELF_VAR << " = " - << cpythonWrapperCPtr(metaClass, QLatin1String("self")) << ";\n"; - writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny, - TypeSystem::TargetLangCode, context); - } - s << "}\n"; + s << "{\n" << indent + << "auto " << CPP_SELF_VAR << " = " + << cpythonWrapperCPtr(metaClass, PYTHON_SELF_VAR) << ";\n"; + writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny, + TypeSystem::TargetLangCode, context); + s << outdent << "}\n"; } s << "return " << getattrFunc << ";\n" << outdent << "}\n\n"; } -void CppGenerator::writeSmartPointerGetattroFunction(TextStream &s, const GeneratorContext &context) +void CppGenerator::writeNbBoolExpression(TextStream &s, const BoolCastFunction &f, + bool invert) { - Q_ASSERT(context.forSmartPointer()); - const AbstractMetaClass *metaClass = context.metaClass(); - writeGetattroDefinition(s, metaClass); - s << "PyObject *tmp = PyObject_GenericGetAttr(self, name);\n" - << "if (tmp)\n"; - { - Indentation indent(s); - s << "return tmp;\n"; - } - s << "if (!PyErr_ExceptionMatches(PyExc_AttributeError))\n"; - { - Indentation indent(s); - s << "return nullptr;\n"; + if (f.function->isOperatorBool()) { + if (invert) + s << '!'; + s << '*' << CPP_SELF_VAR; + return; } - s << "PyErr_Clear();\n"; + if (invert != f.invert) + s << '!'; + s << CPP_SELF_VAR << "->" << f.function->name() << "()"; +} - // This generates the code which dispatches access to member functions - // and fields from the smart pointer to its pointee. - s << "// Try to find the 'name' attribute, by retrieving the PyObject for " - "the corresponding C++ object held by the smart pointer.\n" - << "if (auto rawObj = PyObject_CallMethod(self, " - << SMART_POINTER_GETTER << ", 0)) {\n"; - { - Indentation indent(s); - s << "if (auto attribute = PyObject_GetAttr(rawObj, name))\n"; - { - Indentation indent(s); - s << "tmp = attribute;\n"; - } - s << "Py_DECREF(rawObj);\n"; - } - s << "}\n" - << "if (!tmp) {\n"; - { - Indentation indent(s); - s << R"(PyTypeObject *tp = Py_TYPE(self); -PyErr_Format(PyExc_AttributeError, - "'%.50s' object has no attribute '%.400s'", - tp->tp_name, Shiboken::String::toCString(name)); -)"; - } - s << "}\n" - << "return tmp;\n" << outdent << "}\n\n"; +void CppGenerator::writeNbBoolFunction(const GeneratorContext &context, + const BoolCastFunction &f, + TextStream &s) +{ + s << "static int " << cpythonBaseName(context.metaClass()) << "___nb_bool(PyObject *self)\n" + << "{\n" << indent; + writeCppSelfDefinition(s, context, ErrorReturn::MinusOne); + + const bool allowThread = f.function->allowThread(); + if (allowThread) + s << "int result;\n" << BEGIN_ALLOW_THREADS << "\nresult = "; + else + s << "return "; + + writeNbBoolExpression(s, f); + s << " ? 1 : 0;\n"; + + if (allowThread) + s << END_ALLOW_THREADS << "\nreturn result;\n"; + s << outdent << "}\n\n"; } // Write declaration and invocation of the init function for the module init // function. void CppGenerator::writeInitFunc(TextStream &declStr, TextStream &callStr, const QString &initFunctionName, - const TypeEntry *enclosingEntry) + const TypeEntryCPtr &enclosingEntry, + const QString &pythonName, bool lazy) { - const bool hasParent = - enclosingEntry && enclosingEntry->type() != TypeEntry::TypeSystemType; - declStr << "void init_" << initFunctionName << "(PyObject *" + const QString functionName = "init_"_L1 + initFunctionName; + const bool hasParent = enclosingEntry && enclosingEntry->type() != TypeEntry::TypeSystemType; + declStr << "PyTypeObject *" << functionName << "(PyObject *" << (hasParent ? "enclosingClass" : "module") << ");\n"; - callStr << "init_" << initFunctionName; - if (hasParent) { - callStr << "(reinterpret_cast<PyTypeObject *>(" - << cpythonTypeNameExt(enclosingEntry) << ")->tp_dict);\n"; + + if (!lazy) { + const QString enclosing = hasParent + ? "reinterpret_cast<PyObject *>("_L1 + cpythonTypeNameExt(enclosingEntry) + u')' + : "module"_L1; + callStr << functionName << '(' << enclosing << ");\n"; + } else if (hasParent) { + const QString &enclosingName = enclosingEntry->name(); + const auto parts = QStringView{enclosingName}.split(u"::", Qt::SkipEmptyParts); + const QString namePathPrefix = enclosingEntry->name().replace("::"_L1, "."_L1); + callStr << "Shiboken::Module::AddTypeCreationFunction(" + << "module, \"" << parts[0] << "\", " + << functionName << ", \"" << namePathPrefix << '.' << pythonName << "\");\n"; } else { - callStr << "(module);\n"; + callStr << "Shiboken::Module::AddTypeCreationFunction(" + << "module, \"" << pythonName << "\", " + << functionName << ");\n"; } } +static void writeSubModuleHandling(TextStream &s, const QString &moduleName, + const QString &subModuleOf) +{ + s << "{\n" << indent + << "Shiboken::AutoDecRef parentModule(Shiboken::Module::import(\"" + << subModuleOf << "\"));\n" + << "if (parentModule.isNull())\n" << indent + << "return nullptr;\n" << outdent + << "if (PyModule_AddObject(parentModule.object(), \"" << moduleName + << "\", module) < 0)\n" + << indent << "return nullptr;\n" << outdent << outdent << "}\n"; +} + bool CppGenerator::finishGeneration() { //Generate CPython wrapper file StringStream s_classInitDecl(TextStream::Language::Cpp); StringStream s_classPythonDefines(TextStream::Language::Cpp); - QSet<Include> includes; + std::set<Include> includes; StringStream s_globalFunctionImpl(TextStream::Language::Cpp); StringStream s_globalFunctionDef(TextStream::Language::Cpp); StringStream signatureStream(TextStream::Language::Cpp); @@ -6095,8 +6098,8 @@ bool CppGenerator::finishGeneration() for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) { const AbstractMetaFunctionCList &overloads = it.value(); for (const auto &func : overloads) { - if (func->typeEntry()) - includes << func->typeEntry()->include(); + if (auto te = func->typeEntry()) + includes.insert(te->include()); } if (overloads.isEmpty()) @@ -6108,34 +6111,69 @@ bool CppGenerator::finishGeneration() writeMethodWrapper(s_globalFunctionImpl, overloadData, classContext); writeSignatureInfo(signatureStream, overloadData); - writeMethodDefinition(s_globalFunctionDef, overloadData); + s_globalFunctionDef << methodDefinitionEntries(overloadData); } AbstractMetaClassCList classesWithStaticFields; - for (auto cls : api().classes()){ - if (shouldGenerate(cls)) { + for (const auto &cls : api().classes()){ + auto te = cls->typeEntry(); + if (shouldGenerate(te)) { + const bool hasConfigCondition = te->hasConfigCondition(); + if (hasConfigCondition) { + s_classInitDecl << te->configCondition() << '\n'; + s_classPythonDefines << te->configCondition() << '\n'; + } writeInitFunc(s_classInitDecl, s_classPythonDefines, getSimpleClassInitFunctionName(cls), - cls->typeEntry()->targetLangEnclosingEntry()); + targetLangEnclosingEntry(te), cls->name()); if (cls->hasStaticFields()) { - s_classInitDecl << "void " - << getSimpleClassStaticFieldsInitFunctionName(cls) << "();\n"; + s_classInitDecl << "PyTypeObject *" + << getSimpleClassStaticFieldsInitFunctionName(cls) << "(PyObject *module);\n"; classesWithStaticFields.append(cls); } + if (hasConfigCondition) { + s_classInitDecl << "#endif\n"; + s_classPythonDefines << "#endif\n"; + } } } // Initialize smart pointer types. - const auto &smartPtrs = instantiatedSmartPointers(); - for (const AbstractMetaType &metaType : smartPtrs) { - GeneratorContext context = contextForSmartPointer(nullptr, metaType); + for (const auto &smp : api().instantiatedSmartPointers()) { + GeneratorContext context = contextForSmartPointer(smp.specialized, smp.type); + const auto enclosingClass = context.metaClass()->enclosingClass(); + auto enclosingTypeEntry = enclosingClass + ? enclosingClass->typeEntry() + : targetLangEnclosingEntry(smp.type.typeEntry()); + writeInitFunc(s_classInitDecl, s_classPythonDefines, getInitFunctionName(context), - metaType.typeEntry()->targetLangEnclosingEntry()); + enclosingTypeEntry, smp.type.name()); + includes.insert(smp.type.instantiations().constFirst().typeEntry()->include()); + } + + for (auto &instantiatedContainer : api().instantiatedContainers()) { + includes.insert(instantiatedContainer.typeEntry()->include()); + for (const auto &inst : instantiatedContainer.instantiations()) + includes.insert(inst.typeEntry()->include()); + } + + const ExtendedConverterData extendedConverters = getExtendedConverters(); + for (auto it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) { + TypeEntryCPtr te = it.key(); + includes.insert(te->include()); + for (const auto &metaClass : it.value()) + includes.insert(metaClass->typeEntry()->include()); + } + + const QList<CustomConversionPtr> &typeConversions = getPrimitiveCustomConversions(); + for (const auto &c : typeConversions) { + if (auto te = c->ownerType()) + includes.insert(te->include()); } - QString moduleFileName(outputDirectory() + QLatin1Char('/') + subDirectoryForPackage(packageName())); - moduleFileName += QLatin1Char('/') + moduleName().toLower() + QLatin1String("_module_wrapper.cpp"); + QString moduleFileName(outputDirectory() + u'/' + subDirectoryForPackage(packageName())); + moduleFileName += u'/' + moduleName().toLower() + u"_module_wrapper.cpp"_s; FileOut file(moduleFileName); @@ -6149,60 +6187,78 @@ bool CppGenerator::finishGeneration() #include <algorithm> #include <signature.h> )"; + + if (!api().instantiatedContainers().isEmpty()) + s << "#include <sbkcontainer.h>\n#include <sbkstaticstrings.h>\n"; + if (usePySideExtensions()) { s << includeQDebug; - s << R"(#include <pyside.h> + s << R"(#include <pysidecleanup.h> #include <pysideqenum.h> #include <feature_select.h> +#include <pysidestaticstrings.h> )"; } s << "#include \"" << getModuleHeaderFileName() << '"' << "\n\n"; - for (const Include &include : qAsConst(includes)) + for (const Include &include : includes) s << include; s << '\n'; // Global enums AbstractMetaEnumList globalEnums = api().globalEnums(); - for (const AbstractMetaClass *nsp : invisibleTopNamespaces()) + for (const auto &nsp : invisibleTopNamespaces()) { + const auto oldSize = globalEnums.size(); nsp->getEnumsToBeGenerated(&globalEnums); + if (globalEnums.size() > oldSize) + s << nsp->typeEntry()->include(); + } TypeDatabase *typeDb = TypeDatabase::instance(); - const TypeSystemTypeEntry *moduleEntry = typeDb->defaultTypeSystemType(); + TypeSystemTypeEntryCPtr moduleEntry = typeDb->defaultTypeSystemType(); Q_ASSERT(moduleEntry); - //Extra includes - s << '\n' << "// Extra includes\n"; + s << '\n'; + // Extra includes QList<Include> extraIncludes = moduleEntry->extraIncludes(); - for (const AbstractMetaEnum &cppEnum : qAsConst(globalEnums)) + for (const AbstractMetaEnum &cppEnum : std::as_const(globalEnums)) extraIncludes.append(cppEnum.typeEntry()->extraIncludes()); - std::sort(extraIncludes.begin(), extraIncludes.end()); - for (const Include &inc : qAsConst(extraIncludes)) - s << inc; - s << '\n' - << "// Current module's type array.\n" - << "PyTypeObject **" << cppApiVariableName() << " = nullptr;\n" + if (!extraIncludes.isEmpty()) { + s << "// Extra includes\n"; + std::sort(extraIncludes.begin(), extraIncludes.end()); + for (const Include &inc : std::as_const(extraIncludes)) + s << inc; + s << '\n'; + } + + // FIXME PYSIDE-7: Remove backwards compatible structure + s << "// Current module's type array.\n" + << "Shiboken::Module::TypeInitStruct *" << cppApiVariableName() << " = nullptr;\n" + << "// Backwards compatible structure with identical indexing.\n" + << "PyTypeObject **" << cppApiVariableNameOld() << " = nullptr;\n" << "// Current module's PyObject pointer.\n" << "PyObject *" << pythonModuleObjectName() << " = nullptr;\n" << "// Current module's converter array.\n" - << "SbkConverter **" << convertersVariableName() << " = nullptr;\n"; + << "SbkConverter **" << convertersVariableName() << " = nullptr;\n\n"; const CodeSnipList snips = moduleEntry->codeSnips(); // module inject-code native/beginning - if (!snips.isEmpty()) - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode); + writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode); // cleanup staticMetaObject attribute if (usePySideExtensions()) { + QString iType = cppApiVariableName() + "[i].type"_L1; + QString iName = cppApiVariableName() + "[i].fullName"_L1; + s << "void cleanTypesAttributes() {\n" << indent - << "for (int i = 0, imax = SBK_" << moduleName() - << "_IDX_COUNT; i < imax; i++) {\n" << indent - << "PyObject *pyType = reinterpret_cast<PyObject *>(" << cppApiVariableName() << "[i]);\n" - << "Shiboken::AutoDecRef attrName(Py_BuildValue(\"s\", \"staticMetaObject\"));\n" - << "if (pyType && PyObject_HasAttr(pyType, attrName))\n" << indent + << "static PyObject *attrName = Shiboken::PyName::qtStaticMetaObject();\n" + << "const int imax = SBK_" << moduleName() << "_IDX_COUNT;\n" + << "for (int i = 0; i < imax && " << iName << " != nullptr; ++i) {\n" << indent + << "auto *pyType = reinterpret_cast<PyObject *>(" << iType << ");\n" + << "if (pyType != nullptr && PyObject_HasAttr(pyType, attrName))\n" << indent << "PyObject_SetAttr(pyType, attrName, Py_None);\n" << outdent - << outdent << "}\n" << outdent << "}\n"; + << outdent << "}\n" << outdent << "}\n\n"; } s << "// Global functions " @@ -6210,7 +6266,7 @@ bool CppGenerator::finishGeneration() << s_globalFunctionImpl.toString() << '\n' << "static PyMethodDef " << moduleName() << "_methods[] = {\n" << indent << s_globalFunctionDef.toString() - << "{0} // Sentinel\n" << outdent << "};\n\n" + << METHOD_DEF_SENTINEL << outdent << "};\n\n" << "// Classes initialization functions " << "------------------------------------------------------------\n" << s_classInitDecl.toString() << '\n'; @@ -6220,12 +6276,8 @@ bool CppGenerator::finishGeneration() s << "// Enum definitions " << "------------------------------------------------------------\n"; - for (const AbstractMetaEnum &cppEnum : qAsConst(globalEnums)) { - if (cppEnum.isAnonymous() || cppEnum.isPrivate()) - continue; + for (const AbstractMetaEnum &cppEnum : std::as_const(globalEnums)) writeEnumConverterFunctions(s, cppEnum); - s << '\n'; - } if (convImpl.size() > 0) { s << "// Enum converters " @@ -6235,7 +6287,6 @@ bool CppGenerator::finishGeneration() << "} // namespace Shiboken\n\n"; } - writeFlagsNumberMethodsDefinitions(s, globalEnums); s << '\n'; } @@ -6243,20 +6294,19 @@ bool CppGenerator::finishGeneration() if (!requiredModules.isEmpty()) s << "// Required modules' type and converter arrays.\n"; for (const QString &requiredModule : requiredModules) { - s << "PyTypeObject **" << cppApiVariableName(requiredModule) << ";\n" + s << "Shiboken::Module::TypeInitStruct *" << cppApiVariableName(requiredModule) << ";\n" << "SbkConverter **" << convertersVariableName(requiredModule) << ";\n"; } s << "\n// Module initialization " << "------------------------------------------------------------\n"; - ExtendedConverterData extendedConverters = getExtendedConverters(); if (!extendedConverters.isEmpty()) { s << '\n' << "// Extended Converters.\n\n"; for (ExtendedConverterData::const_iterator it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) { - const TypeEntry *externalType = it.key(); + TypeEntryCPtr externalType = it.key(); s << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName() << '.' << '\n'; - for (const AbstractMetaClass *sourceClass : it.value()) { + for (const auto &sourceClass : it.value()) { AbstractMetaType sourceType = AbstractMetaType::fromAbstractMetaClass(sourceClass); AbstractMetaType targetType = AbstractMetaType::fromTypeEntry(externalType); writePythonToCppConversionFunctions(s, sourceType, targetType); @@ -6264,10 +6314,9 @@ bool CppGenerator::finishGeneration() } } - const QList<const CustomConversion *> &typeConversions = getPrimitiveCustomConversions(); if (!typeConversions.isEmpty()) { s << "\n// Primitive Type converters.\n\n"; - for (const CustomConversion *conversion : typeConversions) { + for (const auto &conversion : typeConversions) { s << "// C++ to Python conversion for primitive type '" << conversion->ownerType()->qualifiedCppName() << "'.\n"; writeCppToPythonFunction(s, conversion); writeCustomConverterFunctions(s, conversion); @@ -6275,23 +6324,32 @@ bool CppGenerator::finishGeneration() s << '\n'; } - const auto &containers = instantiatedContainers(); + QHash<AbstractMetaType, OpaqueContainerData> opaqueContainers; + const auto &containers = api().instantiatedContainers(); + QSet<AbstractMetaType> valueConverters; if (!containers.isEmpty()) { s << "// Container Type converters.\n\n"; for (const AbstractMetaType &container : containers) { - s << "// C++ to Python conversion for container type '" << container.cppSignature() << "'.\n"; + s << "// C++ to Python conversion for container type '" + << container.cppSignature() << "'.\n"; writeContainerConverterFunctions(s, container); + if (container.generateOpaqueContainer()) { + auto data = writeOpaqueContainerConverterFunctions(s, container, + &valueConverters); + opaqueContainers.insert(container, data); + } } s << '\n'; } // Implicit smart pointers conversions - const auto smartPointersList = instantiatedSmartPointers(); + const auto &smartPointersList = api().instantiatedSmartPointers(); if (!smartPointersList.isEmpty()) { s << "// SmartPointers converters.\n\n"; - for (const AbstractMetaType &smartPointer : smartPointersList) { - s << "// C++ to Python conversion for smart pointer type '" << smartPointer.cppSignature() << "'.\n"; - writeSmartPointerConverterFunctions(s, smartPointer); + for (const auto &smp : smartPointersList) { + s << "// C++ to Python conversion for smart pointer type '" + << smp.type.cppSignature() << "'.\n"; + writeSmartPointerConverterFunctions(s, smp.type); } s << '\n'; } @@ -6310,6 +6368,8 @@ bool CppGenerator::finishGeneration() // PYSIDE-510: Create a signatures string for the introspection feature. writeSignatureStrings(s, signatureStream.toString(), moduleName(), "global functions"); + writeInitInheritance(s); + // Write module init function const QString globalModuleVar = pythonModuleObjectName(); s << "extern \"C\" LIBSHIBOKEN_EXPORT PyObject *PyInit_" @@ -6318,10 +6378,9 @@ bool CppGenerator::finishGeneration() s << "if (" << globalModuleVar << " != nullptr)\n" << indent << "return " << globalModuleVar << ";\n" << outdent; - ErrorCode errorCode(QLatin1String("nullptr")); // module inject-code target/beginning - if (!snips.isEmpty()) - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode); + writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, + TypeSystem::TargetLangCode); for (const QString &requiredModule : requiredModules) { s << "{\n" << indent @@ -6335,11 +6394,27 @@ bool CppGenerator::finishGeneration() << "}\n\n"; } - int maxTypeIndex = getMaxTypeIndex() + instantiatedSmartPointers().size(); + int maxTypeIndex = getMaxTypeIndex() + api().instantiatedSmartPointers().size(); if (maxTypeIndex) { - s << "// Create an array of wrapper types for the current module.\n" - << "static PyTypeObject *cppApi[SBK_" << moduleName() << "_IDX_COUNT];\n" - << cppApiVariableName() << " = cppApi;\n\n"; + s << "// Create an array of wrapper types/names for the current module.\n" + << "static Shiboken::Module::TypeInitStruct cppApi[] = {\n" << indent; + + // Windows did not like an array of QString. + QStringList typeNames; + for (int idx = 0; idx < maxTypeIndex; ++idx) + typeNames.append("+++ unknown entry #"_L1 + QString::number(idx) + + " in "_L1 + moduleName()); + + collectFullTypeNamesArray(typeNames); + + for (auto typeName : typeNames) + s << "{nullptr, \"" << typeName << "\"},\n"; + + s << "{nullptr, nullptr}\n" << outdent << "};\n" + << "// The new global structure consisting of (type, name) pairs.\n" + << cppApiVariableName() << " = cppApi;\n" + << "// The backward compatible alias with upper case indexes.\n" + << cppApiVariableNameOld() << " = reinterpret_cast<PyTypeObject **>(cppApi);\n\n"; } s << "// Create an array of primitive type converters for the current module.\n" @@ -6349,13 +6424,18 @@ bool CppGenerator::finishGeneration() << "PyObject *module = Shiboken::Module::create(\"" << moduleName() << "\", &moduledef);\n\n" << "// Make module available from global scope\n" - << globalModuleVar << " = module;\n\n" - << "// Initialize classes in the type system\n" + << globalModuleVar << " = module;\n\n"; + + const QString subModuleOf = typeDb->defaultTypeSystemType()->subModuleOf(); + if (!subModuleOf.isEmpty()) + writeSubModuleHandling(s, moduleName(), subModuleOf); + + s << "// Initialize classes in the type system\n" << s_classPythonDefines.toString(); if (!typeConversions.isEmpty()) { s << '\n'; - for (const CustomConversion *conversion : typeConversions) { + for (const auto &conversion : typeConversions) { writePrimitiveConverterInitialization(s, conversion); s << '\n'; } @@ -6364,15 +6444,29 @@ bool CppGenerator::finishGeneration() if (!containers.isEmpty()) { s << '\n'; for (const AbstractMetaType &container : containers) { - writeContainerConverterInitialization(s, container); + const QString converterObj = writeContainerConverterInitialization(s, container, api()); + const auto it = opaqueContainers.constFind(container); + if (it != opaqueContainers.constEnd()) { + writeSetPythonToCppPointerConversion(s, converterObj, + it.value().pythonToConverterFunctionName, + it.value().converterCheckFunctionName); + } s << '\n'; } } + if (!opaqueContainers.isEmpty()) { + s << "\n// Opaque container type registration\n" + << "PyObject *ob_type{};\n"; + for (const auto &d : opaqueContainers) + s << d.registrationCode; + s << '\n'; + } + if (!smartPointersList.isEmpty()) { s << '\n'; - for (const AbstractMetaType &smartPointer : smartPointersList) { - writeSmartPointerConverterInitialization(s, smartPointer); + for (const auto &smp : smartPointersList) { + writeSmartPointerConverterInitialization(s, smp.type); s << '\n'; } } @@ -6388,21 +6482,14 @@ bool CppGenerator::finishGeneration() writeEnumsInitialization(s, globalEnums); s << "// Register primitive types converters.\n"; - const PrimitiveTypeEntryList &primitiveTypeList = primitiveTypes(); - for (const PrimitiveTypeEntry *pte : primitiveTypeList) { - if (!pte->generateCode() || !pte->isCppPrimitive()) + const PrimitiveTypeEntryCList &primitiveTypeList = primitiveTypes(); + for (const auto &pte : primitiveTypeList) { + if (!pte->generateCode() || !isCppPrimitive(pte)) continue; - const TypeEntry *referencedType = pte->basicReferencedTypeEntry(); - if (!referencedType) + if (!pte->referencesType()) continue; - QString converter = converterObject(referencedType); - QStringList cppSignature = pte->qualifiedCppName().split(QLatin1String("::"), Qt::SkipEmptyParts); - while (!cppSignature.isEmpty()) { - QString signature = cppSignature.join(QLatin1String("::")); - s << "Shiboken::Conversions::registerConverterName(" - << converter << ", \"" << signature << "\");\n"; - cppSignature.removeFirst(); - } + TypeEntryCPtr referencedType = basicReferencedTypeEntry(pte); + registerConverterInScopes(s, pte->qualifiedCppName(), converterObject(referencedType)); } s << '\n'; @@ -6414,27 +6501,29 @@ bool CppGenerator::finishGeneration() // of the previously registered types (PYSIDE-1529). if (!classesWithStaticFields.isEmpty()) { s << "\n// Static field initialization\n"; - for (auto cls : qAsConst(classesWithStaticFields)) - s << getSimpleClassStaticFieldsInitFunctionName(cls) << "();\n"; + for (const auto &cls : std::as_const(classesWithStaticFields)) { + ConfigurableScope configScope(s, cls->typeEntry()); + s << getSimpleClassStaticFieldsInitFunctionName(cls) << "(module);\n"; + } } - s << "\nif (PyErr_Occurred()) {\n" << indent + s << '\n' << initInheritanceFunction << "();\n" + << "\nif (" << shibokenErrorsOccurred << ") {\n" << indent << "PyErr_Print();\n" << "Py_FatalError(\"can't initialize module " << moduleName() << "\");\n" << outdent << "}\n"; // module inject-code target/end - if (!snips.isEmpty()) - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode); + writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode); // module inject-code native/end - if (!snips.isEmpty()) - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode); + writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode); if (usePySideExtensions()) { - for (const AbstractMetaEnum &metaEnum : qAsConst(globalEnums)) + for (const AbstractMetaEnum &metaEnum : std::as_const(globalEnums)) if (!metaEnum.isAnonymous()) { - s << "qRegisterMetaType< ::" << metaEnum.typeEntry()->qualifiedCppName() + ConfigurableScope configScope(s, metaEnum.typeEntry()); + s << "qRegisterMetaType< " << getFullTypeName(metaEnum.typeEntry()) << " >(\"" << metaEnum.name() << "\");\n"; } @@ -6442,7 +6531,7 @@ bool CppGenerator::finishGeneration() s << "PySide::registerCleanupFunction(cleanTypesAttributes);\n\n"; } - // finish the rest of __signature__ initialization. + // finish the rest of get_signature() initialization. s << "FinishSignatureInitialization(module, " << moduleName() << "_SignatureStrings);\n" << "\nreturn module;\n" << outdent << "}\n"; @@ -6459,12 +6548,35 @@ static ArgumentOwner getArgumentOwner(const AbstractMetaFunctionCPtr &func, int return argOwner; } +// Whether to enable parent ownership heuristic for a function and its argument. +// Both must belong to the same class hierarchy and have the same +// type entry enabling parent management. +static bool useParentHeuristics(const ApiExtractorResult &api, + const AbstractMetaFunctionCPtr &func, + const AbstractMetaType &argType) +{ + if (!ComplexTypeEntry::isParentManagementEnabled()) // FIXME PYSIDE 7: Remove this + return true; + const auto owner = func->ownerClass(); + if (!owner) + return false; + auto ownerEntry = parentManagementEntry(owner); + if (!ownerEntry) + return false; + auto argTypeEntry = argType.typeEntry(); + if (!argTypeEntry->isComplex()) + return false; + const auto argClass = AbstractMetaClass::findClass(api.classes(), argTypeEntry); + return argClass && parentManagementEntry(argClass) == ownerEntry; +} + bool CppGenerator::writeParentChildManagement(TextStream &s, const AbstractMetaFunctionCPtr &func, int argIndex, bool usePyArgs, bool useHeuristicPolicy) const { - const int numArgs = func->arguments().count(); + const int numArgs = func->arguments().size(); bool ctorHeuristicEnabled = func->isConstructor() && useCtorHeuristic() && useHeuristicPolicy; + bool heuristicTriggered = false; ArgumentOwner argOwner = getArgumentOwner(func, argIndex); ArgumentOwner::Action action = argOwner.action; @@ -6472,10 +6584,12 @@ bool CppGenerator::writeParentChildManagement(TextStream &s, const AbstractMetaF int childIndex = argIndex; if (ctorHeuristicEnabled && argIndex > 0 && argIndex <= numArgs) { const AbstractMetaArgument &arg = func->arguments().at(argIndex-1); - if (arg.name() == QLatin1String("parent") && arg.type().isObjectType()) { + if (arg.name() == u"parent" && arg.type().isObjectType() + && useParentHeuristics(api(), func, arg.type())) { action = ArgumentOwner::Add; parentIndex = argIndex; childIndex = -1; + heuristicTriggered = true; } } @@ -6487,28 +6601,32 @@ bool CppGenerator::writeParentChildManagement(TextStream &s, const AbstractMetaF << "Argument index for parent tag out of bounds: " << func->signature(); if (action == ArgumentOwner::Remove) { - parentVariable = QLatin1String("Py_None"); + parentVariable = u"Py_None"_s; } else { if (parentIndex == 0) { - parentVariable = QLatin1String(PYTHON_RETURN_VAR); + parentVariable = PYTHON_RETURN_VAR; } else if (parentIndex == -1) { - parentVariable = QLatin1String("self"); + parentVariable = PYTHON_SELF_VAR; } else { parentVariable = usePyArgs - ? pythonArgsAt(parentIndex - 1) : QLatin1String(PYTHON_ARG); + ? pythonArgsAt(parentIndex - 1) : PYTHON_ARG; } } if (childIndex == 0) { - childVariable = QLatin1String(PYTHON_RETURN_VAR); + childVariable = PYTHON_RETURN_VAR; } else if (childIndex == -1) { - childVariable = QLatin1String("self"); + childVariable = PYTHON_SELF_VAR; } else { childVariable = usePyArgs - ? pythonArgsAt(childIndex - 1) : QLatin1String(PYTHON_ARG); + ? pythonArgsAt(childIndex - 1) : PYTHON_ARG; } - s << "Shiboken::Object::setParent(" << parentVariable << ", " << childVariable << ");\n"; + s << "// Ownership transferences"; + if (heuristicTriggered) + s << " (constructor heuristics)"; + s << ".\nShiboken::Object::setParent(" << parentVariable << ", " + << childVariable << ");\n"; return true; } @@ -6519,7 +6637,7 @@ void CppGenerator::writeParentChildManagement(TextStream &s, const AbstractMetaF bool usesPyArgs, bool useHeuristicForReturn) const { - const int numArgs = func->arguments().count(); + const int numArgs = func->arguments().size(); // -1 = return value // 0 = self @@ -6539,27 +6657,40 @@ void CppGenerator::writeReturnValueHeuristics(TextStream &s, const AbstractMetaF || type.isVoid() || func->isStatic() || func->isConstructor() - || !func->typeReplaced(0).isEmpty()) { + || func->isTypeModified() + || !useParentHeuristics(api(), func, type) + // Something like parent(), parentWidget(): No child relationship here. + || (func->maybeAccessor() && func->name().startsWith(u"parent"))) { return; } ArgumentOwner argOwner = getArgumentOwner(func, ArgumentOwner::ReturnIndex); if (argOwner.action == ArgumentOwner::Invalid || argOwner.index != ArgumentOwner::ThisIndex) { - if (type.isPointerToWrapperType()) - s << "Shiboken::Object::setParent(self, " << PYTHON_RETURN_VAR << ");\n"; + if (type.isPointerToWrapperType()) { + s << "// Ownership transferences (return value heuristics).\n" + << "Shiboken::Object::setParent(self, " << PYTHON_RETURN_VAR << ");\n"; + } } } -void CppGenerator::writeHashFunction(TextStream &s, const GeneratorContext &context) const +void CppGenerator::writeHashFunction(TextStream &s, const GeneratorContext &context) { - const AbstractMetaClass *metaClass = context.metaClass(); + const auto metaClass = context.metaClass(); const char hashType[] = "Py_hash_t"; s << "static " << hashType << ' ' << cpythonBaseName(metaClass) - << "_HashFunc(PyObject *self) {\n" << indent; + << "_HashFunc(PyObject *self)\n{\n" << indent; writeCppSelfDefinition(s, context); - s << "return " << hashType << '(' - << metaClass->typeEntry()->hashFunction() << '('; - if (!metaClass->isObjectType()) + + bool deref = true; + QString name = metaClass->typeEntry()->hashFunction(); + if (name.isEmpty()) + name = metaClass->hashFunction(); + else + deref = !metaClass->isObjectType(); + Q_ASSERT(!name.isEmpty()); + + s << "return " << hashType << '(' << name << '('; + if (deref) s << '*'; s << CPP_SELF_VAR << "));\n" << outdent << "}\n\n"; @@ -6568,21 +6699,22 @@ void CppGenerator::writeHashFunction(TextStream &s, const GeneratorContext &cont void CppGenerator::writeDefaultSequenceMethods(TextStream &s, const GeneratorContext &context) const { - const AbstractMetaClass *metaClass = context.metaClass(); - ErrorCode errorCode(0); + const auto metaClass = context.metaClass(); + ErrorReturn errorReturn = ErrorReturn::Zero; // __len__ - s << "Py_ssize_t " << cpythonBaseName(metaClass->typeEntry()) + const QString namePrefix = cpythonBaseName(metaClass->typeEntry()); + s << "Py_ssize_t " << namePrefix << "__len__(PyObject *self)\n{\n" << indent; - writeCppSelfDefinition(s, context); + writeCppSelfDefinition(s, context, errorReturn); s << "return " << CPP_SELF_VAR << "->size();\n" << outdent << "}\n"; // __getitem__ - s << "PyObject *" << cpythonBaseName(metaClass->typeEntry()) + s << "PyObject *" << namePrefix << "__getitem__(PyObject *self, Py_ssize_t _i)\n{\n" << indent; - writeCppSelfDefinition(s, context); - writeIndexError(s, QLatin1String("index out of bounds")); + writeCppSelfDefinition(s, context, errorReturn); + writeIndexError(s, u"index out of bounds"_s, errorReturn); s << metaClass->qualifiedCppName() << "::const_iterator _item = " << CPP_SELF_VAR << "->begin();\n" @@ -6599,29 +6731,25 @@ void CppGenerator::writeDefaultSequenceMethods(TextStream &s, const AbstractMetaType &itemType = instantiations.constFirst(); s << "return "; - writeToPythonConversion(s, itemType, metaClass, QLatin1String("*_item")); + writeToPythonConversion(s, itemType, metaClass, u"*_item"_s); s << ";\n" << outdent << "}\n"; // __setitem__ - ErrorCode errorCode2(-1); - s << "int " << cpythonBaseName(metaClass->typeEntry()) + s << "int " << namePrefix << "__setitem__(PyObject *self, Py_ssize_t _i, PyObject *pyArg)\n{\n" << indent; - writeCppSelfDefinition(s, context); - writeIndexError(s, QLatin1String("list assignment index out of range")); + errorReturn = ErrorReturn::MinusOne; + writeCppSelfDefinition(s, context, errorReturn); + writeIndexError(s, u"list assignment index out of range"_s, errorReturn); - s << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ";\n" + s << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR << ";\n" << "if (!"; - writeTypeCheck(s, itemType, QLatin1String("pyArg"), isNumber(itemType.typeEntry())); - s << ") {\n"; - { - Indentation indent(s); - s << "PyErr_SetString(PyExc_TypeError, \"attributed value with wrong type, '" - << itemType.name() << "' or other convertible type expected\");\n" - << "return -1;\n"; - } - s << "}\n"; - writeArgumentConversion(s, itemType, QLatin1String("cppValue"), QLatin1String("pyArg"), metaClass); + writeTypeCheck(s, itemType, u"pyArg"_s, isNumber(itemType.typeEntry())); + s << ") {\n" << indent + << "Shiboken::Errors::setSequenceTypeError(\"" << itemType.name() << "\");\n" + << "return -1;\n" << outdent << "}\n"; + writeArgumentConversion(s, itemType, u"cppValue"_s, + u"pyArg"_s, errorReturn, metaClass); s << metaClass->qualifiedCppName() << "::iterator _item = " << CPP_SELF_VAR << "->begin();\n" @@ -6630,25 +6758,28 @@ void CppGenerator::writeDefaultSequenceMethods(TextStream &s, s << "return {};\n" << outdent << "}\n"; } -void CppGenerator::writeIndexError(TextStream &s, const QString &errorMsg) +void CppGenerator::writeIndexError(TextStream &s, const QString &errorMsg, + ErrorReturn errorReturn) { - s << "if (_i < 0 || _i >= (Py_ssize_t) " << CPP_SELF_VAR << "->size()) {\n"; - { - Indentation indent(s); - s << "PyErr_SetString(PyExc_IndexError, \"" << errorMsg << "\");\n" - << returnStatement(m_currentErrorCode) << '\n'; - } - s << "}\n"; + s << "if (_i < 0 || _i >= (Py_ssize_t) " << CPP_SELF_VAR << "->size()) {\n" + << indent << "PyErr_SetString(PyExc_IndexError, \"" << errorMsg << "\");\n" + << errorReturn << outdent << "}\n"; +} + +QString CppGenerator::writeReprFunctionHeader(TextStream &s, const GeneratorContext &context) +{ + QString funcName = cpythonBaseName(context.metaClass()) + REPR_FUNCTION; + s << "extern \"C\"\n{\n" + << "static PyObject *" << funcName << "(PyObject *self)\n{\n" << indent; + return funcName; } QString CppGenerator::writeReprFunction(TextStream &s, const GeneratorContext &context, - uint indirections) const + uint indirections) { - const AbstractMetaClass *metaClass = context.metaClass(); - QString funcName = cpythonBaseName(metaClass) + reprFunction(); - s << "extern \"C\"\n{\n" - << "static PyObject *" << funcName << "(PyObject *self)\n{\n" << indent; + const auto metaClass = context.metaClass(); + QString funcName = writeReprFunctionHeader(s, context); writeCppSelfDefinition(s, context); s << R"(QBuffer buffer; buffer.open(QBuffer::ReadWrite); @@ -6659,27 +6790,25 @@ dbg << )"; s << CPP_SELF_VAR << R"(; buffer.close(); QByteArray str = buffer.data(); -int idx = str.indexOf('('); +const auto idx = str.indexOf('('); +auto *typeName = Py_TYPE(self)->tp_name; if (idx >= 0) -)"; - { - Indentation indent(s); - s << "str.replace(0, idx, Py_TYPE(self)->tp_name);\n"; - } - s << "str = str.trimmed();\n" - << "PyObject *mod = PyDict_GetItem(Py_TYPE(self)->tp_dict, Shiboken::PyMagicName::module());\n"; +)" << indent << "str.replace(0, idx, typeName);\n" << outdent + << "str = str.trimmed();\n" + << "Shiboken::AutoDecRef tpDict(PepType_GetDict(Py_TYPE(self)));\n" + << "PyObject *mod = PyDict_GetItem(tpDict.object(), Shiboken::PyMagicName::module());\n"; // PYSIDE-595: The introduction of heap types has the side effect that the module name // is always prepended to the type name. Therefore the strchr check: - s << "if (mod && !strchr(str, '.'))\n"; - { - Indentation indent(s); - s << "return Shiboken::String::fromFormat(\"<%s.%s at %p>\", Shiboken::String::toCString(mod), str.constData(), self);\n"; - } - s << "else\n"; - { - Indentation indent(s); - s << "return Shiboken::String::fromFormat(\"<%s at %p>\", str.constData(), self);\n"; - } - s << outdent << "}\n} // extern C\n\n"; + s << "if (mod != nullptr && std::strchr(typeName, '.') == nullptr)\n" << indent + << "return Shiboken::String::fromFormat(\"<%s.%s at %p>\"," + " Shiboken::String::toCString(mod), str.constData(), self);\n" + << outdent + << "return Shiboken::String::fromFormat(\"<%s at %p>\", str.constData(), self);\n"; + writeReprFunctionFooter(s); return funcName; } + +void CppGenerator::writeReprFunctionFooter(TextStream &s) +{ + s << outdent << "}\n} // extern C\n\n"; +} |