diff options
Diffstat (limited to 'sources/shiboken6/generator')
27 files changed, 4580 insertions, 3839 deletions
diff --git a/sources/shiboken6/generator/CMakeLists.txt b/sources/shiboken6/generator/CMakeLists.txt index bac998ae5..aebe2cd5e 100644 --- a/sources/shiboken6/generator/CMakeLists.txt +++ b/sources/shiboken6/generator/CMakeLists.txt @@ -1,29 +1,46 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + project(shibokengenerator) set(package_name "Shiboken6Tools") set(CMAKE_AUTOMOC ON) -if(NOT (Qt${QT_MAJOR_VERSION}Core_FOUND AND PYTHONINTERP_FOUND)) +if(NOT (Qt${QT_MAJOR_VERSION}Core_FOUND AND Python_Interpreter_FOUND)) message(WARNING "Some dependencies were not found: shiboken6 generator compilation disabled!") return() endif() set(shiboken6_SRC -generator.cpp -generatorcontext.cpp -defaultvalue.cpp -shiboken/cppgenerator.cpp -shiboken/cppgenerator_container.cpp -shiboken/generatorargument.cpp -shiboken/headergenerator.cpp -shiboken/overloaddata.cpp -shiboken/shibokengenerator.cpp +defaultvalue.cpp defaultvalue.h +generator.cpp generator.h +generatorcontext.cpp generatorcontext.h main.cpp +shiboken/configurablescope.h +shiboken/cppgenerator.cpp shiboken/cppgenerator.h +shiboken/cppgenerator_container.cpp +shiboken/cppgenerator_smartpointer.cpp +shiboken/ctypenames.h +shiboken/generatorargument.cpp shiboken/generatorargument.h shiboken/generatorstrings.h +shiboken/headergenerator.cpp shiboken/headergenerator.h +shiboken/overloaddata.cpp shiboken/overloaddata.h +shiboken/pytypenames.h +shiboken/shibokengenerator.cpp shiboken/shibokengenerator.h ) +find_libclang() + +if(${STANDALONE}) + list(APPEND CMAKE_INSTALL_RPATH ${base}/Qt/lib) +else() + list(APPEND CMAKE_INSTALL_RPATH ${QT6_INSTALL_PREFIX}/${QT6_INSTALL_LIBS} + ${libclang_lib_dir}) +endif() + add_executable(shiboken6 ${shiboken6_SRC}) add_executable(Shiboken6::shiboken6 ALIAS shiboken6) add_dependencies(shiboken6 apiextractor) + set_target_properties(shiboken6 PROPERTIES OUTPUT_NAME shiboken6${shiboken6_SUFFIX}) target_include_directories(shiboken6 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/shiboken @@ -32,9 +49,13 @@ target_include_directories(shiboken6 PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${apiextractor_SOURCE_DIR} ) -target_link_libraries(shiboken6 apiextractor Qt${QT_MAJOR_VERSION}::Core) +target_link_libraries(shiboken6 apiextractor Qt::Core) if (NOT DISABLE_DOCSTRINGS) - target_sources(shiboken6 PRIVATE qtdoc/qtxmltosphinx.cpp qtdoc/qtdocgenerator.cpp) + target_sources(shiboken6 PRIVATE + qtdoc/qtdocgenerator.cpp qtdoc/qtdocgenerator.h + qtdoc/qtxmltosphinx.cpp qtdoc/qtxmltosphinx.h + qtdoc/qtxmltosphinxinterface.h + qtdoc/rstformat.h) target_compile_definitions(shiboken6 PUBLIC DOCSTRINGS_ENABLED QT_LEAN_HEADERS=1) endif() diff --git a/sources/shiboken6/generator/_config.py.in b/sources/shiboken6/generator/_config.py.in index 985735fa4..ed7e67098 100644 --- a/sources/shiboken6/generator/_config.py.in +++ b/sources/shiboken6/generator/_config.py.in @@ -7,3 +7,4 @@ version_info = (@shiboken_MAJOR_VERSION@, @shiboken_MINOR_VERSION@, @shiboken_MI @PACKAGE_BUILD_COMMIT_HASH_DESCRIBED@ @PACKAGE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT@ @PACKAGE_SETUP_PY_PACKAGE_VERSION_ASSIGNMENT@ +@QT_MACOS_DEPLOYMENT_TARGET@ diff --git a/sources/shiboken6/generator/defaultvalue.cpp b/sources/shiboken6/generator/defaultvalue.cpp index c3983a2e3..89cc9fa77 100644 --- a/sources/shiboken6/generator/defaultvalue.cpp +++ b/sources/shiboken6/generator/defaultvalue.cpp @@ -47,7 +47,7 @@ QString DefaultValue::returnValue() const case DefaultValue::Pointer: return u"nullptr"_s; case DefaultValue::Void: - return QString(); + return {}; case DefaultValue::DefaultConstructorWithDefaultValues: return m_value + u"()"_s; case DefaultValue::DefaultConstructor: @@ -76,7 +76,7 @@ QString DefaultValue::initialization() const case DefaultValue::DefaultConstructorWithDefaultValues: break; } - return QString(); + return {}; } QString DefaultValue::constructorParameter() const diff --git a/sources/shiboken6/generator/generator.cpp b/sources/shiboken6/generator/generator.cpp index b1accb74b..a01326530 100644 --- a/sources/shiboken6/generator/generator.cpp +++ b/sources/shiboken6/generator/generator.cpp @@ -10,6 +10,7 @@ #include "abstractmetafunction.h" #include "abstractmetalang.h" #include "messages.h" +#include <optionsparser.h> #include "reporthandler.h" #include "fileout.h" #include "arraytypeentry.h" @@ -29,8 +30,14 @@ using namespace Qt::StringLiterals; -static const char ENABLE_PYSIDE_EXTENSIONS[] = "enable-pyside-extensions"; -static const char AVOID_PROTECTED_HACK[] = "avoid-protected-hack"; +static constexpr auto ENABLE_PYSIDE_EXTENSIONS = "enable-pyside-extensions"_L1; +static constexpr auto AVOID_PROTECTED_HACK = "avoid-protected-hack"_L1; + +struct GeneratorOptions +{ + bool usePySideExtensions = false; + bool avoidProtectedHack = false; +}; struct Generator::GeneratorPrivate { @@ -40,10 +47,14 @@ struct Generator::GeneratorPrivate QString licenseComment; AbstractMetaClassCList m_invisibleTopNamespaces; bool m_hasPrivateClasses = false; - bool m_usePySideExtensions = false; - bool m_avoidProtectedHack = false; + static GeneratorOptions m_options; }; +GeneratorOptions Generator::GeneratorPrivate::m_options; + +// Kept as a variable for a potential Qt-in-namespace support +QString Generator::m_gsp = "::"_L1; + Generator::Generator() : m_d(new GeneratorPrivate) { } @@ -66,10 +77,10 @@ bool Generator::setup(const ApiExtractorResult &api) return false; } - for (auto c : api.classes()) { + for (const auto &c : api.classes()) { if (c->enclosingClass() == nullptr && c->isInvisibleNamespace()) { m_d->m_invisibleTopNamespaces.append(c); - c->invisibleNamespaceRecursion([&](const AbstractMetaClass *ic) { + c->invisibleNamespaceRecursion([&](const AbstractMetaClassCPtr &ic) { m_d->m_invisibleTopNamespaces.append(ic); }); } @@ -78,33 +89,51 @@ bool Generator::setup(const ApiExtractorResult &api) return doSetup(); } -Generator::OptionDescriptions Generator::options() const +QList<OptionDescription> Generator::options() { return { - {QLatin1StringView(AVOID_PROTECTED_HACK), + {AVOID_PROTECTED_HACK, u"Avoid the use of the '#define protected public' hack."_s}, - {QLatin1StringView(ENABLE_PYSIDE_EXTENSIONS), + {ENABLE_PYSIDE_EXTENSIONS, u"Enable PySide extensions, such as support for signal/slots,\n" "use this if you are creating a binding for a Qt-based library."_s} }; } -bool Generator::handleOption(const QString & key, const QString & /* value */) +class GeneratorOptionsParser : public OptionsParser +{ +public: + explicit GeneratorOptionsParser(GeneratorOptions *o) : m_options(o) {} + + bool handleBoolOption(const QString &key, OptionSource source) override; + +private: + GeneratorOptions *m_options; +}; + +bool GeneratorOptionsParser::handleBoolOption(const QString & key, OptionSource source) { - if (key == QLatin1StringView(ENABLE_PYSIDE_EXTENSIONS)) - return ( m_d->m_usePySideExtensions = true); - if (key == QLatin1StringView(AVOID_PROTECTED_HACK)) - return (m_d->m_avoidProtectedHack = true); + if (source == OptionSource::CommandLineSingleDash) + return false; + if (key == ENABLE_PYSIDE_EXTENSIONS) + return ( m_options->usePySideExtensions = true); + if (key == AVOID_PROTECTED_HACK) + return ( m_options->avoidProtectedHack = true); return false; } +std::shared_ptr<OptionsParser> Generator::createOptionsParser() +{ + return std::make_shared<GeneratorOptionsParser>(&GeneratorPrivate::m_options); +} + QString Generator::fileNameForContextHelper(const GeneratorContext &context, const QString &suffix, FileNameFlags flags) { if (!context.forSmartPointer()) { - const AbstractMetaClass *metaClass = context.metaClass(); + const auto metaClass = context.metaClass(); QString fileNameBase = flags.testFlag(FileNameFlag::UnqualifiedName) ? metaClass->name() : metaClass->qualifiedCppName(); if (!flags.testFlag(FileNameFlag::KeepCase)) @@ -176,7 +205,7 @@ void Generator::setOutputDirectory(const QString &outDir) bool Generator::generateFileForContext(const GeneratorContext &context) { - const AbstractMetaClass *cls = context.metaClass(); + const auto cls = context.metaClass(); auto typeEntry = cls->typeEntry(); if (!shouldGenerate(typeEntry)) @@ -202,23 +231,23 @@ QString Generator::getFileNameBaseForSmartPointer(const AbstractMetaType &smartP const AbstractMetaType innerType = smartPointerType.getSmartPointerInnerType(); smartPointerType.typeEntry()->qualifiedCppName(); QString fileName = smartPointerType.typeEntry()->qualifiedCppName().toLower(); - fileName.replace(u"::"_s, u"_"_s); - fileName.append(u"_"_s); + fileName.append(u'_'); fileName.append(innerType.name().toLower()); - + fileName.replace(u"::"_s, u"_"_s); // std::shared_ptr<std::string> return fileName; } -GeneratorContext Generator::contextForClass(const AbstractMetaClass *c) const +GeneratorContext Generator::contextForClass(const AbstractMetaClassCPtr &c) const { GeneratorContext result; result.m_metaClass = c; return result; } -GeneratorContext Generator::contextForSmartPointer(const AbstractMetaClass *c, - const AbstractMetaType &t, - const AbstractMetaClass *pointeeClass) +GeneratorContext + Generator::contextForSmartPointer(const AbstractMetaClassCPtr &c, + const AbstractMetaType &t, + const AbstractMetaClassCPtr &pointeeClass) { GeneratorContext result; result.m_metaClass = c; @@ -230,7 +259,7 @@ GeneratorContext Generator::contextForSmartPointer(const AbstractMetaClass *c, bool Generator::generate() { - for (auto cls : m_d->api.classes()) { + for (const auto &cls : m_d->api.classes()) { if (!generateFileForContext(contextForClass(cls))) return false; auto te = cls->typeEntry(); @@ -239,7 +268,7 @@ bool Generator::generate() } for (const auto &smp: m_d->api.instantiatedSmartPointers()) { - const AbstractMetaClass *pointeeClass = nullptr; + AbstractMetaClassCPtr pointeeClass; const auto instantiatedType = smp.type.instantiations().constFirst().typeEntry(); if (instantiatedType->isComplex()) // not a C++ primitive pointeeClass = AbstractMetaClass::findClass(m_d->api.classes(), instantiatedType); @@ -266,24 +295,22 @@ bool Generator::hasPrivateClasses() const return m_d->m_hasPrivateClasses; } -bool Generator::usePySideExtensions() const +bool Generator::usePySideExtensions() { - return m_d->m_usePySideExtensions; + return GeneratorPrivate::m_options.usePySideExtensions; } -bool Generator::avoidProtectedHack() const +bool Generator::avoidProtectedHack() { - return m_d->m_avoidProtectedHack; + return GeneratorPrivate::m_options.avoidProtectedHack; } QString Generator::getFullTypeName(TypeEntryCPtr type) { QString result = type->qualifiedCppName(); if (type->isArray()) - type = qSharedPointerCast<const ArrayTypeEntry>(type)->nestedTypeEntry(); - if (!isCppPrimitive(type)) - result.prepend(u"::"_s); - return result; + type = std::static_pointer_cast<const ArrayTypeEntry>(type)->nestedTypeEntry(); + return isCppPrimitive(type) ? result : addGlobalScopePrefix(result); } QString Generator::getFullTypeName(const AbstractMetaType &type) @@ -293,7 +320,7 @@ QString Generator::getFullTypeName(const AbstractMetaType &type) if (type.isVoidPointer()) return u"void*"_s; if (type.typeEntry()->isContainer()) - return u"::"_s + type.cppSignature(); + return addGlobalScopePrefix(type.cppSignature()); QString typeName; if (type.typeEntry()->isComplex() && type.hasInstantiations()) typeName = getFullTypeNameWithoutModifiers(type); @@ -302,9 +329,11 @@ QString Generator::getFullTypeName(const AbstractMetaType &type) return typeName + QString::fromLatin1("*").repeated(type.indirections()); } -QString Generator::getFullTypeName(const AbstractMetaClass *metaClass) +QString Generator::getFullTypeName(const AbstractMetaClassCPtr &metaClass) { - return u"::"_s + metaClass->qualifiedCppName(); + const QString &qualName = metaClass->qualifiedCppName(); + // Typedefs are generated into the global namespace + return metaClass->isTypeDef() ? qualName : addGlobalScopePrefix(qualName); } QString Generator::getFullTypeNameWithoutModifiers(const AbstractMetaType &type) @@ -330,7 +359,7 @@ QString Generator::getFullTypeNameWithoutModifiers(const AbstractMetaType &type) } while (typeName.endsWith(u'*') || typeName.endsWith(u' ')) typeName.chop(1); - return u"::"_s + typeName; + return addGlobalScopePrefix(typeName); } std::optional<DefaultValue> @@ -359,13 +388,13 @@ std::optional<DefaultValue> if (type.isNativePointer()) return DefaultValue(DefaultValue::Pointer, type.typeEntry()->qualifiedCppName()); if (type.isPointer()) - return DefaultValue(DefaultValue::Pointer, u"::"_s + type.typeEntry()->qualifiedCppName()); + return DefaultValue(DefaultValue::Pointer, getFullTypeName(type.typeEntry())); if (type.typeEntry()->isSmartPointer()) return minimalConstructor(api, type.typeEntry()); if (type.typeEntry()->isComplex()) { - auto cType = qSharedPointerCast<const ComplexTypeEntry>(type.typeEntry()); + auto cType = std::static_pointer_cast<const ComplexTypeEntry>(type.typeEntry()); if (cType->hasDefaultConstructor()) return DefaultValue(DefaultValue::Custom, cType->defaultConstructor()); auto klass = AbstractMetaClass::findClass(api.classes(), cType); @@ -404,12 +433,11 @@ std::optional<DefaultValue> } if (type->isEnum()) { - const auto enumEntry = qSharedPointerCast<const EnumTypeEntry>(type); - if (const auto nullValue = enumEntry->nullValue(); !nullValue.isNull()) + const auto enumEntry = std::static_pointer_cast<const EnumTypeEntry>(type); + if (const auto nullValue = enumEntry->nullValue()) return DefaultValue(DefaultValue::Enum, nullValue->name()); return DefaultValue(DefaultValue::Custom, - u"static_cast< ::"_s + type->qualifiedCppName() - + u">(0)"_s); + "static_cast< "_L1 + getFullTypeName(type) + ">(0)"_L1); } if (type->isFlags()) { @@ -418,7 +446,7 @@ std::optional<DefaultValue> } if (type->isPrimitive()) { - QString ctor = qSharedPointerCast<const PrimitiveTypeEntry>(type)->defaultConstructor(); + QString ctor = std::static_pointer_cast<const PrimitiveTypeEntry>(type)->defaultConstructor(); // If a non-C++ (i.e. defined by the user) primitive type does not have // a default constructor defined by the user, the empty constructor is // heuristically returned. If this is wrong the build of the generated @@ -455,13 +483,13 @@ static QString constructorCall(const QString &qualifiedCppName, const QStringLis std::optional<DefaultValue> Generator::minimalConstructor(const ApiExtractorResult &api, - const AbstractMetaClass *metaClass, + const AbstractMetaClassCPtr &metaClass, QString *errorString) { if (!metaClass) return {}; - auto cType = qSharedPointerCast<const ComplexTypeEntry>(metaClass->typeEntry()); + auto cType = std::static_pointer_cast<const ComplexTypeEntry>(metaClass->typeEntry()); if (cType->hasDefaultConstructor()) return DefaultValue(DefaultValue::Custom, cType->defaultConstructor()); @@ -501,8 +529,7 @@ std::optional<DefaultValue> for (auto it = candidates.cbegin(), end = candidates.cend(); it != end; ++it) { const AbstractMetaArgumentList &arguments = it.value()->arguments(); QStringList args; - for (qsizetype i = 0, size = arguments.size(); i < size; ++i) { - const AbstractMetaArgument &arg = arguments.at(i); + for (const auto &arg : arguments) { if (arg.hasModifiedDefaultValueExpression()) { args << arg.defaultValueExpression(); // Spell out modified values break; @@ -521,7 +548,7 @@ std::optional<DefaultValue> } QString Generator::translateType(AbstractMetaType cType, - const AbstractMetaClass *context, + const AbstractMetaClassCPtr &context, Options options) const { QString s; @@ -537,20 +564,20 @@ QString Generator::translateType(AbstractMetaType cType, } else if (cType.isArray()) { s = translateType(*cType.arrayElementType(), context, options) + u"[]"_s; } else { + AbstractMetaType copyType = cType; if (options & Generator::ExcludeConst || options & Generator::ExcludeReference) { - AbstractMetaType copyType = cType; - if (options & Generator::ExcludeConst) copyType.setConstant(false); - if (options & Generator::ExcludeReference) copyType.setReferenceType(NoReference); + } - s = copyType.cppSignature(); - if (!copyType.typeEntry()->isVoid() && !isCppPrimitive(copyType.typeEntry())) - s.prepend(u"::"_s); - } else { - s = cType.cppSignature(); + s = copyType.cppSignature(); + const auto te = copyType.typeEntry(); + if (!te->isVoid() && !isCppPrimitive(te)) { // Add scope resolution + const auto pos = s.indexOf(te->qualifiedCppName()); // Skip const/volatile + Q_ASSERT(pos >= 0); + s.insert(pos, u"::"_s); } } @@ -574,7 +601,6 @@ static const QHash<QString, QString> &pythonOperators() {u"operator++"_s, u"__iadd__"_s}, {u"operator--"_s, u"__isub__"_s}, {u"operator*="_s, u"__imul__"_s}, - {u"operator/="_s, u"__idiv__"_s}, {u"operator%="_s, u"__imod__"_s}, // Bitwise operators {u"operator&"_s, u"__and__"_s}, @@ -595,7 +621,10 @@ static const QHash<QString, QString> &pythonOperators() {u"operator<"_s, u"__lt__"_s}, {u"operator>"_s, u"__gt__"_s}, {u"operator<="_s, u"__le__"_s}, - {u"operator>="_s, u"__ge__"_s} + {u"operator>="_s, u"__ge__"_s}, + // Conversion (note bool has special handling with heuristics) + {u"operator int"_s, u"__int__"_s}, + {u"operator double"_s, u"__float__"_s} }; return result; } @@ -605,6 +634,11 @@ QString Generator::pythonOperatorFunctionName(const QString &cppOpFuncName) return pythonOperators().value(cppOpFuncName); } +bool Generator::isPythonOperatorFunctionName(const QString &cppOpFuncName) +{ + return pythonOperators().contains(cppOpFuncName); +} + QString Generator::subDirectoryForPackage(QString packageNameIn) const { if (packageNameIn.isEmpty()) @@ -613,11 +647,21 @@ QString Generator::subDirectoryForPackage(QString packageNameIn) const return packageNameIn; } +QString Generator::addGlobalScopePrefix(const QString &t) +{ + return t.startsWith("std::"_L1) ? t : m_gsp + t; +} + +QString Generator::globalScopePrefix(const GeneratorContext &classContext) +{ + return classContext.useWrapper() ? QString{} : m_gsp; +} + template<typename T> -static QString getClassTargetFullName_(const T *t, bool includePackageName) +static QString getClassTargetFullName_(T t, bool includePackageName) { QString name = t->name(); - const AbstractMetaClass *context = t->enclosingClass(); + AbstractMetaClassCPtr context = t->enclosingClass(); while (context) { // If the type was marked as 'visible=false' we should not use it in // the type name @@ -634,12 +678,14 @@ static QString getClassTargetFullName_(const T *t, bool includePackageName) return name; } -QString getClassTargetFullName(const AbstractMetaClass *metaClass, bool includePackageName) +QString getClassTargetFullName(const AbstractMetaClassCPtr &metaClass, + bool includePackageName) { return getClassTargetFullName_(metaClass, includePackageName); } -QString getClassTargetFullName(const AbstractMetaEnum &metaEnum, bool includePackageName) +QString getClassTargetFullName(const AbstractMetaEnum &metaEnum, + bool includePackageName) { return getClassTargetFullName_(&metaEnum, includePackageName); } diff --git a/sources/shiboken6/generator/generator.h b/sources/shiboken6/generator/generator.h index 131ad427d..5b051b599 100644 --- a/sources/shiboken6/generator/generator.h +++ b/sources/shiboken6/generator/generator.h @@ -6,18 +6,23 @@ #include <abstractmetalang_typedefs.h> #include <typedatabase_typedefs.h> -#include <QtCore/QSharedPointer> #include <QtCore/QList> +#include <memory> #include <optional> + class ApiExtractorResult; class GeneratorContext; class DefaultValue; +struct OptionDescription; +class OptionsParser; class TextStream; -QString getClassTargetFullName(const AbstractMetaClass *metaClass, bool includePackageName = true); -QString getClassTargetFullName(const AbstractMetaEnum &metaEnum, bool includePackageName = true); +QString getClassTargetFullName(const AbstractMetaClassCPtr &metaClass, + bool includePackageName = true); +QString getClassTargetFullName(const AbstractMetaEnum &metaEnum, + bool includePackageName = true); QString getFilteredCppSignatureString(QString signature); /** @@ -25,12 +30,11 @@ QString getFilteredCppSignatureString(QString signature); * you must subclass this to create your own generators. */ class Generator -{ +{; public: - using OptionDescription = QPair<QString, QString>; - using OptionDescriptions = QList<OptionDescription>; + Q_DISABLE_COPY_MOVE(Generator) - /// Optiosn used around the generator code + /// Options used around the generator code enum Option { NoOption = 0x00000000, ExcludeConst = 0x00000001, @@ -56,8 +60,8 @@ public: bool setup(const ApiExtractorResult &api); - virtual OptionDescriptions options() const; - virtual bool handleOption(const QString &key, const QString &value); + static QList<OptionDescription> options(); + static std::shared_ptr<OptionsParser> createOptionsParser(); /// Returns the top namespace made invisible const AbstractMetaClassCList &invisibleTopNamespaces() const; @@ -91,10 +95,10 @@ public: bool hasPrivateClasses() const; /// Returns true if the user enabled PySide extensions (command line option) - bool usePySideExtensions() const; + static bool usePySideExtensions(); /// Returns true if the generated code should not use the /// "#define protected public" hack. - bool avoidProtectedHack() const; + static bool avoidProtectedHack(); /** * Retrieves the name of the currently processed module. @@ -107,6 +111,7 @@ public: static QString moduleName(); static QString pythonOperatorFunctionName(const QString &cppOpFuncName); + static bool isPythonOperatorFunctionName(const QString &cppOpFuncName); protected: /// Helper for determining the file name @@ -120,10 +125,10 @@ protected: /// Returns all container types found by APIExtractor static ContainerTypeEntryCList containerTypes(); - virtual GeneratorContext contextForClass(const AbstractMetaClass *c) const; - static GeneratorContext contextForSmartPointer(const AbstractMetaClass *c, - const AbstractMetaType &t, - const AbstractMetaClass *pointeeClass = nullptr); + virtual GeneratorContext contextForClass(const AbstractMetaClassCPtr &c) const; + static GeneratorContext + contextForSmartPointer(const AbstractMetaClassCPtr &c, const AbstractMetaType &t, + const AbstractMetaClassCPtr &pointeeClass = {}); /// Generates a file for given AbstractMetaClass or AbstractMetaType (smart pointer case). bool generateFileForContext(const GeneratorContext &context); @@ -142,7 +147,7 @@ protected: * \return the metatype translated to binding source format */ QString translateType(AbstractMetaType metatype, - const AbstractMetaClass *context, + const AbstractMetaClassCPtr &context, Options options = NoOption) const; /** @@ -153,7 +158,7 @@ protected: // Returns the full name of the type. static QString getFullTypeName(TypeEntryCPtr type); static QString getFullTypeName(const AbstractMetaType &type); - static QString getFullTypeName(const AbstractMetaClass *metaClass); + static QString getFullTypeName(const AbstractMetaClassCPtr &metaClass); /** * Returns the full qualified C++ name for an AbstractMetaType, but removing modifiers @@ -175,7 +180,7 @@ protected: QString *errorString = nullptr); static std::optional<DefaultValue> minimalConstructor(const ApiExtractorResult &api, - const AbstractMetaClass *metaClass, + const AbstractMetaClassCPtr &metaClass, QString *errorString = nullptr); /** @@ -209,6 +214,11 @@ protected: */ virtual QString subDirectoryForPackage(QString packageName = QString()) const; + static QString addGlobalScopePrefix(const QString &t); + static QString globalScopePrefix(const GeneratorContext &classContext); + + static QString m_gsp; + private: struct GeneratorPrivate; GeneratorPrivate *m_d; @@ -217,7 +227,7 @@ private: Q_DECLARE_OPERATORS_FOR_FLAGS(Generator::Options) Q_DECLARE_OPERATORS_FOR_FLAGS(Generator::FileNameFlags) -using GeneratorPtr = QSharedPointer<Generator>; +using GeneratorPtr = std::shared_ptr<Generator>; using Generators = QList<GeneratorPtr>; #endif // GENERATOR_H diff --git a/sources/shiboken6/generator/generatorcontext.h b/sources/shiboken6/generator/generatorcontext.h index 573f98758..2e58d4346 100644 --- a/sources/shiboken6/generator/generatorcontext.h +++ b/sources/shiboken6/generator/generatorcontext.h @@ -31,9 +31,9 @@ public: GeneratorContext() = default; - const AbstractMetaClass *metaClass() const { return m_metaClass; } + AbstractMetaClassCPtr metaClass() const { return m_metaClass; } const AbstractMetaType &preciseType() const { return m_preciseClassType; } - const AbstractMetaClass *pointeeClass() const { return m_pointeeClass; } + AbstractMetaClassCPtr pointeeClass() const { return m_pointeeClass; } bool forSmartPointer() const { return m_type == SmartPointer; } bool useWrapper() const { return m_type == WrappedClass; } @@ -44,8 +44,8 @@ public: QString effectiveClassName() const; private: - const AbstractMetaClass *m_metaClass = nullptr; - const AbstractMetaClass *m_pointeeClass = nullptr; + AbstractMetaClassCPtr m_metaClass; + AbstractMetaClassCPtr m_pointeeClass; AbstractMetaType m_preciseClassType; QString m_wrappername; Type m_type = Class; diff --git a/sources/shiboken6/generator/main.cpp b/sources/shiboken6/generator/main.cpp index 5c9d9320c..9871df206 100644 --- a/sources/shiboken6/generator/main.cpp +++ b/sources/shiboken6/generator/main.cpp @@ -9,12 +9,13 @@ #include <apiextractor.h> #include <apiextractorresult.h> +#include <exception.h> #include <fileout.h> #include <messages.h> +#include <optionsparser.h> #include <reporthandler.h> #include <typedatabase.h> -#include <QtCore/QCoreApplication> #include <QtCore/QDir> #include <QtCore/QFile> #include <QtCore/QLibrary> @@ -27,280 +28,8 @@ using namespace Qt::StringLiterals; -static const QChar clangOptionsSplitter = u','; -static const QChar keywordsSplitter = u','; -static const QChar dropTypeEntriesSplitter = u';'; -static const QChar apiVersionSplitter = u'|'; - -static inline QString keywordsOption() { return QStringLiteral("keywords"); } -static inline QString clangOptionOption() { return QStringLiteral("clang-option"); } -static inline QString clangOptionsOption() { return QStringLiteral("clang-options"); } -static inline QString compilerOption() { return QStringLiteral("compiler"); } -static inline QString compilerPathOption() { return QStringLiteral("compiler-path"); } -static inline QString platformOption() { return QStringLiteral("platform"); } -static inline QString apiVersionOption() { return QStringLiteral("api-version"); } -static inline QString dropTypeEntriesOption() { return QStringLiteral("drop-type-entries"); } -static inline QString languageLevelOption() { return QStringLiteral("language-level"); } -static inline QString includePathOption() { return QStringLiteral("include-paths"); } -static inline QString frameworkIncludePathOption() { return QStringLiteral("framework-include-paths"); } -static inline QString systemIncludePathOption() { return QStringLiteral("system-include-paths"); } -static inline QString typesystemPathOption() { return QStringLiteral("typesystem-paths"); } -static inline QString helpOption() { return QStringLiteral("help"); } -static inline QString diffOption() { return QStringLiteral("diff"); } -static inline QString useGlobalHeaderOption() { return QStringLiteral("use-global-header"); } -static inline QString dryrunOption() { return QStringLiteral("dry-run"); } -static inline QString skipDeprecatedOption() { return QStringLiteral("skip-deprecated"); } -static inline QString printBuiltinTypesOption() { return QStringLiteral("print-builtin-types"); } - static const char helpHint[] = "Note: use --help or -h for more information.\n"; - -using OptionDescriptions = Generator::OptionDescriptions; - -struct CommandLineArguments -{ - void addToOptionsList(const QString &option, - const QString &value); - void addToOptionsList(const QString &option, - const QStringList &value); - void addToOptionsList(const QString &option, - const QString &listValue, - QChar separator); - void addToOptionsPathList(const QString &option, - const QString &pathListValue) - { - addToOptionsList(option, pathListValue, QDir::listSeparator()); - } - - bool addCommonOption(const QString &option, const QString &value); - - QVariantMap options; // string,stringlist for path lists, etc. - QStringList positionalArguments; -}; - -void CommandLineArguments::addToOptionsList(const QString &option, - const QString &value) -{ - auto it = options.find(option); - if (it == options.end()) { - options.insert(option, QVariant(QStringList(value))); - } else { - auto list = it.value().toStringList(); - list += value; - options[option] = QVariant(list); - } -} - -void CommandLineArguments::addToOptionsList(const QString &option, - const QStringList &value) -{ - auto it = options.find(option); - if (it == options.end()) { - options.insert(option, QVariant(value)); - } else { - auto list = it.value().toStringList(); - list += value; - options[option] = QVariant(list); - } -} - -void CommandLineArguments::addToOptionsList(const QString &option, - const QString &listValue, - QChar separator) -{ - const auto newValues = listValue.split(separator, Qt::SkipEmptyParts); - addToOptionsList(option, newValues); -} - -// Add options common to project file and command line -bool CommandLineArguments::addCommonOption(const QString &option, - const QString &value) -{ - bool result = true; - if (option == compilerOption() || option == compilerPathOption() - || option == platformOption()) { - options.insert(option, value); - } else if (option == clangOptionOption()) { - options.insert(option, QStringList(value)); - } else if (option == clangOptionsOption()) { - addToOptionsList(option, value, clangOptionsSplitter); - } else if (option == apiVersionOption()) { - addToOptionsList(option, value, apiVersionSplitter); - } else if (option == keywordsOption()) { - addToOptionsList(option, value, keywordsSplitter); - } else if (option == dropTypeEntriesOption()) { - addToOptionsList(option, value, dropTypeEntriesSplitter); - } else { - result = false; - } - return result; -} - -static void printOptions(QTextStream &s, const OptionDescriptions &options) -{ - s.setFieldAlignment(QTextStream::AlignLeft); - for (const auto &od : options) { - if (!od.first.startsWith(u'-')) - s << "--"; - s << od.first; - if (od.second.isEmpty()) { - s << ", "; - } else { - s << Qt::endl; - const auto lines = QStringView{od.second}.split(u'\n'); - for (const auto &line : lines) - s << " " << line << Qt::endl; - s << Qt::endl; - } - } -} - -// Return the file command line option matching a project file keyword -static QString projectFileKeywordToCommandLineOption(const QString &p) -{ - if (p == u"include-path") - return includePathOption(); // "include-paths", ... - if (p == u"framework-include-path") - return frameworkIncludePathOption(); - if (p == u"typesystem-path") - return typesystemPathOption(); - if (p == u"system-include-paths") - return systemIncludePathOption(); - return {}; -} - -static void processProjectFileLine(const QByteArray &line, CommandLineArguments &args) -{ - if (line.isEmpty()) - return; - const QString lineS = QString::fromUtf8(line); - const auto split = line.indexOf(u'='); - if (split < 0) { - args.options.insert(lineS, QString{}); - return; - } - - const QString key = lineS.left(split).trimmed(); - const QString value = lineS.mid(split + 1).trimmed(); - const QString fileOption = projectFileKeywordToCommandLineOption(key); - if (fileOption.isEmpty()) { - if (key == u"header-file") { - args.positionalArguments.prepend(value); - } else if (key == u"typesystem-file") { - args.positionalArguments.append(value); - } else { - args.options.insert(key, value); - } - } else { - // Add single line value to the path list - args.addToOptionsList(fileOption, QDir::toNativeSeparators(value)); - } -} - -static std::optional<CommandLineArguments> - processProjectFile(const QString &appName, QFile &projectFile) -{ - QByteArray line = projectFile.readLine().trimmed(); - if (line.isEmpty() || line != "[generator-project]") { - std::cerr << qPrintable(appName) << ": first line of project file \"" - << qPrintable(projectFile.fileName()) - << "\" must be the string \"[generator-project]\"\n"; - return {}; - } - - CommandLineArguments args; - while (!projectFile.atEnd()) - processProjectFileLine(projectFile.readLine().trimmed(), args); - return args; -} - -static std::optional<CommandLineArguments> getProjectFileArguments() -{ - QStringList arguments = QCoreApplication::arguments(); - QString appName = arguments.constFirst(); - arguments.removeFirst(); - - QString projectFileName; - for (const QString &arg : std::as_const(arguments)) { - if (arg.startsWith(u"--project-file")) { - int split = arg.indexOf(u'='); - if (split > 0) - projectFileName = arg.mid(split + 1).trimmed(); - break; - } - } - - if (projectFileName.isEmpty()) - return CommandLineArguments{}; - - if (!QFile::exists(projectFileName)) { - std::cerr << qPrintable(appName) << ": Project file \"" - << qPrintable(projectFileName) << "\" not found.\n"; - return {}; - } - - QFile projectFile(projectFileName); - if (!projectFile.open(QIODevice::ReadOnly)) { - std::cerr << qPrintable(appName) << ": Cannot open project file \"" - << qPrintable(projectFileName) << "\" : " << qPrintable(projectFile.errorString()) - << '\n'; - return {}; - } - return processProjectFile(appName, projectFile); -} - -static void getCommandLineArg(QString arg, int &argNum, CommandLineArguments &args) -{ - if (arg.startsWith(u"--")) { - arg.remove(0, 2); - const auto split = arg.indexOf(u'='); - if (split < 0) { - args.options.insert(arg, QString()); - return; - } - const QString option = arg.left(split); - const QString value = arg.mid(split + 1).trimmed(); - if (args.addCommonOption(option, value)) { - } else if (option == includePathOption() || option == frameworkIncludePathOption() - || option == systemIncludePathOption() || option == typesystemPathOption()) { - // Add platform path-separator separated list value to path list - args.addToOptionsPathList(option, value); - } else { - args.options.insert(option, value); - } - return; - } - if (arg.startsWith(u'-')) { - arg.remove(0, 1); - if (arg.startsWith(u'I')) // Shorthand path arguments -I/usr/include... - args.addToOptionsPathList(includePathOption(), arg.mid(1)); - else if (arg.startsWith(u'F')) - args.addToOptionsPathList(frameworkIncludePathOption(), arg.mid(1)); - else if (arg.startsWith(u"isystem")) - args.addToOptionsPathList(systemIncludePathOption(), arg.mid(7)); - else if (arg.startsWith(u'T')) - args.addToOptionsPathList(typesystemPathOption(), arg.mid(1)); - else if (arg == u"h") - args.options.insert(helpOption(), QString()); - else if (arg.startsWith(u"std=")) - args.options.insert(languageLevelOption(), arg.mid(4)); - else - args.options.insert(arg, QString()); - return; - } - if (argNum < args.positionalArguments.size()) - args.positionalArguments[argNum] = arg; - else - args.positionalArguments.append(arg); - ++argNum; -} - -static void getCommandLineArgs(CommandLineArguments &args) -{ - const QStringList arguments = QCoreApplication::arguments(); - int argNum = 0; - for (qsizetype i = 1, size = arguments.size(); i < size; ++i) - getCommandLineArg(arguments.at(i).trimmed(), argNum, args); -} +static const char appName[] = "shiboken"; static inline Generators docGenerators() { @@ -318,66 +47,57 @@ static inline Generators shibokenGenerators() return result; } -static inline QString languageLevelDescription() +struct CommonOptions { - return u"C++ Language level (c++11..c++17, default="_s - + QLatin1StringView(clang::languageLevelOption(clang::emulatedCompilerLanguageLevel())) - + u')'; -} + QString generatorSet; + QString licenseComment; + QString outputDirectory = u"out"_s; + QStringList headers; + QString typeSystemFileName; + bool help = false; + bool version = false; + bool diff = false; + bool dryRun = false; + bool logUnmatched = false; + bool printBuiltinTypes = false; +}; -void printUsage() +class CommonOptionsParser : public OptionsParser { - const QChar pathSplitter = QDir::listSeparator(); - QTextStream s(stdout); - s << "Usage:\n " - << "shiboken [options] header-file(s) typesystem-file\n\n" - << "General options:\n"; - QString pathSyntax; - QTextStream(&pathSyntax) << "<path>[" << pathSplitter << "<path>" - << pathSplitter << "...]"; - OptionDescriptions generalOptions = { - {u"api-version=<\"package mask\">,<\"version\">"_s, - u"Specify the supported api version used to generate the bindings"_s}, +public: + explicit CommonOptionsParser(CommonOptions *o) : m_options(o) {} + + bool handleBoolOption(const QString &key, OptionSource source) override; + bool handleOption(const QString &key, const QString &value, OptionSource source) override; + + static OptionDescriptions optionDescriptions(); + +private: + CommonOptions *m_options; +}; + +OptionDescriptions CommonOptionsParser::optionDescriptions() +{ + return { {u"debug-level=[sparse|medium|full]"_s, u"Set the debug level"_s}, {u"documentation-only"_s, u"Do not generates any code, just the documentation"_s}, - {u"drop-type-entries=\"<TypeEntry0>[;TypeEntry1;...]\""_s, - u"Semicolon separated list of type system entries (classes, namespaces,\n" - "global functions and enums) to be dropped from generation."_s}, - {keywordsOption() + QStringLiteral("=keyword1[,keyword2,...]"), - u"A comma-separated list of keywords for conditional typesystem parsing"_s}, - {clangOptionOption(), - u"Option to be passed to clang"_s}, - {clangOptionsOption(), - u"A comma-separated list of options to be passed to clang"_s}, - {compilerOption() + u"=<type>"_s, + {u"compiler=<type>"_s, u"Emulated compiler type (g++, msvc, clang)"_s}, - {platformOption() + u"=<name>"_s, + {u"platform=<name>"_s, u"Emulated platform (windows, darwin, unix)"_s}, - {compilerPathOption() + u"=<file>"_s, + {u"compiler-path=<file>"_s, u"Path to the compiler for determining builtin include paths"_s}, - {u"-F<path>"_s, {} }, - {u"framework-include-paths="_s + pathSyntax, - u"Framework include paths used by the C++ parser"_s}, - {u"-isystem<path>"_s, {} }, - {u"system-include-paths="_s + pathSyntax, - u"System include paths used by the C++ parser"_s}, - {useGlobalHeaderOption(), - u"Use the global headers in generated code."_s}, {u"generator-set=<\"generator module\">"_s, u"generator-set to be used. e.g. qtdoc"_s}, - {skipDeprecatedOption(), - u"Skip deprecated functions"_s}, - {diffOption(), u"Print a diff of wrapper files"_s}, - {dryrunOption(), u"Dry run, do not generate wrapper files"_s}, + {u"diff"_s, u"Print a diff of wrapper files"_s}, + {u"dry-run"_s, u"Dry run, do not generate wrapper files"_s}, {u"-h"_s, {} }, - {helpOption(), u"Display this help and exit"_s}, + {u"help"_s, u"Display this help and exit"_s}, {u"-I<path>"_s, {} }, - {u"include-paths="_s + pathSyntax, + {u"include-paths="_s + OptionsParser::pathSyntax(), u"Include paths used by the C++ parser"_s}, - {languageLevelOption() + u"=, -std=<level>"_s, - languageLevelDescription()}, {u"license-file=<license-file>"_s, u"File used for copyright headers of generated files"_s}, {u"no-suppress-warnings"_s, @@ -388,339 +108,252 @@ void printUsage() u"text file containing a description of the binding project.\n" "Replaces and overrides command line arguments"_s}, {u"silent"_s, u"Avoid printing any message"_s}, - {u"-T<path>"_s, {} }, - {u"typesystem-paths="_s + pathSyntax, - u"Paths used when searching for typesystems"_s}, - {printBuiltinTypesOption(), + {u"print-builtin-types"_s, u"Print information about builtin types"_s}, {u"version"_s, u"Output version information and exit"_s} }; - printOptions(s, generalOptions); - - const Generators generators = shibokenGenerators() + docGenerators(); - for (const GeneratorPtr &generator : generators) { - const OptionDescriptions options = generator->options(); - if (!options.isEmpty()) { - s << Qt::endl << generator->name() << " options:\n\n"; - printOptions(s, generator->options()); +} + +bool CommonOptionsParser::handleBoolOption(const QString &key, OptionSource source) +{ + if (source == OptionSource::CommandLineSingleDash) { + if (key == u"h") { + m_options->help = true; + return true; + } + return false; + } + + if (key == u"version") { + m_options->version = true; + return true; + } + if (key == u"help") { + m_options->help = true; + return true; + } + if (key == u"diff") { + FileOut::setDiff(true); + return true; + } + if (key == u"dry-run") { + FileOut::setDryRun(true); + return true; + } + if (key == u"silent") { + ReportHandler::setSilent(true); + return true; + } + if (key == u"log-unmatched") { + m_options->logUnmatched = true; + return true; + } + if (key == u"print-builtin-types") { + m_options->printBuiltinTypes = true; + return true; + } + + return false; +} + +bool CommonOptionsParser::handleOption(const QString &key, const QString &value, + OptionSource source) +{ + if (source == OptionSource::CommandLineSingleDash) + return false; + + if (key == u"generator-set" || key == u"generatorSet" /* legacy */) { + m_options->generatorSet = value; + return true; + } + if (key == u"license-file") { + QFile licenseFile(value); + if (!licenseFile.open(QIODevice::ReadOnly)) + throw Exception(msgCannotOpenForReading(licenseFile)); + m_options->licenseComment = QString::fromUtf8(licenseFile.readAll()); + return true; + } + if (key == u"debug-level") { + if (!ReportHandler::setDebugLevelFromArg(value)) + throw Exception(u"Invalid debug level: "_s + value); + return true; + } + if (key == u"output-directory") { + m_options->outputDirectory = value; + return true; + } + if (key == u"compiler") { + if (!clang::setCompiler(value)) + throw Exception(u"Invalid value \""_s + value + u"\" passed to --compiler"_s); + return true; + } + if (key == u"compiler-path") { + clang::setCompilerPath(value); + return true; + } + if (key == u"platform") { + if (!clang::setPlatform(value)) + throw Exception(u"Invalid value \""_s + value + u"\" passed to --platform"_s); + return true; + } + + if (source == OptionSource::ProjectFile) { + if (key == u"header-file") { + m_options->headers.append(value); + return true; + } + if (key == u"typesystem-file") { + m_options->typeSystemFileName = value; + return true; } } + + return false; +} + +void printUsage() +{ + const auto generatorOptions = Generator::options(); + + QTextStream s(stdout); + s << "Usage:\n " + << "shiboken [options] header-file(s) typesystem-file\n\n" + << "General options:\n" + << CommonOptionsParser::optionDescriptions() + << ApiExtractor::options() + << TypeDatabase::options() + << "\nSource generator options:\n\n" << generatorOptions + << ShibokenGenerator::options(); + +#ifdef DOCSTRINGS_ENABLED + s << "\nDocumentation Generator options:\n\n" + << generatorOptions << QtDocGenerator::options(); +#endif } static inline void printVerAndBanner() { - std::cout << "shiboken v" SHIBOKEN_VERSION << std::endl; + std::cout << appName << " v" << SHIBOKEN_VERSION << std::endl; std::cout << "Copyright (C) 2016 The Qt Company Ltd." << std::endl; } -static inline void errorPrint(const QString &s) +static inline void errorPrint(const QString &s, const QStringList &arguments) { - QStringList arguments = QCoreApplication::arguments(); - arguments.pop_front(); - std::cerr << "shiboken: " << qPrintable(s) << "\nCommand line:\n"; + std::cerr << appName << ": " << qPrintable(s) << "\nCommand line:\n"; for (const auto &argument : arguments) std::cerr << " \"" << qPrintable(argument) << "\"\n"; } -static void parseIncludePathOption(const QString &option, HeaderType headerType, - CommandLineArguments &args, - ApiExtractor &extractor) -{ - const auto it = args.options.find(option); - if (it != args.options.end()) { - const auto includePathListList = it.value().toStringList(); - args.options.erase(it); - for (const QString &s : includePathListList) { - auto path = QFile::encodeName(QDir::cleanPath(s)); - extractor.addIncludePath(HeaderPath{path, headerType}); - } - } -} - -int shibokenMain(int argc, char *argv[]) +int shibokenMain(const QStringList &argV) { // PYSIDE-757: Request a deterministic ordering of QHash in the code model // and type system. - qSetGlobalQHashSeed(0); - // needed by qxmlpatterns - QCoreApplication app(argc, argv); + QHashSeed::setDeterministicGlobalSeed(); + ReportHandler::install(); if (ReportHandler::isDebug(ReportHandler::SparseDebug)) - qCInfo(lcShiboken()).noquote().nospace() << QCoreApplication::arguments().join(u' '); + qCInfo(lcShiboken()).noquote().nospace() << appName << ' ' << argV.join(u' '); - // Store command arguments in a map - const auto projectFileArgumentsOptional = getProjectFileArguments(); - if (!projectFileArgumentsOptional.has_value()) - return EXIT_FAILURE; - - const CommandLineArguments projectFileArguments = projectFileArgumentsOptional.value(); - CommandLineArguments args = projectFileArguments; - getCommandLineArgs(args); - Generators generators; + Options options; + options.setOptions(argV); - auto ait = args.options.find(u"version"_s); - if (ait != args.options.end()) { - args.options.erase(ait); + CommonOptions commonOptions; + { + CommonOptionsParser parser(&commonOptions); + parser.process(&options); + } + if (commonOptions.version) { printVerAndBanner(); return EXIT_SUCCESS; } - - QString generatorSet; - ait = args.options.find(u"generator-set"_s); - if (ait == args.options.end()) // Also check "generatorSet" command line argument for backward compatibility. - ait = args.options.find(u"generatorSet"_s); - if (ait != args.options.end()) { - generatorSet = ait.value().toString(); - args.options.erase(ait); + if (commonOptions.help) { + printUsage(); + return EXIT_SUCCESS; } + Generators generators; + + OptionsParserList optionParser; + optionParser.append(Generator::createOptionsParser()); + optionParser.append(TypeDatabase::instance()->createOptionsParser()); + ApiExtractor extractor; + optionParser.append(extractor.createOptionsParser()); + // Pre-defined generator sets. - if (generatorSet == u"qtdoc") { + if (commonOptions.generatorSet == u"qtdoc") { generators = docGenerators(); if (generators.isEmpty()) { - errorPrint(u"Doc strings extractions was not enabled in this shiboken build."_s); + errorPrint(u"Doc strings extractions was not enabled in this shiboken build."_s, argV); return EXIT_FAILURE; } - } else if (generatorSet.isEmpty() || generatorSet == u"shiboken") { +#ifdef DOCSTRINGS_ENABLED + optionParser.append(QtDocGenerator::createOptionsParser()); +#endif + } else if (commonOptions.generatorSet.isEmpty() || commonOptions.generatorSet == u"shiboken") { generators = shibokenGenerators(); + optionParser.append(ShibokenGenerator::createOptionsParser()); } else { - errorPrint(u"Unknown generator set, try \"shiboken\" or \"qtdoc\"."_s); + errorPrint(u"Unknown generator set, try \"shiboken\" or \"qtdoc\"."_s, argV); return EXIT_FAILURE; } - ait = args.options.find(u"help"_s); - if (ait != args.options.end()) { - args.options.erase(ait); - printUsage(); - return EXIT_SUCCESS; - } - - ait = args.options.find(diffOption()); - if (ait != args.options.end()) { - args.options.erase(ait); - FileOut::setDiff(true); - } - - ait = args.options.find(useGlobalHeaderOption()); - if (ait != args.options.end()) { - args.options.erase(ait); - ApiExtractor::setUseGlobalHeader(true); - } - - ait = args.options.find(dryrunOption()); - if (ait != args.options.end()) { - args.options.erase(ait); - FileOut::setDryRun(true); - } - - QString licenseComment; - ait = args.options.find(u"license-file"_s); - if (ait != args.options.end()) { - QFile licenseFile(ait.value().toString()); - args.options.erase(ait); - if (licenseFile.open(QIODevice::ReadOnly)) { - licenseComment = QString::fromUtf8(licenseFile.readAll()); - } else { - errorPrint(QStringLiteral("Could not open the file \"%1\" containing the license heading: %2"). - arg(QDir::toNativeSeparators(licenseFile.fileName()), licenseFile.errorString())); - return EXIT_FAILURE; - } - } - - QString outputDirectory = u"out"_s; - ait = args.options.find(u"output-directory"_s); - if (ait != args.options.end()) { - outputDirectory = ait.value().toString(); - args.options.erase(ait); - } - - if (!QDir(outputDirectory).exists()) { - if (!QDir().mkpath(outputDirectory)) { + if (!QDir(commonOptions.outputDirectory).exists()) { + if (!QDir().mkpath(commonOptions.outputDirectory)) { qCWarning(lcShiboken).noquote().nospace() - << "Can't create output directory: " << QDir::toNativeSeparators(outputDirectory); + << "Can't create output directory: " + << QDir::toNativeSeparators(commonOptions.outputDirectory); return EXIT_FAILURE; } } // Create and set-up API Extractor - ApiExtractor extractor; - extractor.setLogDirectory(outputDirectory); - ait = args.options.find(skipDeprecatedOption()); - if (ait != args.options.end()) { - extractor.setSkipDeprecated(true); - args.options.erase(ait); - } - - ait = args.options.find(u"silent"_s); - if (ait != args.options.end()) { - extractor.setSilent(true); - args.options.erase(ait); - } else { - ait = args.options.find(u"debug-level"_s); - if (ait != args.options.end()) { - const QString value = ait.value().toString(); - if (!ReportHandler::setDebugLevelFromArg(value)) { - errorPrint(u"Invalid debug level: "_s + value); - return EXIT_FAILURE; - } - args.options.erase(ait); - } - } - ait = args.options.find(u"no-suppress-warnings"_s); - if (ait != args.options.end()) { - args.options.erase(ait); - extractor.setSuppressWarnings(false); - } - ait = args.options.find(apiVersionOption()); - if (ait != args.options.end()) { - const QStringList &versions = ait.value().toStringList(); - args.options.erase(ait); - for (const QString &fullVersion : versions) { - QStringList parts = fullVersion.split(u','); - QString package; - QString version; - package = parts.size() == 1 ? u"*"_s : parts.constFirst(); - version = parts.constLast(); - if (!extractor.setApiVersion(package, version)) { - errorPrint(msgInvalidVersion(package, version)); - return EXIT_FAILURE; - } - } - } - - ait = args.options.find(dropTypeEntriesOption()); - if (ait != args.options.end()) { - extractor.setDropTypeEntries(ait.value().toStringList()); - args.options.erase(ait); - } - - ait = args.options.find(keywordsOption()); - if (ait != args.options.end()) { - extractor.setTypesystemKeywords(ait.value().toStringList()); - args.options.erase(ait); - } - - ait = args.options.find(typesystemPathOption()); - if (ait != args.options.end()) { - extractor.addTypesystemSearchPath(ait.value().toStringList()); - args.options.erase(ait); - } - - ait = args.options.find(clangOptionsOption()); - if (ait != args.options.end()) { - extractor.setClangOptions(ait.value().toStringList()); - args.options.erase(ait); - } - - ait = args.options.find(compilerOption()); - if (ait != args.options.end()) { - const QString name = ait.value().toString(); - if (!clang::setCompiler(name)) { - errorPrint(u"Invalid value \""_s + name + u"\" passed to --compiler"_s); + extractor.setLogDirectory(commonOptions.outputDirectory); + + if (commonOptions.typeSystemFileName.isEmpty() && commonOptions.headers.isEmpty()) { + if (options.positionalArguments.size() < 2) { + errorPrint(u"Insufficient positional arguments, specify header-file and typesystem-file."_s, + argV); + std::cout << '\n'; + printUsage(); return EXIT_FAILURE; } - args.options.erase(ait); - } - ait = args.options.find(printBuiltinTypesOption()); - const bool printBuiltinTypes = ait != args.options.end(); - if (printBuiltinTypes) - args.options.erase(ait); - - ait = args.options.find(compilerPathOption()); - if (ait != args.options.end()) { - clang::setCompilerPath(ait.value().toString()); - args.options.erase(ait); + commonOptions.typeSystemFileName = options.positionalArguments.takeLast(); + commonOptions.headers = options.positionalArguments; } - ait = args.options.find(platformOption()); - if (ait != args.options.end()) { - const QString name = ait.value().toString(); - if (!clang::setPlatform(name)) { - errorPrint(u"Invalid value \""_s + name + u"\" passed to --platform"_s); - return EXIT_FAILURE; - } - args.options.erase(ait); - } - - parseIncludePathOption(includePathOption(), HeaderType::Standard, - args, extractor); - parseIncludePathOption(frameworkIncludePathOption(), HeaderType::Framework, - args, extractor); - parseIncludePathOption(systemIncludePathOption(), HeaderType::System, - args, extractor); - - if (args.positionalArguments.size() < 2) { - errorPrint(u"Insufficient positional arguments, specify header-file and typesystem-file."_s); - std::cout << '\n'; - printUsage(); - return EXIT_FAILURE; - } - - const QString typeSystemFileName = args.positionalArguments.takeLast(); - QString messagePrefix = QFileInfo(typeSystemFileName).baseName(); + QString messagePrefix = QFileInfo(commonOptions.typeSystemFileName).baseName(); if (messagePrefix.startsWith(u"typesystem_")) messagePrefix.remove(0, 11); ReportHandler::setPrefix(u'(' + messagePrefix + u')'); QFileInfoList cppFileNames; - for (const QString &cppFileName : std::as_const(args.positionalArguments)) { + for (const QString &cppFileName : std::as_const(commonOptions.headers)) { const QFileInfo cppFileNameFi(cppFileName); if (!cppFileNameFi.isFile() && !cppFileNameFi.isSymLink()) { - errorPrint(u'"' + cppFileName + u"\" does not exist."_s); + errorPrint(u'"' + cppFileName + u"\" does not exist."_s, argV); return EXIT_FAILURE; } cppFileNames.append(cppFileNameFi); } - // Pass option to all generators (Cpp/Header generator have the same options) - for (ait = args.options.begin(); ait != args.options.end(); ) { - bool found = false; - for (const GeneratorPtr &generator : std::as_const(generators)) - found |= generator->handleOption(ait.key(), ait.value().toString()); - if (found) - ait = args.options.erase(ait); - else - ++ait; - } - - ait = args.options.find(languageLevelOption()); - if (ait != args.options.end()) { - const QByteArray languageLevelBA = ait.value().toString().toLatin1(); - args.options.erase(ait); - const LanguageLevel level = clang::languageLevelFromOption(languageLevelBA.constData()); - if (level == LanguageLevel::Default) { - std::cout << "Invalid argument for language level: \"" - << languageLevelBA.constData() << "\"\n" << helpHint; - return EXIT_FAILURE; - } - extractor.setLanguageLevel(level); - } - - /* Make sure to remove the project file's arguments (if any) and - * --project-file, also the arguments of each generator before - * checking if there isn't any existing arguments in argsHandler. - */ - args.options.remove(u"project-file"_s); - for (auto it = projectFileArguments.options.cbegin(), end = projectFileArguments.options.cend(); - it != end; ++it) { - args.options.remove(it.key()); - } + optionParser.process(&options); + optionParser.clear(); - if (!args.options.isEmpty()) { - errorPrint(msgLeftOverArguments(args.options)); + if (!options.boolOptions.isEmpty() || !options.valueOptions.isEmpty()) { + errorPrint(msgLeftOverArguments(options.msgUnprocessedOptions(), argV), argV); std::cout << helpHint; return EXIT_FAILURE; } - if (typeSystemFileName.isEmpty()) { + if (commonOptions.typeSystemFileName.isEmpty()) { std::cout << "You must specify a Type System file." << std::endl << helpHint; return EXIT_FAILURE; } extractor.setCppFileNames(cppFileNames); - extractor.setTypeSystem(typeSystemFileName); + extractor.setTypeSystem(commonOptions.typeSystemFileName); ApiExtractorFlags apiExtractorFlags; if (generators.constFirst()->usePySideExtensions()) @@ -730,7 +363,7 @@ int shibokenMain(int argc, char *argv[]) const std::optional<ApiExtractorResult> apiOpt = extractor.run(apiExtractorFlags); if (!apiOpt.has_value()) { - errorPrint(u"Error running ApiExtractor."_s); + errorPrint(u"Error running ApiExtractor."_s, argV); return EXIT_FAILURE; } @@ -743,35 +376,59 @@ int shibokenMain(int argc, char *argv[]) << "\n\nType datase:\n" << *TypeDatabase::instance(); } - if (printBuiltinTypes) + if (commonOptions.printBuiltinTypes) TypeDatabase::instance()->formatBuiltinTypes(qInfo()); for (const GeneratorPtr &g : std::as_const(generators)) { - g->setOutputDirectory(outputDirectory); - g->setLicenseComment(licenseComment); - ReportHandler::startProgress(QByteArray("Running ") + g->name() + "..."); + g->setOutputDirectory(commonOptions.outputDirectory); + g->setLicenseComment(commonOptions.licenseComment); + ReportHandler::startProgress("Ran "_ba + g->name() + '.'); const bool ok = g->setup(apiOpt.value()) && g->generate(); ReportHandler::endProgress(); if (!ok) { errorPrint(u"Error running generator: "_s - + QLatin1StringView(g->name()) + u'.'); + + QLatin1StringView(g->name()) + u'.', argV); return EXIT_FAILURE; } } + if (commonOptions.logUnmatched) + TypeDatabase::instance()->logUnmatched(); + const QByteArray doneMessage = ReportHandler::doneMessage(); std::cout << doneMessage.constData() << std::endl; return EXIT_SUCCESS; } +#ifndef Q_OS_WIN + +static inline QString argvToString(const char *arg) +{ + return QString::fromLocal8Bit(arg); +} + int main(int argc, char *argv[]) +#else + +static inline QString argvToString(const wchar_t *arg) +{ + return QString::fromWCharArray(arg); +} + +int wmain(int argc, wchar_t *argv[]) +#endif { int ex = EXIT_SUCCESS; + + QStringList argV; + argV.reserve(argc - 1); + std::transform(argv + 1, argv + argc, std::back_inserter(argV), argvToString); + try { - ex = shibokenMain(argc, argv); + ex = shibokenMain(argV); } catch (const std::exception &e) { - std::cerr << e.what() << std::endl; + std::cerr << appName << " error: " << e.what() << std::endl; ex = EXIT_FAILURE; } return ex; diff --git a/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp b/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp index cf16efaa0..1634a7e83 100644 --- a/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp +++ b/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp @@ -15,6 +15,7 @@ #include <abstractmetafield.h> #include <abstractmetafunction.h> #include <abstractmetalang.h> +#include "abstractmetalang_helpers.h" #include <fileout.h> #include <messages.h> #include <modifications.h> @@ -25,6 +26,8 @@ #include <functiontypeentry.h> #include <enumtypeentry.h> #include <complextypeentry.h> +#include <flagstypeentry.h> +#include <primitivetypeentry.h> #include <qtdocparser.h> #include <doxygenparser.h> @@ -36,12 +39,36 @@ #include <QtCore/QJsonArray> #include <QtCore/QJsonDocument> #include <QtCore/QJsonObject> +#include <QtCore/QSet> #include <algorithm> #include <limits> using namespace Qt::StringLiterals; +static inline QString classScope(const AbstractMetaClassCPtr &metaClass) +{ + return metaClass->fullName(); +} + +struct DocPackage +{ + QStringList classPages; + QStringList decoratorPages; + AbstractMetaFunctionCList globalFunctions; + AbstractMetaEnumList globalEnums; +}; + +struct DocGeneratorOptions +{ + QtXmlToSphinxParameters parameters; + QString extraSectionDir; + QString additionalDocumentationList; + QString inheritanceFile; + bool doxygen = false; + bool inheritanceDiagram = true; +}; + struct GeneratorDocumentation { struct Property @@ -55,8 +82,7 @@ struct GeneratorDocumentation AbstractMetaFunctionCPtr notify; }; - AbstractMetaFunctionCList constructors; - AbstractMetaFunctionCList allFunctions; // Except constructors + AbstractMetaFunctionCList allFunctions; AbstractMetaFunctionCList tocNormalFunctions; // Index lists AbstractMetaFunctionCList tocVirtuals; AbstractMetaFunctionCList tocSignalFunctions; @@ -72,19 +98,18 @@ static bool operator<(const GeneratorDocumentation::Property &lhs, return lhs.name < rhs.name; } -static QString propertyRefTarget(const AbstractMetaClass *cppClass, const QString &name) +static QString propertyRefTarget(const QString &name) { - QString result = cppClass->fullName() + u'.' + name; - result.replace(u"::"_s, u"."_s); + QString result = name; // For sphinx referencing, disambiguate the target from the getter name - // by inserting an invisible "Hangul choseong filler" character. - result.insert(1, QChar(0x115F)); + // by appending an invisible "Hangul choseong filler" character. + result.append(QChar(0x115F)); return result; } -static inline QString additionalDocumentationOption() { return QStringLiteral("additional-documentation"); } +constexpr auto additionalDocumentationOption = "additional-documentation"_L1; -static inline QString none() { return QStringLiteral("None"); } +constexpr auto none = "None"_L1; static bool shouldSkip(const AbstractMetaFunctionCPtr &func) { @@ -121,7 +146,14 @@ static bool shouldSkip(const AbstractMetaFunctionCPtr &func) static bool functionSort(const AbstractMetaFunctionCPtr &func1, const AbstractMetaFunctionCPtr &func2) { - return func1->name() < func2->name(); + const bool ctor1 = func1->isConstructor(); + if (ctor1 != func2->isConstructor()) + return ctor1; + const QString &name1 = func1->name(); + const QString &name2 = func2->name(); + if (name1 != name2) + return name1 < name2; + return func1->arguments().size() < func2->arguments().size(); } static inline QVersionNumber versionOf(const TypeEntryCPtr &te) @@ -131,82 +163,144 @@ static inline QVersionNumber versionOf(const TypeEntryCPtr &te) if (!version.isNull() && version > QVersionNumber(0, 0)) return version; } - return QVersionNumber(); + return {}; } -// Format a documentation reference (meth/attr): ":meth:`name<target>`" -// We do not use the short form ":meth:`~target`" since that adds parentheses () -// for functions where we list the parameters instead. struct docRef { - explicit docRef(const char *kind, const QString &name, const AbstractMetaClass *cppClass) : - m_kind(kind), m_name(name), m_cppClass(cppClass) {} + explicit docRef(const char *kind, QAnyStringView name) : + m_kind(kind), m_name(name) {} const char *m_kind; - const QString &m_name; - const AbstractMetaClass *m_cppClass; + QAnyStringView m_name; }; static TextStream &operator<<(TextStream &s, const docRef &dr) { - QString className = dr.m_cppClass->fullName(); - className.replace(u"::"_s, u"."_s); - s << ':' << dr.m_kind << ":`" << dr.m_name << '<'; - if (!dr.m_name.startsWith(className)) - s << className << '.'; - s << dr.m_name << ">`"; + s << ':' << dr.m_kind << ":`" << dr.m_name << '`'; return s; } +static QString fileNameToTocEntry(const QString &fileName) +{ + constexpr auto rstSuffix = ".rst"_L1; + + QString result = fileName; + if (result.endsWith(rstSuffix)) + result.chop(rstSuffix.size()); // Remove the .rst extension + // skip namespace if necessary + auto lastDot = result.lastIndexOf(u'.'); + if (lastDot != -1) + result.remove(0, lastDot + 1); + return result; +} + +static void readExtraDoc(const QFileInfo &fi, + const QString &moduleName, + const QString &outputDir, + DocPackage *docPackage, QStringList *extraTocEntries) +{ + // Strip to "Property.rst" in output directory + const QString newFileName = fi.fileName().mid(moduleName.size() + 1); + QFile sourceFile(fi.absoluteFilePath()); + if (!sourceFile.open(QIODevice::ReadOnly|QIODevice::Text)) { + qCWarning(lcShibokenDoc, "%s", qPrintable(msgCannotOpenForReading(sourceFile))); + return; + } + const QByteArray contents = sourceFile.readAll(); + sourceFile.close(); + QFile targetFile(outputDir + u'/' + newFileName); + if (!targetFile.open(QIODevice::WriteOnly|QIODevice::Text)) { + qCWarning(lcShibokenDoc, "%s", qPrintable(msgCannotOpenForWriting(targetFile))); + return; + } + targetFile.write(contents); + if (contents.contains("decorator::")) + docPackage->decoratorPages.append(newFileName); + else + docPackage->classPages.append(newFileName); + extraTocEntries->append(fileNameToTocEntry(newFileName)); +} + // Format a short documentation reference (automatically dropping the prefix // by using '~'), usable for property/attributes ("attr"). struct shortDocRef { - explicit shortDocRef(const char *kind, const QString &target) : - m_kind(kind), m_target(target) {} + explicit shortDocRef(const char *kind, QAnyStringView name) : + m_kind(kind), m_name(name) {} const char *m_kind; - const QString &m_target; + QAnyStringView m_name; }; static TextStream &operator<<(TextStream &s, const shortDocRef &sdr) { - s << ':' << sdr.m_kind << ":`~" << sdr.m_target << '`'; + s << ':' << sdr.m_kind << ":`~" << sdr.m_name << '`'; return s; } struct functionRef : public docRef { - explicit functionRef(const QString &name, const AbstractMetaClass *cppClass) : - docRef("meth", name, cppClass) {} + explicit functionRef(QAnyStringView name) : docRef("meth", name) {} }; -struct functionTocEntry // Format a TOC entry for a function +struct classRef : public shortDocRef { - explicit functionTocEntry(const AbstractMetaFunctionCPtr& func, - const AbstractMetaClass *cppClass) : - m_func(func), m_cppClass(cppClass) {} + explicit classRef(QAnyStringView name) : shortDocRef("class", name) {} +}; - AbstractMetaFunctionCPtr m_func; - const AbstractMetaClass *m_cppClass; +struct propRef : public shortDocRef // Attribute/property (short) reference +{ + explicit propRef(const QString &target) : + shortDocRef("attr", target) {} }; -static TextStream &operator<<(TextStream &s, const functionTocEntry &ft) +struct headline { - s << functionRef(QtDocGenerator::getFuncName(ft.m_func), ft.m_cppClass) - << ' ' << QtDocGenerator::formatArgs(ft.m_func); + explicit headline(QAnyStringView title, char underLineChar = '-') : + m_title(title), m_underLineChar(underLineChar) {} + + QAnyStringView m_title; + char m_underLineChar; +}; + +static TextStream &operator<<(TextStream &s, const headline &h) +{ + s << h.m_title << '\n' << Pad(h.m_underLineChar, h.m_title.size()) << "\n\n"; return s; } -struct propRef : public shortDocRef // Attribute/property (short) reference +struct pyClass { - explicit propRef(const QString &target) : - shortDocRef("attr", target) {} + explicit pyClass(QAnyStringView name) : m_name(name) {} + + QAnyStringView m_name; +}; + +static TextStream &operator<<(TextStream &s, pyClass c) +{ + s << ".. py:class:: " << c.m_name << "\n\n"; + return s; +} + +struct currentModule +{ + explicit currentModule(QAnyStringView module) : m_module(module) {} + + QAnyStringView m_module; }; +static TextStream &operator<<(TextStream &s, const currentModule &m) +{ + s << ".. currentmodule:: " << m.m_module << "\n\n\n"; + return s; +} + +DocGeneratorOptions QtDocGenerator::m_options; + QtDocGenerator::QtDocGenerator() { - m_parameters.snippetComparison = + m_options.parameters.snippetComparison = ReportHandler::debugLevel() >= ReportHandler::FullDebug; } @@ -231,28 +325,23 @@ QString QtDocGenerator::fileNameForContext(const GeneratorContext &context) cons } void QtDocGenerator::writeFormattedBriefText(TextStream &s, const Documentation &doc, - const AbstractMetaClass *metaclass) const + const QString &scope) const { - writeFormattedText(s, doc.brief(), doc.format(), metaclass); + writeFormattedText(s, doc.brief(), doc.format(), scope); } void QtDocGenerator::writeFormattedDetailedText(TextStream &s, const Documentation &doc, - const AbstractMetaClass *metaclass) const + const QString &scope) const { - writeFormattedText(s, doc.detailed(), doc.format(), metaclass); + writeFormattedText(s, doc.detailed(), doc.format(), scope); } void QtDocGenerator::writeFormattedText(TextStream &s, const QString &doc, Documentation::Format format, - const AbstractMetaClass *metaClass) const + const QString &scope) const { - QString metaClassName; - - if (metaClass) - metaClassName = metaClass->fullName(); - if (format == Documentation::Native) { - QtXmlToSphinx x(this, m_parameters, doc, metaClassName); + QtXmlToSphinx x(this, m_options.parameters, doc, scope); s << x; } else { const auto lines = QStringView{doc}.split(u'\n'); @@ -276,50 +365,71 @@ void QtDocGenerator::writeFormattedText(TextStream &s, const QString &doc, s << '\n'; } -static void writeInheritedByList(TextStream &s, const AbstractMetaClass *metaClass, +static void writeInheritanceList(TextStream &s, const AbstractMetaClassCList& classes, + const char *label) +{ + s << "**" << label << ":** "; + for (qsizetype i = 0, size = classes.size(); i < size; ++i) { + if (i > 0) + s << ", "; + s << classRef(classes.at(i)->fullName()); + } + s << "\n\n"; +} + +static void writeInheritedByList(TextStream &s, const AbstractMetaClassCPtr &metaClass, const AbstractMetaClassCList& allClasses) { AbstractMetaClassCList res; - for (auto c : allClasses) { - if (c != metaClass && c->inheritsFrom(metaClass)) + for (const auto &c : allClasses) { + if (c != metaClass && inheritsFrom(c, metaClass)) res << c; } - if (res.isEmpty()) - return; + if (!res.isEmpty()) + writeInheritanceList(s, res, "Inherited by"); +} + +static void writeInheritedFromList(TextStream &s, const AbstractMetaClassCPtr &metaClass) +{ + AbstractMetaClassCList res; - s << "**Inherited by:** "; - QStringList classes; - for (auto c : std::as_const(res)) - classes << u":ref:`"_s + c->name() + u'`'; - s << classes.join(u", "_s) << "\n\n"; + recurseClassHierarchy(metaClass, [&res, metaClass](const AbstractMetaClassCPtr &c) { + if (c.get() != metaClass.get()) + res.append(c); + return false; + }); + + if (!res.isEmpty()) + writeInheritanceList(s, res, "Inherits from"); } void QtDocGenerator::generateClass(TextStream &s, const GeneratorContext &classContext) { - const AbstractMetaClass *metaClass = classContext.metaClass(); + AbstractMetaClassCPtr metaClass = classContext.metaClass(); qCDebug(lcShibokenDoc).noquote().nospace() << "Generating Documentation for " << metaClass->fullName(); - m_packages[metaClass->package()] << fileNameForContext(classContext); + m_packages[metaClass->package()].classPages << fileNameForContext(classContext); m_docParser->setPackageName(metaClass->package()); - m_docParser->fillDocumentation(const_cast<AbstractMetaClass*>(metaClass)); - - QString className = metaClass->name(); - s << ".. _" << className << ":" << "\n\n"; - s << ".. currentmodule:: " << metaClass->package() << "\n\n\n"; + m_docParser->fillDocumentation(std::const_pointer_cast<AbstractMetaClass>(metaClass)); - s << className << '\n'; - s << Pad('*', className.size()) << "\n\n"; + s << currentModule(metaClass->package()) << pyClass(metaClass->name()); + Indentation indent(s); auto documentation = metaClass->documentation(); + const QString scope = classScope(metaClass); if (documentation.hasBrief()) - writeFormattedBriefText(s, documentation, metaClass); - - s << ".. inheritance-diagram:: " << metaClass->fullName()<< '\n' - << " :parts: 2\n\n"; - // TODO: This would be a parameter in the future... + writeFormattedBriefText(s, documentation, scope); + if (!metaClass->baseClasses().isEmpty()) { + if (m_options.inheritanceDiagram) { + s << ".. inheritance-diagram:: " << metaClass->fullName()<< '\n' + << " :parts: 2\n\n"; + } else { + writeInheritedFromList(s, metaClass); + } + } writeInheritedByList(s, metaClass, api().classes()); @@ -332,73 +442,68 @@ void QtDocGenerator::generateClass(TextStream &s, const GeneratorContext &classC const GeneratorDocumentation doc = generatorDocumentation(metaClass); if (!doc.allFunctions.isEmpty() || !doc.properties.isEmpty()) { - s << "\nSynopsis\n--------\n\n"; - writePropertyToc(s, doc, metaClass); - writeFunctionToc(s, u"Functions"_s, metaClass, doc.tocNormalFunctions); - writeFunctionToc(s, u"Virtual functions"_s, metaClass, doc.tocVirtuals); - writeFunctionToc(s, u"Slots"_s, metaClass, doc.tocSlotFunctions); - writeFunctionToc(s, u"Signals"_s, metaClass, doc.tocSignalFunctions); - writeFunctionToc(s, u"Static functions"_s, metaClass, doc.tocStaticFunctions); + s << '\n' << headline("Synopsis"); + writePropertyToc(s, doc); + writeFunctionToc(s, u"Methods"_s, doc.tocNormalFunctions); + writeFunctionToc(s, u"Virtual methods"_s, doc.tocVirtuals); + writeFunctionToc(s, u"Slots"_s, doc.tocSlotFunctions); + writeFunctionToc(s, u"Signals"_s, doc.tocSignalFunctions); + writeFunctionToc(s, u"Static functions"_s, doc.tocStaticFunctions); } - s << "\nDetailed Description\n" - "--------------------\n\n" - << ".. _More:\n"; + s << "\n.. note::\n" + " This documentation may contain snippets that were automatically\n" + " translated from C++ to Python. We always welcome contributions\n" + " to the snippet translation. If you see an issue with the\n" + " translation, you can also let us know by creating a ticket on\n" + " https:/bugreports.qt.io/projects/PYSIDE\n\n"; - writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, metaClass, nullptr); - if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, metaClass, nullptr)) - writeFormattedDetailedText(s, documentation, metaClass); + s << '\n' << headline("Detailed Description") << ".. _More:\n"; - if (!metaClass->isNamespace()) - writeConstructors(s, metaClass, doc.constructors); + writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, metaClass); + if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, metaClass)) + writeFormattedDetailedText(s, documentation, scope); + writeInjectDocumentation(s, TypeSystem::DocModificationAppend, metaClass); + + writeEnums(s, metaClass->enums(), scope); if (!doc.properties.isEmpty()) writeProperties(s, doc, metaClass); - writeEnums(s, metaClass); if (!metaClass->isNamespace()) writeFields(s, metaClass); - QString lastName; - for (const auto &func : std::as_const(doc.allFunctions)) { - const bool indexed = func->name() != lastName; - lastName = func->name(); - s << (func->isStatic() ? ".. py:staticmethod:: " : ".. py:method:: "); - writeFunction(s, metaClass, func, indexed); - } - - writeInjectDocumentation(s, TypeSystem::DocModificationAppend, metaClass, nullptr); + writeFunctions(s, doc.allFunctions, metaClass, scope); } void QtDocGenerator::writeFunctionToc(TextStream &s, const QString &title, - const AbstractMetaClass *cppClass, const AbstractMetaFunctionCList &functions) { if (!functions.isEmpty()) { - s << title << '\n' - << Pad('^', title.size()) << '\n'; - - s << ".. container:: function_list\n\n" << indent; - for (const auto &func : functions) - s << "* def " << functionTocEntry(func, cppClass) << '\n'; + s << headline(title, '^') + << ".. container:: function_list\n\n" << indent; + // Functions are sorted by the Metabuilder; erase overloads + QStringList toc; + toc.reserve(functions.size()); + std::transform(functions.cbegin(), functions.end(), + std::back_inserter(toc), getFuncName); + toc.erase(std::unique(toc.begin(), toc.end()), toc.end()); + for (const auto &func : toc) + s << "* def " << functionRef(func) << '\n'; s << outdent << "\n\n"; } } void QtDocGenerator::writePropertyToc(TextStream &s, - const GeneratorDocumentation &doc, - const AbstractMetaClass *cppClass) + const GeneratorDocumentation &doc) { if (doc.properties.isEmpty()) return; - const QString title = u"Properties"_s; - s << title << '\n' - << Pad('^', title.size()) << '\n'; - - s << ".. container:: function_list\n\n" << indent; + s << headline("Properties", '^') + << ".. container:: function_list\n\n" << indent; for (const auto &prop : doc.properties) { - s << "* " << propRef(propertyRefTarget(cppClass, prop.name)); + s << "* " << propRef(propertyRefTarget(prop.name)); if (prop.documentation.hasBrief()) s << " - " << prop.documentation.brief(); s << '\n'; @@ -408,38 +513,39 @@ void QtDocGenerator::writePropertyToc(TextStream &s, void QtDocGenerator::writeProperties(TextStream &s, const GeneratorDocumentation &doc, - const AbstractMetaClass *cppClass) const + const AbstractMetaClassCPtr &cppClass) const { s << "\n.. note:: Properties can be used directly when " << "``from __feature__ import true_property`` is used or via accessor " << "functions otherwise.\n\n"; + const QString scope = classScope(cppClass); for (const auto &prop : doc.properties) { const QString type = translateToPythonType(prop.type, cppClass, /* createRef */ false); - s << ".. py:property:: " << propertyRefTarget(cppClass, prop.name) + s << ".. py:property:: " << propertyRefTarget(prop.name) << "\n :type: " << type << "\n\n\n"; if (!prop.documentation.isEmpty()) - writeFormattedText(s, prop.documentation.detailed(), Documentation::Native, cppClass); + writeFormattedText(s, prop.documentation.detailed(), Documentation::Native, scope); s << "**Access functions:**\n"; - if (!prop.getter.isNull()) - s << " * " << functionTocEntry(prop.getter, cppClass) << '\n'; - if (!prop.setter.isNull()) - s << " * " << functionTocEntry(prop.setter, cppClass) << '\n'; - if (!prop.reset.isNull()) - s << " * " << functionTocEntry(prop.reset, cppClass) << '\n'; - if (!prop.notify.isNull()) - s << " * Signal " << functionTocEntry(prop.notify, cppClass) << '\n'; + if (prop.getter) + s << " * " << functionRef(prop.getter->name()) << '\n'; + if (prop.setter) + s << " * " << functionRef(prop.setter->name()) << '\n'; + if (prop.reset) + s << " * " << functionRef(prop.reset->name()) << '\n'; + if (prop.notify) + s << " * Signal " << functionRef(prop.notify->name()) << '\n'; s << '\n'; } } -void QtDocGenerator::writeEnums(TextStream &s, const AbstractMetaClass *cppClass) const +void QtDocGenerator::writeEnums(TextStream &s, const AbstractMetaEnumList &enums, + const QString &scope) const { - static const QString section_title = u".. attribute:: "_s; - - for (const AbstractMetaEnum &en : cppClass->enums()) { - s << section_title << cppClass->fullName() << '.' << en.name() << "\n\n"; - writeFormattedDetailedText(s, en.documentation(), cppClass); + for (const AbstractMetaEnum &en : enums) { + s << pyClass(en.name()); + Indentation indent(s); + writeFormattedDetailedText(s, en.documentation(), scope); const auto version = versionOf(en.typeEntry()); if (!version.isNull()) s << rstVersionAdded(version); @@ -447,66 +553,17 @@ void QtDocGenerator::writeEnums(TextStream &s, const AbstractMetaClass *cppClass } -void QtDocGenerator::writeFields(TextStream &s, const AbstractMetaClass *cppClass) const +void QtDocGenerator::writeFields(TextStream &s, const AbstractMetaClassCPtr &cppClass) const { - static const QString section_title = u".. attribute:: "_s; + constexpr auto section_title = ".. attribute:: "_L1; + const QString scope = classScope(cppClass); for (const AbstractMetaField &field : cppClass->fields()) { s << section_title << cppClass->fullName() << "." << field.name() << "\n\n"; - writeFormattedDetailedText(s, field.documentation(), cppClass); + writeFormattedDetailedText(s, field.documentation(), scope); } } -void QtDocGenerator::writeConstructors(TextStream &s, const AbstractMetaClass *cppClass, - const AbstractMetaFunctionCList &constructors) const -{ - static const QString sectionTitle = u".. class:: "_s; - - bool first = true; - QHash<QString, AbstractMetaArgument> arg_map; - - if (constructors.isEmpty()) { - s << sectionTitle << cppClass->fullName(); - } else { - QByteArray pad; - for (const auto &func : constructors) { - s << pad; - if (first) { - first = false; - s << sectionTitle; - pad = QByteArray(sectionTitle.size(), ' '); - } - s << functionSignature(cppClass, func) << "\n\n"; - - const auto version = versionOf(func->typeEntry()); - if (!version.isNull()) - s << pad << rstVersionAdded(version); - if (func->isDeprecated()) - s << pad << rstDeprecationNote("constructor"); - - const AbstractMetaArgumentList &arguments = func->arguments(); - for (const AbstractMetaArgument &arg : arguments) { - if (!arg_map.contains(arg.name())) { - arg_map.insert(arg.name(), arg); - } - } - } - } - - s << '\n'; - - for (auto it = arg_map.cbegin(), end = arg_map.cend(); it != end; ++it) { - s.indent(2); - writeParameterType(s, cppClass, it.value()); - s.outdent(2); - } - - s << '\n'; - - for (const auto &func : constructors) - writeFormattedDetailedText(s, func->documentation(), cppClass); -} - QString QtDocGenerator::formatArgs(const AbstractMetaFunctionCPtr &func) { QString ret = u"("_s; @@ -538,13 +595,13 @@ QString QtDocGenerator::formatArgs(const AbstractMetaFunctionCPtr &func) || defValue.startsWith(u"QList")) { defValue = u"list()"_s; } else if (defValue == u"QVariant()") { - defValue = none(); + defValue = none; } else { defValue.replace(u"::"_s, u"."_s); if (defValue == u"nullptr") - defValue = none(); + defValue = none; else if (defValue == u"0" && arg.type().isObject()) - defValue = none(); + defValue = none; } ret += u'=' + defValue; } @@ -612,25 +669,21 @@ void QtDocGenerator::writeDocSnips(TextStream &s, } } -bool QtDocGenerator::writeInjectDocumentation(TextStream &s, - TypeSystem::DocModificationMode mode, - const AbstractMetaClass *cppClass, - const AbstractMetaFunctionCPtr &func) +bool QtDocGenerator::writeDocModifications(TextStream &s, + const DocModificationList &mods, + TypeSystem::DocModificationMode mode, + const QString &scope) const { - Indentation indentation(s); bool didSomething = false; - - const DocModificationList mods = DocParser::getDocModifications(cppClass, func); - for (const DocModification &mod : mods) { if (mod.mode() == mode) { switch (mod.format()) { case TypeSystem::NativeCode: - writeFormattedText(s, mod.code(), Documentation::Native, cppClass); + writeFormattedText(s, mod.code(), Documentation::Native, scope); didSomething = true; break; case TypeSystem::TargetLangCode: - writeFormattedText(s, mod.code(), Documentation::Target, cppClass); + writeFormattedText(s, mod.code(), Documentation::Target, scope); didSomething = true; break; default: @@ -638,67 +691,105 @@ bool QtDocGenerator::writeInjectDocumentation(TextStream &s, } } } + return didSomething; +} +bool QtDocGenerator::writeInjectDocumentation(TextStream &s, + TypeSystem::DocModificationMode mode, + const AbstractMetaClassCPtr &cppClass) const +{ + const bool didSomething = + writeDocModifications(s, DocParser::getDocModifications(cppClass), + mode, classScope(cppClass)); s << '\n'; // FIXME PYSIDE-7: Deprecate the use of doc string on glue code. // This is pre "add-function" and "inject-documentation" tags. const TypeSystem::CodeSnipPosition pos = mode == TypeSystem::DocModificationPrepend ? TypeSystem::CodeSnipPositionBeginning : TypeSystem::CodeSnipPositionEnd; - if (func) - writeDocSnips(s, func->injectedCodeSnips(), pos, TypeSystem::TargetLangCode); - else - writeDocSnips(s, cppClass->typeEntry()->codeSnips(), pos, TypeSystem::TargetLangCode); + writeDocSnips(s, cppClass->typeEntry()->codeSnips(), pos, TypeSystem::TargetLangCode); return didSomething; } -QString QtDocGenerator::functionSignature(const AbstractMetaClass *cppClass, - const AbstractMetaFunctionCPtr &func) +bool QtDocGenerator::writeInjectDocumentation(TextStream &s, + TypeSystem::DocModificationMode mode, + const DocModificationList &modifications, + const AbstractMetaFunctionCPtr &func, + const QString &scope) const { - QString funcName = cppClass->fullName(); - if (!func->isConstructor()) - funcName += u'.' + getFuncName(func); + const bool didSomething = writeDocModifications(s, modifications, mode, scope); + s << '\n'; - return funcName + formatArgs(func); + // FIXME PYSIDE-7: Deprecate the use of doc string on glue code. + // This is pre "add-function" and "inject-documentation" tags. + const TypeSystem::CodeSnipPosition pos = mode == TypeSystem::DocModificationPrepend + ? TypeSystem::CodeSnipPositionBeginning : TypeSystem::CodeSnipPositionEnd; + writeDocSnips(s, func->injectedCodeSnips(), pos, TypeSystem::TargetLangCode); + return didSomething; +} + +static QString inline toRef(const QString &t) +{ + return ":class:`~"_L1 + t + u'`'; } QString QtDocGenerator::translateToPythonType(const AbstractMetaType &type, - const AbstractMetaClass *cppClass, + const AbstractMetaClassCPtr &cppClass, bool createRef) const { static const QStringList nativeTypes = - {boolT(), floatT(), intT(), pyObjectT(), pyStrT()}; + {boolT, floatT, intT, pyObjectT, pyStrT}; - const QString name = type.name(); + QString name = type.name(); if (nativeTypes.contains(name)) return name; - static const QMap<QString, QString> typeMap = { - { cPyObjectT(), pyObjectT() }, - { qStringT(), pyStrT() }, - { u"uchar"_s, pyStrT() }, + if (type.typeUsagePattern() == AbstractMetaType::PrimitivePattern) { + const auto &basicName = basicReferencedTypeEntry(type.typeEntry())->name(); + if (AbstractMetaType::cppSignedIntTypes().contains(basicName) + || AbstractMetaType::cppUnsignedIntTypes().contains(basicName)) { + return intT; + } + if (AbstractMetaType::cppFloatTypes().contains(basicName)) + return floatT; + } + + static const QSet<QString> stringTypes = { + u"uchar"_s, u"std::string"_s, u"std::wstring"_s, + u"std::stringview"_s, u"std::wstringview"_s, + qStringT, u"QStringView"_s, u"QAnyStringView"_s, u"QUtf8StringView"_s + }; + if (stringTypes.contains(name)) + return pyStrT; + + static const QHash<QString, QString> typeMap = { + { cPyObjectT, pyObjectT }, { u"QStringList"_s, u"list of strings"_s }, - { qVariantT(), pyObjectT() }, - { u"quint32"_s, intT() }, - { u"uint32_t"_s, intT() }, - { u"quint64"_s, intT() }, - { u"qint64"_s, intT() }, - { u"size_t"_s, intT() }, - { u"int64_t"_s, intT() }, - { u"qreal"_s, floatT() } + { qVariantT, pyObjectT } }; - const auto found = typeMap.find(name); - if (found != typeMap.end()) + const auto found = typeMap.constFind(name); + if (found != typeMap.cend()) return found.value(); - QString strType; - if (type.isConstant() && name == u"char" && type.indirections() == 1) { - strType = u"str"_s; - } else if (name.startsWith(unsignedShortT())) { - strType = intT(); - } else if (name.startsWith(unsignedT())) { // uint and ulong - strType = intT(); - } else if (type.isContainer()) { + if (type.isFlags()) { + const auto fte = std::static_pointer_cast<const FlagsTypeEntry>(type.typeEntry()); + auto enumTypeEntry = fte->originator(); + auto enumName = enumTypeEntry->targetLangName(); + if (createRef) + enumName.prepend(enumTypeEntry->targetLangPackage() + u'.'); + return "Combination of "_L1 + (createRef ? toRef(enumName) : enumName); + } else if (type.isEnum()) { + auto enumTypeEntry = std::static_pointer_cast<const EnumTypeEntry>(type.typeEntry()); + auto enumName = enumTypeEntry->targetLangName(); + if (createRef) + enumName.prepend(enumTypeEntry->targetLangPackage() + u'.'); + return createRef ? toRef(enumName) : enumName; + } + + if (type.isConstant() && name == "char"_L1 && type.indirections() == 1) + return "str"_L1; + + if (type.isContainer()) { QString strType = translateType(type, cppClass, Options(ExcludeConst) | ExcludeReference); strType.remove(u'*'); strType.remove(u'>'); @@ -714,19 +805,19 @@ QString QtDocGenerator::translateToPythonType(const AbstractMetaType &type, strType = QString::fromLatin1("Dictionary with keys of type %1 and values of type %2.") .arg(types[0], types[1]); } - } else { - auto k = AbstractMetaClass::findClass(api().classes(), type.typeEntry()); - strType = k ? k->fullName() : type.name(); - if (createRef) { - strType.prepend(u":any:`"_s); - strType.append(u'`'); - } + return strType; } - return strType; + + if (auto k = AbstractMetaClass::findClass(api().classes(), type.typeEntry())) + return createRef ? toRef(k->fullName()) : k->name(); + + return createRef ? toRef(name) : name; } QString QtDocGenerator::getFuncName(const AbstractMetaFunctionCPtr &cppFunc) { + if (cppFunc->isConstructor()) + return "__init__"_L1; QString result = cppFunc->name(); if (cppFunc->isOperatorOverload()) { const QString pythonOperator = Generator::pythonOperatorFunctionName(result); @@ -737,14 +828,16 @@ QString QtDocGenerator::getFuncName(const AbstractMetaFunctionCPtr &cppFunc) return result; } -void QtDocGenerator::writeParameterType(TextStream &s, const AbstractMetaClass *cppClass, +void QtDocGenerator::writeParameterType(TextStream &s, + const AbstractMetaClassCPtr &cppClass, const AbstractMetaArgument &arg) const { s << ":param " << arg.name() << ": " << translateToPythonType(arg.type(), cppClass) << '\n'; } -void QtDocGenerator::writeFunctionParametersType(TextStream &s, const AbstractMetaClass *cppClass, +void QtDocGenerator::writeFunctionParametersType(TextStream &s, + const AbstractMetaClassCPtr &cppClass, const AbstractMetaFunctionCPtr &func) const { s << '\n'; @@ -754,36 +847,54 @@ void QtDocGenerator::writeFunctionParametersType(TextStream &s, const AbstractMe writeParameterType(s, cppClass, arg); } - if (!func->isConstructor() && !func->isVoid()) { - - QString retType; + QString retType; + if (!func->isConstructor()) { // check if the return type was modified - for (const auto &mod : func->modifications()) { - for (const ArgumentModification &argMod : mod.argument_mods()) { - if (argMod.index() == 0) { - retType = argMod.modifiedType(); - break; - } - } - } - - if (retType.isEmpty()) + retType = func->modifiedTypeName(); + if (retType.isEmpty() && !func->isVoid()) retType = translateToPythonType(func->type(), cppClass); - s << ":rtype: " << retType << '\n'; } + + if (!retType.isEmpty()) + s << ":rtype: " << retType << '\n'; + s << '\n'; } -void QtDocGenerator::writeFunction(TextStream &s, const AbstractMetaClass *cppClass, - const AbstractMetaFunctionCPtr &func, bool indexed) +static bool containsFunctionDirective(const DocModification &dm) { - s << functionSignature(cppClass, func); + return dm.mode() != TypeSystem::DocModificationXPathReplace + && dm.code().contains(".. py:"_L1); +} - { +void QtDocGenerator::writeFunctions(TextStream &s, const AbstractMetaFunctionCList &funcs, + const AbstractMetaClassCPtr &cppClass, const QString &scope) +{ + QString lastName; + for (const auto &func : funcs) { + const bool indexed = func->name() != lastName; + lastName = func->name(); + writeFunction(s, func, cppClass, scope, indexed); + } +} + +void QtDocGenerator::writeFunction(TextStream &s, const AbstractMetaFunctionCPtr &func, + const AbstractMetaClassCPtr &cppClass, + const QString &scope, bool indexed) +{ + const auto modifications = DocParser::getDocModifications(func, cppClass); + + // Enable injecting parameter documentation by adding a complete function directive. + if (std::none_of(modifications.cbegin(), modifications.cend(), containsFunctionDirective)) { + if (func->ownerClass() == nullptr) + s << ".. py:function:: "; + else + s << (func->isStatic() ? ".. py:staticmethod:: " : ".. py:method:: "); + s << getFuncName(func) << formatArgs(func); Indentation indentation(s); if (!indexed) s << "\n:noindex:"; - if (func->attributes().testFlag(AbstractMetaFunction::Attribute::FinalCppMethod)) + if (func->cppAttributes().testFlag(FunctionAttribute::Final)) s << "\n:final:"; else if (func->isAbstract()) s << "\n:abstractmethod:"; @@ -795,16 +906,12 @@ void QtDocGenerator::writeFunction(TextStream &s, const AbstractMetaClass *cppCl if (func->isDeprecated()) s << rstDeprecationNote("function"); } - writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, cppClass, func); - if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, cppClass, func)) { - writeFormattedBriefText(s, func->documentation(), cppClass); - writeFormattedDetailedText(s, func->documentation(), cppClass); - } - writeInjectDocumentation(s, TypeSystem::DocModificationAppend, cppClass, func); + + writeFunctionDocumentation(s, func, modifications, scope); if (auto propIndex = func->propertySpecIndex(); propIndex >= 0) { const QString name = cppClass->propertySpecs().at(propIndex).name(); - const QString target = propertyRefTarget(cppClass, name); + const QString target = propertyRefTarget(name); if (func->isPropertyReader()) s << "\nGetter of property " << propRef(target) << " .\n\n"; else if (func->isPropertyWriter()) @@ -816,23 +923,79 @@ void QtDocGenerator::writeFunction(TextStream &s, const AbstractMetaClass *cppCl } } -static void writeFancyToc(TextStream& s, const QStringList& items) +void QtDocGenerator::writeFunctionDocumentation(TextStream &s, const AbstractMetaFunctionCPtr &func, + const DocModificationList &modifications, + const QString &scope) const + +{ + writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, modifications, func, scope); + if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, modifications, func, scope)) { + writeFormattedBriefText(s, func->documentation(), scope); + writeFormattedDetailedText(s, func->documentation(), scope); + } + writeInjectDocumentation(s, TypeSystem::DocModificationAppend, modifications, func, scope); +} + +static QStringList fileListToToc(const QStringList &items) +{ + QStringList result; + result.reserve(items.size()); + std::transform(items.cbegin(), items.cend(), std::back_inserter(result), + fileNameToTocEntry); + return result; +} + +static QStringList functionListToToc(const AbstractMetaFunctionCList &functions) +{ + QStringList result; + result.reserve(functions.size()); + for (const auto &f : functions) + result.append(f->name()); + // Functions are sorted by the Metabuilder; erase overloads + result.erase(std::unique(result.begin(), result.end()), result.end()); + return result; +} + +static QStringList enumListToToc(const AbstractMetaEnumList &enums) +{ + QStringList result; + result.reserve(enums.size()); + for (const auto &e : enums) + result.append(e.name()); + return result; +} + +// Sort entries for a TOC by first character, dropping the +// leading common Qt prefixes like 'Q'. +static QChar sortKey(const QString &key) +{ + const auto size = key.size(); + if (size >= 2 && (key.at(0) == u'Q' || key.at(0) == u'q') + && (key.at(1).isUpper() || key.at(1).isDigit())) { + return key.at(1); // "QClass" -> 'C', "qSin()" -> 'S', 'Q3DSurfaceWidget' -> '3' + } + if (size >= 3 && key.startsWith("Q_"_L1)) + return key.at(2).toUpper(); // "Q_ARG" -> 'A' + if (size >= 4 && key.startsWith("QT_"_L1)) + return key.at(3).toUpper(); // "QT_TR" -> 'T' + auto idx = 0; + for (; idx < size && key.at(idx) == u'_'; ++idx) { + } // "__init__" -> 'I' + return idx < size ? key.at(idx).toUpper() : u'A'; +} + +static void writeFancyToc(TextStream& s, QAnyStringView title, + const QStringList& items, + QLatin1StringView referenceType) { using TocMap = QMap<QChar, QStringList>; + + if (items.isEmpty()) + return; + TocMap tocMap; - QChar idx; - for (QString item : items) { - if (item.isEmpty()) - continue; - item.chop(4); // Remove the .rst extension - // skip namespace if necessary - const QString className = item.split(u'.').last(); - if (className.startsWith(u'Q') && className.length() > 1) - idx = className[1]; - else - idx = className[0]; - tocMap[idx] << item; - } + for (const QString &item : items) + tocMap[sortKey(item)] << item; static const qsizetype numColumns = 4; @@ -847,7 +1010,7 @@ static void writeFancyToc(TextStream& s, const QStringList& items) row.clear(); row << QtXmlToSphinx::TableCell(QString{}); } - const QString entry = u"* :doc:`"_s + item + u'`'; + const QString entry = "* :"_L1 + referenceType + ":`"_L1 + item + u'`'; row << QtXmlToSphinx::TableCell(entry); } if (row.size() > 1) @@ -855,33 +1018,44 @@ static void writeFancyToc(TextStream& s, const QStringList& items) } table.normalize(); - s << ".. container:: pysidetoc\n\n"; + s << '\n' << headline(title) << ".. container:: pysidetoc\n\n"; table.format(s); } bool QtDocGenerator::finishGeneration() { - if (!api().classes().isEmpty()) + for (const auto &f : api().globalFunctions()) { + auto ncf = std::const_pointer_cast<AbstractMetaFunction>(f); + m_docParser->fillGlobalFunctionDocumentation(ncf); + m_packages[f->targetLangPackage()].globalFunctions.append(f); + } + + for (auto e : api().globalEnums()) { + m_docParser->fillGlobalEnumDocumentation(e); + m_packages[e.typeEntry()->targetLangPackage()].globalEnums.append(e); + } + + if (!m_packages.isEmpty()) writeModuleDocumentation(); - if (!m_additionalDocumentationList.isEmpty()) + if (!m_options.additionalDocumentationList.isEmpty()) writeAdditionalDocumentation(); - if (!m_inheritanceFile.isEmpty() && !writeInheritanceFile()) + if (!m_options.inheritanceFile.isEmpty() && !writeInheritanceFile()) return false; return true; } bool QtDocGenerator::writeInheritanceFile() { - QFile inheritanceFile(m_inheritanceFile); + QFile inheritanceFile(m_options.inheritanceFile); if (!inheritanceFile.open(QIODevice::WriteOnly | QIODevice::Text)) - throw Exception(msgCannotOpenForWriting(m_inheritanceFile)); + throw Exception(msgCannotOpenForWriting(m_options.inheritanceFile)); QJsonObject dict; - for (auto *c : api().classes()) { + for (const auto &c : api().classes()) { const auto &bases = c->baseClasses(); if (!bases.isEmpty()) { QJsonArray list; - for (auto *base : bases) + for (const auto &base : bases) list.append(QJsonValue(base->fullName())); dict[c->fullName()] = list; } @@ -892,11 +1066,22 @@ bool QtDocGenerator::writeInheritanceFile() return true; } +// Remove function entries that have extra documentation pages +static inline void removeExtraDocs(const QStringList &extraTocEntries, + AbstractMetaFunctionCList *functions) +{ + auto predicate = [&extraTocEntries](const AbstractMetaFunctionCPtr &f) { + return extraTocEntries.contains(f->name()); + }; + functions->erase(std::remove_if(functions->begin(),functions->end(), predicate), + functions->end()); +} + void QtDocGenerator::writeModuleDocumentation() { - QMap<QString, QStringList>::iterator it = m_packages.begin(); - for (; it != m_packages.end(); ++it) { - std::sort(it.value().begin(), it.value().end()); + for (auto it = m_packages.begin(), end = m_packages.end(); it != end; ++it) { + auto &docPackage = it.value(); + std::sort(docPackage.classPages.begin(), docPackage.classPages.end()); QString key = it.key(); key.replace(u'.', u'/'); @@ -905,9 +1090,7 @@ void QtDocGenerator::writeModuleDocumentation() TextStream& s = output.stream; const QString &title = it.key(); - s << ".. module:: " << title << "\n\n" - << title << '\n' - << Pad('*', title.length()) << "\n\n"; + s << ".. module:: " << title << "\n\n" << headline(title, '*'); // Store the it.key() in a QString so that it can be stripped off unwanted // information when neeeded. For example, the RST files in the extras directory @@ -918,11 +1101,12 @@ void QtDocGenerator::writeModuleDocumentation() moduleName.remove(0, lastIndex + 1); // Search for extra-sections - if (!m_extraSectionDir.isEmpty()) { - QDir extraSectionDir(m_extraSectionDir); + QStringList extraTocEntries; + if (!m_options.extraSectionDir.isEmpty()) { + QDir extraSectionDir(m_options.extraSectionDir); if (!extraSectionDir.exists()) { - const QString m = QStringLiteral("Extra sections directory ") + - m_extraSectionDir + QStringLiteral(" doesn't exist"); + const QString m = u"Extra sections directory "_s + + m_options.extraSectionDir + u" doesn't exist"_s; throw Exception(m); } @@ -930,31 +1114,26 @@ void QtDocGenerator::writeModuleDocumentation() const QString filter = moduleName + u".?*.rst"_s; const auto fileList = extraSectionDir.entryInfoList({filter}, QDir::Files, QDir::Name); - for (const auto &fi : fileList) { - // Strip to "Property.rst" in output directory - const QString newFileName = fi.fileName().mid(moduleName.size() + 1); - it.value().append(newFileName); - const QString newFilePath = outputDir + u'/' + newFileName; - if (QFile::exists(newFilePath)) - QFile::remove(newFilePath); - if (!QFile::copy(fi.absoluteFilePath(), newFilePath)) { - qCDebug(lcShibokenDoc).noquote().nospace() << "Error copying extra doc " - << QDir::toNativeSeparators(fi.absoluteFilePath()) - << " to " << QDir::toNativeSeparators(newFilePath); - } - } + for (const auto &fi : fileList) + readExtraDoc(fi, moduleName, outputDir, &docPackage, &extraTocEntries); } + removeExtraDocs(extraTocEntries, &docPackage.globalFunctions); + const bool hasGlobals = !docPackage.globalFunctions.isEmpty() + || !docPackage.globalEnums.isEmpty(); + const QString globalsPage = moduleName + "_globals.rst"_L1; + s << ".. container:: hide\n\n" << indent << ".. toctree::\n" << indent << ":maxdepth: 1\n\n"; - for (const QString &className : std::as_const(it.value())) + if (hasGlobals) + s << globalsPage << '\n'; + for (const QString &className : std::as_const(docPackage.classPages)) s << className << '\n'; - s << "\n\n" << outdent << outdent - << "Detailed Description\n--------------------\n\n"; + s << "\n\n" << outdent << outdent << headline("Detailed Description"); // module doc is always wrong and C++istic, so go straight to the extra directory! - QFile moduleDoc(m_extraSectionDir + u'/' + moduleName + QFile moduleDoc(m_options.extraSectionDir + u'/' + moduleName + u".rst"_s); if (moduleDoc.open(QIODevice::ReadOnly | QIODevice::Text)) { s << moduleDoc.readAll(); @@ -965,19 +1144,48 @@ void QtDocGenerator::writeModuleDocumentation() if (moduleDoc.format() == Documentation::Native) { QString context = it.key(); QtXmlToSphinx::stripPythonQualifiers(&context); - QtXmlToSphinx x(this, m_parameters, moduleDoc.detailed(), context); + QtXmlToSphinx x(this, m_options.parameters, moduleDoc.detailed(), context); s << x; } else { s << moduleDoc.detailed(); } } - s << "\nList of Classes\n" - << "---------------\n\n"; - writeFancyToc(s, it.value()); + writeFancyToc(s, "List of Classes", fileListToToc(docPackage.classPages), + "class"_L1); + writeFancyToc(s, "List of Decorators", fileListToToc(docPackage.decoratorPages), + "deco"_L1); + writeFancyToc(s, "List of Functions", functionListToToc(docPackage.globalFunctions), + "py:func"_L1); + writeFancyToc(s, "List of Enumerations", enumListToToc(docPackage.globalEnums), + "any"_L1); output.done(); + + if (hasGlobals) + writeGlobals(it.key(), outputDir + u'/' + globalsPage, docPackage); + } +} + +void QtDocGenerator::writeGlobals(const QString &package, + const QString &fileName, + const DocPackage &docPackage) +{ + FileOut output(fileName); + TextStream &s = output.stream; + + // Write out functions with injected documentation + if (!docPackage.globalFunctions.isEmpty()) { + s << currentModule(package) << headline("Functions"); + writeFunctions(s, docPackage.globalFunctions, {}, {}); } + + if (!docPackage.globalEnums.isEmpty()) { + s << headline("Enumerations"); + writeEnums(s, docPackage.globalEnums, package); + } + + output.done(); } static inline QString msgNonExistentAdditionalDocFile(const QString &dir, @@ -992,7 +1200,7 @@ static inline QString msgNonExistentAdditionalDocFile(const QString &dir, void QtDocGenerator::writeAdditionalDocumentation() const { - QFile additionalDocumentationFile(m_additionalDocumentationList); + QFile additionalDocumentationFile(m_options.additionalDocumentationList); if (!additionalDocumentationFile.open(QIODevice::ReadOnly | QIODevice::Text)) throw Exception(msgCannotOpenForReading(additionalDocumentationFile)); @@ -1017,8 +1225,8 @@ void QtDocGenerator::writeAdditionalDocumentation() const targetDir = outDir.absolutePath(); } else { if (!outDir.exists(dir) && !outDir.mkdir(dir)) { - const QString m = QStringLiteral("Cannot create directory ") - + dir + QStringLiteral(" under ") + const QString m = "Cannot create directory "_L1 + + dir + " under "_L1 + QDir::toNativeSeparators(outputDirectory()); throw Exception(m); } @@ -1026,7 +1234,7 @@ void QtDocGenerator::writeAdditionalDocumentation() const } } else { // Normal file entry - QFileInfo fi(m_parameters.docDataDir + u'/' + line); + QFileInfo fi(m_options.parameters.docDataDir + u'/' + line); if (fi.isFile()) { const QString rstFileName = fi.baseName() + rstSuffix; const QString rstFile = targetDir + u'/' + rstFileName; @@ -1044,7 +1252,7 @@ void QtDocGenerator::writeAdditionalDocumentation() const // FIXME: This should be an exception, in principle, but it // requires building all modules. qCWarning(lcShibokenDoc, "%s", - qPrintable(msgNonExistentAdditionalDocFile(m_parameters.docDataDir, line))); + qPrintable(msgNonExistentAdditionalDocFile(m_options.parameters.docDataDir, line))); } ++count; } @@ -1063,32 +1271,34 @@ void QtDocGenerator::writeAdditionalDocumentation() const bool QtDocGenerator::doSetup() { - if (m_parameters.codeSnippetDirs.isEmpty()) { - m_parameters.codeSnippetDirs = - m_parameters.libSourceDir.split(QLatin1Char(PATH_SEP)); + if (m_options.parameters.codeSnippetDirs.isEmpty()) { + m_options.parameters.codeSnippetDirs = + m_options.parameters.libSourceDir.split(QLatin1Char(PATH_SEP)); } - if (m_docParser.isNull()) - m_docParser.reset(new QtDocParser); + if (m_docParser.isNull()) { + if (m_options.doxygen) + m_docParser.reset(new DoxygenParser); + else + m_docParser.reset(new QtDocParser); + } - if (m_parameters.libSourceDir.isEmpty() - || m_parameters.docDataDir.isEmpty()) { + if (m_options.parameters.libSourceDir.isEmpty() + || m_options.parameters.docDataDir.isEmpty()) { qCWarning(lcShibokenDoc) << "Documentation data dir and/or Qt source dir not informed, " "documentation will not be extracted from Qt sources."; return false; } - m_docParser->setDocumentationDataDirectory(m_parameters.docDataDir); - m_docParser->setLibrarySourceDirectory(m_parameters.libSourceDir); - m_parameters.outputDirectory = outputDirectory(); + m_docParser->setDocumentationDataDirectory(m_options.parameters.docDataDir); + m_docParser->setLibrarySourceDirectory(m_options.parameters.libSourceDir); + m_options.parameters.outputDirectory = outputDirectory(); return true; } - -Generator::OptionDescriptions QtDocGenerator::options() const +QList<OptionDescription> QtDocGenerator::options() { - auto result = Generator::options(); - result.append({ + return { {u"doc-parser=<parser>"_s, u"The documentation parser used to interpret the documentation\n" "input files (qdoc|doxygen)"_s}, @@ -1102,30 +1312,52 @@ Generator::OptionDescriptions QtDocGenerator::options() const u"Directory used to search for extra documentation sections"_s}, {u"library-source-dir=<dir>"_s, u"Directory where library source code is located"_s}, - {additionalDocumentationOption() + u"=<file>"_s, + {additionalDocumentationOption + u"=<file>"_s, u"List of additional XML files to be converted to .rst files\n" "(for example, tutorials)."_s}, {u"inheritance-file=<file>"_s, - u"Generate a JSON file containing the class inheritance."_s} - - }); - return result; + u"Generate a JSON file containing the class inheritance."_s}, + {u"disable-inheritance-diagram"_s, + u"Disable the generation of the inheritance diagram."_s} + }; } -bool QtDocGenerator::handleOption(const QString &key, const QString &value) +class QtDocGeneratorOptionsParser : public OptionsParser { - if (Generator::handleOption(key, value)) +public: + explicit QtDocGeneratorOptionsParser(DocGeneratorOptions *o) : m_options(o) {} + + bool handleBoolOption(const QString &key, OptionSource source) override; + bool handleOption(const QString &key, const QString &value, OptionSource source) override; + +private: + DocGeneratorOptions *m_options; +}; + +bool QtDocGeneratorOptionsParser::handleBoolOption(const QString &key, OptionSource) +{ + if (key == "disable-inheritance-diagram"_L1) { + m_options->inheritanceDiagram = false; return true; + } + return false; +} + +bool QtDocGeneratorOptionsParser::handleOption(const QString &key, const QString &value, + OptionSource source) +{ + if (source == OptionSource::CommandLineSingleDash) + return false; if (key == u"library-source-dir") { - m_parameters.libSourceDir = value; + m_options->parameters.libSourceDir = value; return true; } if (key == u"documentation-data-dir") { - m_parameters.docDataDir = value; + m_options->parameters.docDataDir = value; return true; } if (key == u"documentation-code-snippets-dir") { - m_parameters.codeSnippetDirs = value.split(QLatin1Char(PATH_SEP)); + m_options->parameters.codeSnippetDirs = value.split(QLatin1Char(PATH_SEP)); return true; } @@ -1133,34 +1365,39 @@ bool QtDocGenerator::handleOption(const QString &key, const QString &value) const auto pos = value.indexOf(u':'); if (pos == -1) return false; - m_parameters.codeSnippetRewriteOld= value.left(pos); - m_parameters.codeSnippetRewriteNew = value.mid(pos + 1); + m_options->parameters.codeSnippetRewriteOld= value.left(pos); + m_options->parameters.codeSnippetRewriteNew = value.mid(pos + 1); return true; } if (key == u"documentation-extra-sections-dir") { - m_extraSectionDir = value; + m_options->extraSectionDir = value; return true; } if (key == u"doc-parser") { qCDebug(lcShibokenDoc).noquote().nospace() << "doc-parser: " << value; if (value == u"doxygen") - m_docParser.reset(new DoxygenParser); + m_options->doxygen = true; return true; } - if (key == additionalDocumentationOption()) { - m_additionalDocumentationList = value; + if (key == additionalDocumentationOption) { + m_options->additionalDocumentationList = value; return true; } if (key == u"inheritance-file") { - m_inheritanceFile = value; + m_options->inheritanceFile = value; return true; } return false; } +std::shared_ptr<OptionsParser> QtDocGenerator::createOptionsParser() +{ + return std::make_shared<QtDocGeneratorOptionsParser>(&m_options); +} + bool QtDocGenerator::convertToRst(const QString &sourceFileName, const QString &targetFileName, const QString &context, @@ -1176,28 +1413,22 @@ bool QtDocGenerator::convertToRst(const QString &sourceFileName, sourceFile.close(); FileOut targetFile(targetFileName); - QtXmlToSphinx x(this, m_parameters, doc, context); + QtXmlToSphinx x(this, m_options.parameters, doc, context); targetFile.stream << x; targetFile.done(); return true; } GeneratorDocumentation - QtDocGenerator::generatorDocumentation(const AbstractMetaClass *cppClass) const + QtDocGenerator::generatorDocumentation(const AbstractMetaClassCPtr &cppClass) { GeneratorDocumentation result; const auto allFunctions = cppClass->functions(); result.allFunctions.reserve(allFunctions.size()); - for (const auto &func : allFunctions) { - if (!shouldSkip(func)) { - if (func->isConstructor()) - result.constructors.append(func); - else - result.allFunctions.append(func); - } - } + std::remove_copy_if(allFunctions.cbegin(), allFunctions.cend(), + std::back_inserter(result.allFunctions), shouldSkip); - std::sort(result.allFunctions.begin(), result.allFunctions.end(), functionSort); + std::stable_sort(result.allFunctions.begin(), result.allFunctions.end(), functionSort); for (const auto &func : std::as_const(result.allFunctions)) { if (func->isStatic()) @@ -1236,11 +1467,11 @@ GeneratorDocumentation // QtXmlToSphinxDocGeneratorInterface QString QtDocGenerator::expandFunction(const QString &function) const { - const int firstDot = function.indexOf(u'.'); - const AbstractMetaClass *metaClass = nullptr; + const auto firstDot = function.indexOf(u'.'); + AbstractMetaClassCPtr metaClass; if (firstDot != -1) { const auto className = QStringView{function}.left(firstDot); - for (auto cls : api().classes()) { + for (const auto &cls : api().classes()) { if (cls->name() == className) { metaClass = cls; break; @@ -1275,8 +1506,8 @@ QString QtDocGenerator::resolveContextForMethod(const QString &context, { const auto currentClass = QStringView{context}.split(u'.').constLast(); - const AbstractMetaClass *metaClass = nullptr; - for (auto cls : api().classes()) { + AbstractMetaClassCPtr metaClass; + for (const auto &cls : api().classes()) { if (cls->name() == currentClass) { metaClass = cls; break; @@ -1291,7 +1522,7 @@ QString QtDocGenerator::resolveContextForMethod(const QString &context, funcList.append(func); } - const AbstractMetaClass *implementingClass = nullptr; + AbstractMetaClassCPtr implementingClass; for (const auto &func : std::as_const(funcList)) { implementingClass = func->implementingClass(); if (implementingClass->name() == currentClass) @@ -1322,7 +1553,7 @@ QtXmlToSphinxLink QtDocGenerator::resolveLink(const QtXmlToSphinxLink &link) con { if (link.type != QtXmlToSphinxLink::Reference || !isRelativeHtmlFile(link.linkRef)) return link; - static const QString prefix = QStringLiteral("https://doc.qt.io/qt-") + static const QString prefix = "https://doc.qt.io/qt-"_L1 + QString::number(QT_VERSION_MAJOR) + u'/'; QtXmlToSphinxLink resolved = link; resolved.type = QtXmlToSphinxLink::External; @@ -1333,6 +1564,28 @@ QtXmlToSphinxLink QtDocGenerator::resolveLink(const QtXmlToSphinxLink &link) con if (anchor != -1) resolved.linkText.truncate(anchor); } - qDebug() << __FUNCTION__ << link << "->" << resolved; return resolved; } + +QtXmlToSphinxDocGeneratorInterface::Image + QtDocGenerator::resolveImage(const QString &href, const QString &context) const +{ + QString relativeSourceDir = href; + const QString source = m_options.parameters.docDataDir + u'/' + relativeSourceDir; + if (!QFileInfo::exists(source)) + throw Exception(msgCannotFindImage(href, context,source)); + + // Determine target directory from context, "Pyside2.QtGui.QPainter" ->"Pyside2/QtGui". + // FIXME: Not perfect yet, should have knowledge about namespaces (DataVis3D) or + // nested classes "Pyside2.QtGui.QTouchEvent.QTouchPoint". + QString relativeTargetDir = context; + const auto lastDot = relativeTargetDir.lastIndexOf(u'.'); + if (lastDot != -1) + relativeTargetDir.truncate(lastDot); + relativeTargetDir.replace(u'.', u'/'); + if (!relativeTargetDir.isEmpty()) + relativeTargetDir += u'/'; + relativeTargetDir += href; + + return {relativeSourceDir, relativeTargetDir}; +} diff --git a/sources/shiboken6/generator/qtdoc/qtdocgenerator.h b/sources/shiboken6/generator/qtdoc/qtdocgenerator.h index af4b60d2e..56e15e2a1 100644 --- a/sources/shiboken6/generator/qtdoc/qtdocgenerator.h +++ b/sources/shiboken6/generator/qtdoc/qtdocgenerator.h @@ -9,13 +9,15 @@ #include "generator.h" #include "documentation.h" +#include <optionsparser.h> #include "typesystem_enums.h" #include "modifications_typedefs.h" #include "qtxmltosphinxinterface.h" class DocParser; - +struct DocGeneratorOptions; struct GeneratorDocumentation; +struct DocPackage; /** * The DocGenerator generates documentation from library being binded. @@ -23,6 +25,8 @@ struct GeneratorDocumentation; class QtDocGenerator : public Generator, public QtXmlToSphinxDocGeneratorInterface { public: + Q_DISABLE_COPY_MOVE(QtDocGenerator) + QtDocGenerator(); ~QtDocGenerator(); @@ -33,8 +37,8 @@ public: return "QtDocGenerator"; } - OptionDescriptions options() const override; - bool handleOption(const QString &key, const QString &value) override; + static QList<OptionDescription> options(); + static std::shared_ptr<OptionsParser> createOptionsParser(); // QtXmlToSphinxDocGeneratorInterface QString expandFunction(const QString &function) const override; @@ -44,6 +48,7 @@ public: const QString &methodName) const override; const QLoggingCategory &loggingCategory() const override; QtXmlToSphinxLink resolveLink(const QtXmlToSphinxLink &) const override; + Image resolveImage(const QString &href, const QString &context) const override; static QString getFuncName(const AbstractMetaFunctionCPtr &cppFunc); static QString formatArgs(const AbstractMetaFunctionCPtr &func); @@ -56,50 +61,57 @@ protected: bool finishGeneration() override; private: - void writeEnums(TextStream &s, const AbstractMetaClass *cppClass) const; - - void writeFields(TextStream &s, const AbstractMetaClass *cppClass) const; - static QString functionSignature(const AbstractMetaClass *cppClass, - const AbstractMetaFunctionCPtr &func); - void writeFunction(TextStream &s, const AbstractMetaClass *cppClass, - const AbstractMetaFunctionCPtr &func, bool indexed = true); - void writeFunctionParametersType(TextStream &s, const AbstractMetaClass *cppClass, + void writeEnums(TextStream &s, const AbstractMetaEnumList &enums, + const QString &scope) const; + + void writeFields(TextStream &s, const AbstractMetaClassCPtr &cppClass) const; + void writeFunctions(TextStream &s, const AbstractMetaFunctionCList &funcs, + const AbstractMetaClassCPtr &cppClass, const QString &scope); + void writeFunction(TextStream &s, const AbstractMetaFunctionCPtr &func, + const AbstractMetaClassCPtr &cppClass = {}, + const QString &scope = {}, bool indexed = true); + void writeFunctionDocumentation(TextStream &s, const AbstractMetaFunctionCPtr &func, + const DocModificationList &modifications, + const QString &scope) const; + void writeFunctionParametersType(TextStream &s, const AbstractMetaClassCPtr &cppClass, const AbstractMetaFunctionCPtr &func) const; static void writeFunctionToc(TextStream &s, const QString &title, - const AbstractMetaClass *cppClass, const AbstractMetaFunctionCList &functions); - void writePropertyToc(TextStream &s, - const GeneratorDocumentation &doc, - const AbstractMetaClass *cppClass); + static void writePropertyToc(TextStream &s, + const GeneratorDocumentation &doc); void writeProperties(TextStream &s, const GeneratorDocumentation &doc, - const AbstractMetaClass *cppClass) const; - void writeParameterType(TextStream &s, const AbstractMetaClass *cppClass, + const AbstractMetaClassCPtr &cppClass) const; + void writeParameterType(TextStream &s, const AbstractMetaClassCPtr &cppClass, const AbstractMetaArgument &arg) const; - - void writeConstructors(TextStream &s, - const AbstractMetaClass *cppClass, - const AbstractMetaFunctionCList &constructors) const; - void writeFormattedText(TextStream &s, const QString &doc, Documentation::Format format, - const AbstractMetaClass *metaClass = nullptr) const; + const QString &scope = {}) const; void writeFormattedBriefText(TextStream &s, const Documentation &doc, - const AbstractMetaClass *metaclass = nullptr) const; + const QString &scope = {}) const; void writeFormattedDetailedText(TextStream &s, const Documentation &doc, - const AbstractMetaClass *metaclass = nullptr) const; + const QString &scope = {}) const; bool writeInjectDocumentation(TextStream &s, TypeSystem::DocModificationMode mode, - const AbstractMetaClass *cppClass, - const AbstractMetaFunctionCPtr &func); + const AbstractMetaClassCPtr &cppClass) const; + bool writeInjectDocumentation(TextStream &s, TypeSystem::DocModificationMode mode, + const DocModificationList &modifications, + const AbstractMetaFunctionCPtr &func, + const QString &scope = {}) const; + bool writeDocModifications(TextStream &s, const DocModificationList &mods, + TypeSystem::DocModificationMode mode, + const QString &scope = {}) const; static void writeDocSnips(TextStream &s, const CodeSnipList &codeSnips, TypeSystem::CodeSnipPosition position, TypeSystem::Language language); void writeModuleDocumentation(); + void writeGlobals(const QString &package, const QString &fileName, + const DocPackage &docPackage); void writeAdditionalDocumentation() const; bool writeInheritanceFile(); - QString translateToPythonType(const AbstractMetaType &type, const AbstractMetaClass *cppClass, + QString translateToPythonType(const AbstractMetaType &type, + const AbstractMetaClassCPtr &cppClass, bool createRef = true) const; bool convertToRst(const QString &sourceFileName, @@ -107,15 +119,12 @@ private: const QString &context = QString(), QString *errorMessage = nullptr) const; - GeneratorDocumentation generatorDocumentation(const AbstractMetaClass *cppClass) const; + static GeneratorDocumentation generatorDocumentation(const AbstractMetaClassCPtr &cppClass); - QString m_extraSectionDir; QStringList m_functionList; - QMap<QString, QStringList> m_packages; + QMap<QString, DocPackage> m_packages; QScopedPointer<DocParser> m_docParser; - QtXmlToSphinxParameters m_parameters; - QString m_additionalDocumentationList; - QString m_inheritanceFile; + static DocGeneratorOptions m_options; }; #endif // DOCGENERATOR_H diff --git a/sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp b/sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp index 222320d38..b8fec836c 100644 --- a/sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp +++ b/sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp @@ -19,10 +19,6 @@ using namespace Qt::StringLiterals; -static inline QString nameAttribute() { return QStringLiteral("name"); } -static inline QString titleAttribute() { return QStringLiteral("title"); } -static inline QString fullTitleAttribute() { return QStringLiteral("fulltitle"); } - QString msgTagWarning(const QXmlStreamReader &reader, const QString &context, const QString &tag, const QString &message) { @@ -63,6 +59,20 @@ static bool isHttpLink(const QString &ref) return ref.startsWith(u"http://") || ref.startsWith(u"https://"); } +static QString trimRight(QString s) +{ + while (!s.isEmpty() && s.crbegin()->isSpace()) + s.chop(1); + return s; +} + +static QString trimLeadingNewlines(QString s) +{ + while (!s.isEmpty() && s.at(0) == u'\n') + s.remove(0, 1); + return s; +} + QDebug operator<<(QDebug d, const QtXmlToSphinxLink &l) { static const QHash<QtXmlToSphinxLink::Type, const char *> typeName = { @@ -407,32 +417,49 @@ void QtXmlToSphinx::callHandler(WebXmlTag t, QXmlStreamReader &r) void QtXmlToSphinx::formatCurrentTable() { - if (m_currentTable.isEmpty()) + Q_ASSERT(!m_tables.isEmpty()); + auto &table = m_tables.back(); + if (table.isEmpty()) return; - m_currentTable.normalize(); + table.normalize(); m_output << '\n'; - m_currentTable.format(m_output); + table.format(m_output); } void QtXmlToSphinx::pushOutputBuffer() { - m_buffers.append(StringSharedPtr(new QString{})); - m_output.setString(m_buffers.top().data()); + m_buffers.append(std::make_shared<QString>()); + m_output.setString(m_buffers.top().get()); } QString QtXmlToSphinx::popOutputBuffer() { Q_ASSERT(!m_buffers.isEmpty()); - QString result(*m_buffers.top().data()); + QString result(*m_buffers.top()); m_buffers.pop(); - m_output.setString(m_buffers.isEmpty() ? nullptr : m_buffers.top().data()); + m_output.setString(m_buffers.isEmpty() ? nullptr : m_buffers.top().get()); return result; } +constexpr auto autoTranslatedPlaceholder = "AUTO_GENERATED\n"_L1; +constexpr auto autoTranslatedNote = +R"(.. warning:: + This section contains snippets that were automatically + translated from C++ to Python and may contain errors. + +)"_L1; + +void QtXmlToSphinx::setAutoTranslatedNote(QString *str) const +{ + if (m_containsAutoTranslations) + str->replace(autoTranslatedPlaceholder, autoTranslatedNote); + else + str->remove(autoTranslatedPlaceholder); +} + QString QtXmlToSphinx::transform(const QString& doc) { Q_ASSERT(m_buffers.isEmpty()); - Indentation indentation(m_output); if (doc.trimmed().isEmpty()) return doc; @@ -440,6 +467,9 @@ QString QtXmlToSphinx::transform(const QString& doc) QXmlStreamReader reader(doc); + m_output << autoTranslatedPlaceholder; + Indentation indentation(m_output); + while (!reader.atEnd()) { QXmlStreamReader::TokenType token = reader.readNext(); if (reader.hasError()) { @@ -480,6 +510,7 @@ QString QtXmlToSphinx::transform(const QString& doc) m_output.flush(); QString retval = popOutputBuffer(); Q_ASSERT(m_buffers.isEmpty()); + setAutoTranslatedNote(&retval); return retval; } @@ -528,7 +559,7 @@ static QString pySnippetName(const QString &path, SnippetType type) QtXmlToSphinx::Snippet QtXmlToSphinx::readSnippetFromLocations(const QString &path, const QString &identifier, const QString &fallbackPath, - QString *errorMessage) const + QString *errorMessage) { // For anything else but C++ header/sources (no conversion to Python), // use existing fallback paths first. @@ -550,6 +581,7 @@ QtXmlToSphinx::Snippet QtXmlToSphinx::readSnippetFromLocations(const QString &pa rewrittenPath.replace(m_parameters.codeSnippetRewriteOld, m_parameters.codeSnippetRewriteNew); const QString code = readFromLocation(rewrittenPath, identifier, errorMessage); + m_containsAutoTranslations = true; return {code, code.isNull() ? Snippet::Error : Snippet::Converted}; } } @@ -561,7 +593,7 @@ QtXmlToSphinx::Snippet QtXmlToSphinx::readSnippetFromLocations(const QString &pa } } - resolvedPath =resolveFile(locations, path); + resolvedPath = resolveFile(locations, path); if (!resolvedPath.isEmpty()) { const QString code = readFromLocation(resolvedPath, identifier, errorMessage); return {code, code.isNull() ? Snippet::Error : Snippet::Resolved}; @@ -577,6 +609,88 @@ QtXmlToSphinx::Snippet QtXmlToSphinx::readSnippetFromLocations(const QString &pa return {{}, Snippet::Error}; } +// Helpers for extracting qdoc snippets "#/// [id]" +static QString fileNameOfDevice(const QIODevice *inputFile) +{ + const auto *file = qobject_cast<const QFile *>(inputFile); + return file ? QDir::toNativeSeparators(file->fileName()) : u"<stdin>"_s; +} + +static QString msgSnippetNotFound(const QIODevice &inputFile, + const QString &identifier) +{ + return u"Code snippet file found ("_s + fileNameOfDevice(&inputFile) + + u"), but snippet ["_s + identifier + u"] not found."_s; +} + +static QString msgEmptySnippet(const QIODevice &inputFile, int lineNo, + const QString &identifier) +{ + return u"Empty code snippet ["_s + identifier + u"] at "_s + + fileNameOfDevice(&inputFile) + u':' + QString::number(lineNo); +} + +// Pattern to match qdoc snippet IDs with "#/// [id]" comments and helper to find ID +static const QRegularExpression &snippetIdPattern() +{ + static const QRegularExpression result(uR"RX((//|#) *! *\[([^]]+)\])RX"_s); + Q_ASSERT(result.isValid()); + return result; +} + +static bool matchesSnippetId(QRegularExpressionMatchIterator it, + const QString &identifier) +{ + while (it.hasNext()) { + if (it.next().captured(2) == identifier) + return true; + } + return false; +} + +QString QtXmlToSphinx::readSnippet(QIODevice &inputFile, const QString &identifier, + QString *errorMessage) +{ + const QByteArray identifierBA = identifier.toUtf8(); + // Lambda that matches the snippet id + const auto snippetIdPred = [&identifierBA, &identifier](const QByteArray &lineBA) + { + const bool isComment = lineBA.contains('/') || lineBA.contains('#'); + if (!isComment || !lineBA.contains(identifierBA)) + return false; + const QString line = QString::fromUtf8(lineBA); + return matchesSnippetId(snippetIdPattern().globalMatch(line), identifier); + }; + + // Find beginning, skip over + int lineNo = 1; + for (; !inputFile.atEnd() && !snippetIdPred(inputFile.readLine()); + ++lineNo) { + } + + if (inputFile.atEnd()) { + *errorMessage = msgSnippetNotFound(inputFile, identifier); + return {}; + } + + QString code; + for (; !inputFile.atEnd(); ++lineNo) { + const QString line = QString::fromUtf8(inputFile.readLine()); + auto it = snippetIdPattern().globalMatch(line); + if (it.hasNext()) { // Skip snippet id lines + if (matchesSnippetId(it, identifier)) + break; + } else { + code += line; + } + } + + if (code.isEmpty()) + *errorMessage = msgEmptySnippet(inputFile, lineNo, identifier); + + return code; +} + QString QtXmlToSphinx::readFromLocation(const QString &location, const QString &identifier, QString *errorMessage) { @@ -586,7 +700,7 @@ QString QtXmlToSphinx::readFromLocation(const QString &location, const QString & QTextStream(errorMessage) << "Could not read code snippet file: " << QDir::toNativeSeparators(inputFile.fileName()) << ": " << inputFile.errorString(); - return QString(); // null + return {}; // null } QString code = u""_s; // non-null @@ -596,37 +710,8 @@ QString QtXmlToSphinx::readFromLocation(const QString &location, const QString & return CodeSnipHelpers::fixSpaces(code); } - const QRegularExpression searchString(u"//!\\s*\\["_s - + identifier + u"\\]"_s); - Q_ASSERT(searchString.isValid()); - static const QRegularExpression cppCodeSnippetCode(u"//!\\s*\\[[\\w\\d\\s]+\\]"_s); - Q_ASSERT(cppCodeSnippetCode.isValid()); - static const QRegularExpression pythonCodeSnippetCode(u"#!\\s*\\[[\\w\\d\\s]+\\]"_s); - Q_ASSERT(pythonCodeSnippetCode.isValid()); - - bool getCode = false; - - while (!inputFile.atEnd()) { - QString line = QString::fromUtf8(inputFile.readLine()); - if (getCode && !line.contains(searchString)) { - line.remove(cppCodeSnippetCode); - line.remove(pythonCodeSnippetCode); - code += line; - } else if (line.contains(searchString)) { - if (getCode) - break; - getCode = true; - } - } - - if (!getCode) { - QTextStream(errorMessage) << "Code snippet file found (" - << QDir::toNativeSeparators(location) << "), but snippet [" - << identifier << "] not found."; - return QString(); // null - } - - return CodeSnipHelpers::fixSpaces(code); + code = readSnippet(inputFile, identifier, errorMessage); + return code.isEmpty() ? QString{} : CodeSnipHelpers::fixSpaces(code); // maintain isNull() } void QtXmlToSphinx::handleHeadingTag(QXmlStreamReader& reader) @@ -689,9 +774,9 @@ void QtXmlToSphinx::handleParaTagEnd() { QString result = popOutputBuffer().simplified(); if (result.startsWith(u"**Warning:**")) - result.replace(0, 12, QStringLiteral(".. warning:: ")); + result.replace(0, 12, ".. warning:: "_L1); else if (result.startsWith(u"**Note:**")) - result.replace(0, 9, QStringLiteral(".. note:: ")); + result.replace(0, 9, ".. note:: "_L1); m_output << result << "\n\n"; } @@ -760,23 +845,23 @@ void QtXmlToSphinx::handleArgumentTag(QXmlStreamReader& reader) } } -static inline QString functionLinkType() { return QStringLiteral("function"); } -static inline QString classLinkType() { return QStringLiteral("class"); } +constexpr auto functionLinkType = "function"_L1; +constexpr auto classLinkType = "class"_L1; static inline QString fixLinkType(QStringView type) { // TODO: create a flag PROPERTY-AS-FUNCTION to ask if the properties // are recognized as such or not in the binding if (type == u"property") - return functionLinkType(); + return functionLinkType; if (type == u"typedef") - return classLinkType(); + return classLinkType; return type.toString(); } static inline QString linkSourceAttribute(const QString &type) { - if (type == functionLinkType() || type == classLinkType()) + if (type == functionLinkType || type == classLinkType) return u"raw"_s; return type == u"enum" || type == u"page" ? type : u"href"_s; @@ -802,7 +887,7 @@ void QtXmlToSphinx::handleSeeAlsoTag(QXmlStreamReader& reader) const QString text = textR.toString(); if (m_seeAlsoContext.isNull()) { const QString type = text.endsWith(u"()") - ? functionLinkType() : classLinkType(); + ? functionLinkType : classLinkType; m_seeAlsoContext.reset(handleLinkStart(type, text)); } handleLinkText(m_seeAlsoContext.data(), text); @@ -821,7 +906,7 @@ void QtXmlToSphinx::handleSeeAlsoTag(QXmlStreamReader& reader) } } -static inline QString fallbackPathAttribute() { return QStringLiteral("path"); } +constexpr auto fallbackPathAttribute = "path"_L1; template <class Indent> // const char*/class Indentor void formatSnippet(TextStream &str, Indent indent, const QString &snippet) @@ -856,14 +941,15 @@ void QtXmlToSphinx::handleSnippetTag(QXmlStreamReader& reader) || m_lastTagName == u"dots" || m_lastTagName == u"codeline"; if (consecutiveSnippet) { m_output.flush(); - m_output.string()->chop(2); + m_output.string()->chop(1); // Strip newline from previous snippet } QString location = reader.attributes().value(u"location"_s).toString(); QString identifier = reader.attributes().value(u"identifier"_s).toString(); QString fallbackPath; - if (reader.attributes().hasAttribute(fallbackPathAttribute())) - fallbackPath = reader.attributes().value(fallbackPathAttribute()).toString(); + if (reader.attributes().hasAttribute(fallbackPathAttribute)) + fallbackPath = reader.attributes().value(fallbackPathAttribute).toString(); QString errorMessage; + const Snippet snippet = readSnippetFromLocations(location, identifier, fallbackPath, &errorMessage); if (!errorMessage.isEmpty()) @@ -886,6 +972,7 @@ void QtXmlToSphinx::handleSnippetTag(QXmlStreamReader& reader) m_output << '\n'; } } + void QtXmlToSphinx::handleDotsTag(QXmlStreamReader& reader) { QXmlStreamReader::TokenType token = reader.tokenType(); @@ -916,11 +1003,11 @@ void QtXmlToSphinx::handleTableTag(QXmlStreamReader& reader) if (token == QXmlStreamReader::StartElement) { if (parentTag() == WebXmlTag::para) handleParaTagEnd(); // End <para> to prevent the table from being rst-escaped - m_currentTable.clear(); + m_tables.push({}); } else if (token == QXmlStreamReader::EndElement) { // write the table on m_output formatCurrentTable(); - m_currentTable.clear(); + m_tables.pop(); if (parentTag() == WebXmlTag::para) handleParaTagStart(); } @@ -936,7 +1023,7 @@ void QtXmlToSphinx::handleTermTag(QXmlStreamReader& reader) } else if (token == QXmlStreamReader::EndElement) { TableCell cell; cell.data = popOutputBuffer().trimmed(); - m_currentTable.appendRow(TableRow(1, cell)); + m_tables.back().appendRow(TableRow(1, cell)); } } @@ -945,18 +1032,20 @@ void QtXmlToSphinx::handleItemTag(QXmlStreamReader& reader) { QXmlStreamReader::TokenType token = reader.tokenType(); if (token == QXmlStreamReader::StartElement) { - if (m_currentTable.isEmpty()) - m_currentTable.appendRow({}); - TableRow& row = m_currentTable.last(); + auto &table = m_tables.back(); + if (table.isEmpty()) + table.appendRow({}); + TableRow& row = table.last(); TableCell cell; cell.colSpan = reader.attributes().value(u"colspan"_s).toShort(); cell.rowSpan = reader.attributes().value(u"rowspan"_s).toShort(); row << cell; pushOutputBuffer(); } else if (token == QXmlStreamReader::EndElement) { - QString data = popOutputBuffer().trimmed(); - if (!m_currentTable.isEmpty()) { - TableRow& row = m_currentTable.last(); + QString data = trimLeadingNewlines(trimRight(popOutputBuffer())); + auto &table = m_tables.back(); + if (!table.isEmpty()) { + TableRow& row = table.last(); if (!row.isEmpty()) row.last().data = data; } @@ -969,15 +1058,16 @@ void QtXmlToSphinx::handleHeaderTag(QXmlStreamReader &reader) // C++ header with "name"/"href" attributes. if (reader.tokenType() == QXmlStreamReader::StartElement && !reader.attributes().hasAttribute(u"name"_s)) { - m_currentTable.setHeaderEnabled(true); - m_currentTable.appendRow({}); + auto &table = m_tables.back(); + table.setHeaderEnabled(true); + table.appendRow({}); } } void QtXmlToSphinx::handleRowTag(QXmlStreamReader& reader) { if (reader.tokenType() == QXmlStreamReader::StartElement) - m_currentTable.appendRow({}); + m_tables.back().appendRow({}); } enum ListType { BulletList, OrderedList, EnumeratedList }; @@ -993,27 +1083,29 @@ static inline ListType webXmlListType(QStringView t) void QtXmlToSphinx::handleListTag(QXmlStreamReader& reader) { - // BUG We do not support a list inside a table cell static ListType listType = BulletList; QXmlStreamReader::TokenType token = reader.tokenType(); if (token == QXmlStreamReader::StartElement) { + m_tables.push({}); + auto &table = m_tables.back(); listType = webXmlListType(reader.attributes().value(u"type"_s)); if (listType == EnumeratedList) { - m_currentTable.appendRow(TableRow{TableCell(u"Constant"_s), - TableCell(u"Description"_s)}); - m_currentTable.setHeaderEnabled(true); + table.appendRow(TableRow{TableCell(u"Constant"_s), + TableCell(u"Description"_s)}); + table.setHeaderEnabled(true); } m_output.indent(); } else if (token == QXmlStreamReader::EndElement) { m_output.outdent(); - if (!m_currentTable.isEmpty()) { + const auto &table = m_tables.back(); + if (!table.isEmpty()) { switch (listType) { case BulletList: case OrderedList: { m_output << '\n'; const char *separator = listType == BulletList ? "* " : "#. "; const char *indentLine = listType == BulletList ? " " : " "; - for (const TableCell &cell : m_currentTable.constFirst()) { + for (const TableCell &cell : table.constFirst()) { const auto itemLines = QStringView{cell.data}.split(u'\n'); m_output << separator << itemLines.constFirst() << '\n'; for (qsizetype i = 1, max = itemLines.size(); i < max; ++i) @@ -1027,7 +1119,7 @@ void QtXmlToSphinx::handleListTag(QXmlStreamReader& reader) break; } } - m_currentTable.clear(); + m_tables.pop(); } } @@ -1069,7 +1161,7 @@ QtXmlToSphinxLink *QtXmlToSphinx::handleLinkStart(const QString &type, QString r if (type == u"external" || isHttpLink(ref)) { result->type = QtXmlToSphinxLink::External; - } else if (type == functionLinkType() && !m_context.isEmpty()) { + } else if (type == functionLinkType && !m_context.isEmpty()) { result->type = QtXmlToSphinxLink::Method; const auto rawlinklist = QStringView{result->linkRef}.split(u'.'); if (rawlinklist.size() == 1 || rawlinklist.constFirst() == m_context) { @@ -1080,9 +1172,9 @@ QtXmlToSphinxLink *QtXmlToSphinx::handleLinkStart(const QString &type, QString r } else { result->linkRef = m_generator->expandFunction(result->linkRef); } - } else if (type == functionLinkType() && m_context.isEmpty()) { + } else if (type == functionLinkType && m_context.isEmpty()) { result->type = QtXmlToSphinxLink::Function; - } else if (type == classLinkType()) { + } else if (type == classLinkType) { result->type = QtXmlToSphinxLink::Class; result->linkRef = m_generator->expandClass(m_context, result->linkRef); } else if (type == u"enum") { @@ -1122,10 +1214,10 @@ static QString fixLinkText(const QtXmlToSphinxLink *linkContext, else QtXmlToSphinx::stripPythonQualifiers(&linktext); if (linkContext->linkRef == linktext) - return QString(); + return {}; if ((linkContext->type & QtXmlToSphinxLink::FunctionMask) != 0 && (linkContext->linkRef + u"()"_s) == linktext) { - return QString(); + return {}; } return linktext; } @@ -1148,36 +1240,17 @@ WebXmlTag QtXmlToSphinx::parentTag() const // Copy images that are placed in a subdirectory "images" under the webxml files // by qdoc to a matching subdirectory under the "rst/PySide6/<module>" directory -static bool copyImage(const QString &href, const QString &docDataDir, - const QString &context, const QString &outputDir, +static bool copyImage(const QString &docDataDir, const QString &relativeSourceFile, + const QString &outputDir, const QString &relativeTargetFile, const QLoggingCategory &lc, QString *errorMessage) { - const QChar slash = u'/'; - const int lastSlash = href.lastIndexOf(slash); - const QString imagePath = lastSlash != -1 ? href.left(lastSlash) : QString(); - const QString imageFileName = lastSlash != -1 ? href.right(href.size() - lastSlash - 1) : href; - QFileInfo imageSource(docDataDir + slash + href); - if (!imageSource.exists()) { - QTextStream(errorMessage) << "Image " << href << " does not exist in " - << QDir::toNativeSeparators(docDataDir); - return false; - } - // Determine directory from context, "Pyside2.QtGui.QPainter" ->"Pyside2/QtGui". - // FIXME: Not perfect yet, should have knowledge about namespaces (DataVis3D) or - // nested classes "Pyside2.QtGui.QTouchEvent.QTouchPoint". - QString relativeTargetDir = context; - const int lastDot = relativeTargetDir.lastIndexOf(u'.'); - if (lastDot != -1) - relativeTargetDir.truncate(lastDot); - relativeTargetDir.replace(u'.', slash); - if (!imagePath.isEmpty()) - relativeTargetDir += slash + imagePath; - - const QString targetDir = outputDir + slash + relativeTargetDir; - const QString targetFileName = targetDir + slash + imageFileName; + QString targetFileName = outputDir + u'/' + relativeTargetFile; if (QFileInfo::exists(targetFileName)) return true; - if (!QFileInfo::exists(targetDir)) { + + QString relativeTargetDir = relativeTargetFile; + relativeTargetDir.truncate(qMax(relativeTargetDir.lastIndexOf(u'/'), qsizetype(0))); + if (!relativeTargetDir.isEmpty() && !QFileInfo::exists(outputDir + u'/' + relativeTargetDir)) { const QDir outDir(outputDir); if (!outDir.mkpath(relativeTargetDir)) { QTextStream(errorMessage) << "Cannot create " << QDir::toNativeSeparators(relativeTargetDir) @@ -1186,28 +1259,29 @@ static bool copyImage(const QString &href, const QString &docDataDir, } } - QFile source(imageSource.absoluteFilePath()); + QFile source(docDataDir + u'/' + relativeSourceFile); if (!source.copy(targetFileName)) { QTextStream(errorMessage) << "Cannot copy " << QDir::toNativeSeparators(source.fileName()) << " to " << QDir::toNativeSeparators(targetFileName) << ": " << source.errorString(); return false; } - qCDebug(lc).noquote().nospace() << __FUNCTION__ << " href=\"" - << href << "\", context=\"" << context << "\", docDataDir=\"" - << docDataDir << "\", outputDir=\"" << outputDir << "\", copied \"" - << source.fileName() << "\"->\"" << targetFileName << '"'; + + qCDebug(lc).noquote().nospace() << __FUNCTION__ << " \"" << relativeSourceFile + << "\"->\"" << relativeTargetFile << '"'; return true; } bool QtXmlToSphinx::copyImage(const QString &href) const { QString errorMessage; - const bool result = - ::copyImage(href, m_parameters.docDataDir, m_context, - m_parameters.outputDirectory, - m_generator->loggingCategory(), - &errorMessage); + const auto imagePaths = m_generator->resolveImage(href, m_context); + const bool result = ::copyImage(m_parameters.docDataDir, + imagePaths.source, + m_parameters.outputDirectory, + imagePaths.target, + m_generator->loggingCategory(), + &errorMessage); if (!result) throw Exception(errorMessage); return result; @@ -1233,7 +1307,7 @@ void QtXmlToSphinx::handleInlineImageTag(QXmlStreamReader& reader) // enclosed by '|' and define it further down. Determine tag from the base //file name with number. QString tag = href; - int pos = tag.lastIndexOf(u'/'); + auto pos = tag.lastIndexOf(u'/'); if (pos != -1) tag.remove(0, pos + 1); pos = tag.indexOf(u'.'); @@ -1301,11 +1375,11 @@ void QtXmlToSphinx::handlePageTag(QXmlStreamReader &reader) m_output << disableIndent; - const auto title = reader.attributes().value(titleAttribute()); + const auto title = reader.attributes().value("title"); if (!title.isEmpty()) m_output << rstLabel(title.toString()); - const auto fullTitle = reader.attributes().value(fullTitleAttribute()); + const auto fullTitle = reader.attributes().value("fulltitle"); const int size = fullTitle.isEmpty() ? writeEscapedRstText(m_output, title) : writeEscapedRstText(m_output, fullTitle); @@ -1318,7 +1392,7 @@ void QtXmlToSphinx::handleTargetTag(QXmlStreamReader &reader) { if (reader.tokenType() != QXmlStreamReader::StartElement) return; - const auto name = reader.attributes().value(nameAttribute()); + const auto name = reader.attributes().value("name"); if (!name.isEmpty()) m_output << rstLabel(name.toString()); } @@ -1493,13 +1567,11 @@ void QtXmlToSphinx::Table::format(TextStream& s) const // print line s << '+'; for (qsizetype col = 0; col < headerColumnCount; ++col) { - char c; + char c = '-'; if (col >= row.size() || row[col].rowSpan == -1) c = ' '; else if (i == 1 && hasHeader()) c = '='; - else - c = '-'; s << Pad(c, colWidths.at(col)) << '+'; } s << '\n'; diff --git a/sources/shiboken6/generator/qtdoc/qtxmltosphinx.h b/sources/shiboken6/generator/qtdoc/qtxmltosphinx.h index e3efde412..398c5bc97 100644 --- a/sources/shiboken6/generator/qtdoc/qtxmltosphinx.h +++ b/sources/shiboken6/generator/qtdoc/qtxmltosphinx.h @@ -8,9 +8,10 @@ #include <QtCore/QList> #include <QtCore/QScopedPointer> -#include <QtCore/QSharedPointer> #include <QtCore/QStack> +#include <memory> + QT_BEGIN_NAMESPACE class QDebug; class QXmlStreamReader; @@ -69,14 +70,9 @@ public: return m_normalized; } - void clear() { - m_normalized = false; - m_rows.clear(); - } - void appendRow(const TableRow &row) { m_rows.append(row); } - const TableRow &constFirst() { return m_rows.constFirst(); } + const TableRow &constFirst() const { return m_rows.constFirst(); } TableRow &first() { return m_rows.first(); } TableRow &last() { return m_rows.last(); } @@ -105,8 +101,12 @@ public: static void stripPythonQualifiers(QString *s); + // For testing + static QString readSnippet(QIODevice &inputFile, const QString &identifier, + QString *errorMessage); + private: - using StringSharedPtr = QSharedPointer<QString>; + using StringSharedPtr = std::shared_ptr<QString>; QString transform(const QString& doc); @@ -160,7 +160,7 @@ private: QStack<StringSharedPtr> m_buffers; // Maintain address stability since it used in TextStream - Table m_currentTable; + QStack<Table> m_tables; // Stack of tables, used for <table><list> with nested <item> QScopedPointer<QtXmlToSphinxLink> m_linkContext; // for <link> QScopedPointer<QtXmlToSphinxLink> m_seeAlsoContext; // for <see-also>foo()</see-also> QString m_context; @@ -173,6 +173,8 @@ private: QString m_opened_anchor; QList<InlineImage> m_inlineImages; + bool m_containsAutoTranslations = false; + struct Snippet { enum Result { @@ -186,10 +188,12 @@ private: Result result; }; + void setAutoTranslatedNote(QString *str) const; + Snippet readSnippetFromLocations(const QString &path, const QString &identifier, const QString &fallbackPath, - QString *errorMessage) const; + QString *errorMessage); static QString readFromLocation(const QString &location, const QString &identifier, QString *errorMessage); void pushOutputBuffer(); diff --git a/sources/shiboken6/generator/qtdoc/qtxmltosphinxinterface.h b/sources/shiboken6/generator/qtdoc/qtxmltosphinxinterface.h index 16eefad83..d4a098a12 100644 --- a/sources/shiboken6/generator/qtdoc/qtxmltosphinxinterface.h +++ b/sources/shiboken6/generator/qtdoc/qtxmltosphinxinterface.h @@ -53,6 +53,15 @@ public: virtual QtXmlToSphinxLink resolveLink(const QtXmlToSphinxLink &) const = 0; + // Resolve images paths relative to doc data directory/output directory. + struct Image + { + QString source; + QString target; + }; + + virtual Image resolveImage(const QString &href, const QString &context) const = 0; + virtual ~QtXmlToSphinxDocGeneratorInterface() = default; }; diff --git a/sources/shiboken6/generator/qtdoc/rstformat.h b/sources/shiboken6/generator/qtdoc/rstformat.h index 6e97c5fcd..8af7671fb 100644 --- a/sources/shiboken6/generator/qtdoc/rstformat.h +++ b/sources/shiboken6/generator/qtdoc/rstformat.h @@ -30,28 +30,6 @@ inline QByteArray rstDeprecationNote(const char *what) + what + QByteArrayLiteral(" is deprecated.\n\n"); } -class Pad -{ -public: - explicit Pad(char c, int count) : m_char(c), m_count(count) {} - - void write(TextStream &str) const - { - for (int i = 0; i < m_count; ++i) - str << m_char; - } - -private: - const char m_char; - const int m_count; -}; - -inline TextStream &operator<<(TextStream &str, const Pad &pad) -{ - pad.write(str); - return str; -} - template <class String> inline int writeEscapedRstText(TextStream &str, const String &s) { diff --git a/sources/shiboken6/generator/shiboken/configurablescope.h b/sources/shiboken6/generator/shiboken/configurablescope.h new file mode 100644 index 000000000..9040c7ad9 --- /dev/null +++ b/sources/shiboken6/generator/shiboken/configurablescope.h @@ -0,0 +1,33 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef CONFIGURABLESCOPE_H +#define CONFIGURABLESCOPE_H + +#include <textstream.h> +#include <configurabletypeentry.h> + +/// Enclose a scope within preprocessor conditions for configurable entries +class ConfigurableScope +{ +public: + explicit ConfigurableScope(TextStream &s, const ConfigurableTypeEntryCPtr &t) : + m_stream(s), + m_hasConfigCondition(t->hasConfigCondition()) + { + if (m_hasConfigCondition) + m_stream << t->configCondition() << '\n'; + } + + ~ConfigurableScope() + { + if (m_hasConfigCondition) + m_stream << "#endif\n"; + } + +private: + TextStream &m_stream; + const bool m_hasConfigCondition; +}; + +#endif // CONFIGURABLESCOPE_H diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp index 47fb271f9..97a38a08d 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp +++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp @@ -2,7 +2,9 @@ // 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" @@ -50,14 +52,30 @@ #include <algorithm> #include <cstring> #include <memory> +#include <set> using namespace Qt::StringLiterals; +static const char shibokenErrorsOccurred[] = "Shiboken::Errors::occurred() != nullptr"; + +static constexpr auto virtualMethodStaticReturnVar = "result"_L1; +static constexpr auto initFuncPrefix = "init_"_L1; + +static constexpr auto sbkObjectTypeF = "SbkObject_TypeF()"_L1; +static const char initInheritanceFunction[] = "initInheritance"; + +static QString mangleName(QString name) +{ + if (name == u"None" || name == u"False" || name == u"True" || name == u"from") + name += u'_'; + return name; +} + struct sbkUnusedVariableCast { - explicit sbkUnusedVariableCast(QString name) : m_name(name) {} + explicit sbkUnusedVariableCast(QAnyStringView name) : m_name(name) {} - const QString m_name; + const QAnyStringView m_name; }; TextStream &operator<<(TextStream &str, const sbkUnusedVariableCast &c) @@ -66,36 +84,23 @@ TextStream &operator<<(TextStream &str, const sbkUnusedVariableCast &c) return str; } -static const QString CPP_ARG0 = u"cppArg0"_s; -static const char methodDefSentinel[] = "{nullptr, nullptr, 0, nullptr} // Sentinel\n"; -const char *CppGenerator::PYTHON_TO_CPPCONVERSION_STRUCT = "Shiboken::Conversions::PythonToCppConversion"; +struct pyTypeGetSlot +{ + explicit pyTypeGetSlot(QAnyStringView funcType, QAnyStringView typeObject, + QAnyStringView aSlot) : + m_funcType(funcType), m_typeObject(typeObject), m_slot(aSlot) {} -static inline QString reprFunction() { return QStringLiteral("__repr__"); } + const QAnyStringView m_funcType; + const QAnyStringView m_typeObject; + const QAnyStringView m_slot; +}; -static const char typeNameFunc[] = R"CPP(template <class T> -static const char *typeNameOf(const T &t) +TextStream &operator<<(TextStream &str, const pyTypeGetSlot &p) { - 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; + str << "reinterpret_cast<" << p.m_funcType << ">(PepType_GetSlot(" + << p.m_typeObject << ", " << p.m_slot << "));\n"; + return str; } -)CPP"; TextStream &operator<<(TextStream &s, CppGenerator::ErrorReturn r) { @@ -117,6 +122,25 @@ TextStream &operator<<(TextStream &s, CppGenerator::ErrorReturn r) return s; } +static constexpr auto converterVar = "converter"_L1; + +struct registerConverterName +{ + explicit registerConverterName(QAnyStringView typeName, + QAnyStringView varName = converterVar) : + m_typeName(typeName), m_varName(varName) {} + + QAnyStringView m_typeName; + QAnyStringView m_varName; +}; + +TextStream &operator<<(TextStream &s, const registerConverterName &r) +{ + s << "Shiboken::Conversions::registerConverterName(" << r.m_varName + << ", \"" << r.m_typeName << "\");\n"; + return s; +} + // Protocol function name / function parameters / return type struct ProtocolEntry { @@ -149,7 +173,7 @@ const ProtocolEntries &mappingProtocols() u"PyObject*"_s}, {u"__msetitem__"_s, u"PyObject *self, PyObject *_key, PyObject *_value"_s, - intT()}}; + intT}}; return result; } @@ -166,16 +190,16 @@ const ProtocolEntries &sequenceProtocols() u"PyObject*"_s}, {u"__setitem__"_s, u"PyObject *self, Py_ssize_t _i, PyObject *_value"_s, - intT()}, + 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()}, + intT}, {u"__contains__"_s, u"PyObject *self, PyObject *_value"_s, - intT()}, + intT}, {u"__concat__"_s, u"PyObject *self, PyObject *_other"_s, u"PyObject*"_s} @@ -187,13 +211,13 @@ const ProtocolEntries &sequenceProtocols() static QString opaqueContainerCreationFunc(const AbstractMetaType &type) { const auto containerTypeEntry = - qSharedPointerCast<const ContainerTypeEntry>(type.typeEntry()); + 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(instantiationTypeEntry->name()); + result += containerTypeEntry->opaqueContainerName(type.instantiationCppSignatures()); return result; } @@ -213,135 +237,16 @@ QString CppGenerator::fileNameForContext(const GeneratorContext &context) const return fileNameForContextHelper(context, u"_wrapper.cpp"_s); } -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) -{ - // ( 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()); - return result; -} - -CppGenerator::BoolCastFunctionOptional - CppGenerator::boolCast(const AbstractMetaClass *metaClass) const -{ - const auto te = metaClass->typeEntry(); - if (te->isSmartPointer()) { - auto ste = qSharedPointerCast<const SmartPointerTypeEntry>(te); - - auto valueCheckMethod = ste->valueCheckMethod(); - if (!valueCheckMethod.isEmpty()) { - const auto func = metaClass->findFunction(valueCheckMethod); - if (func.isNull()) - throw Exception(msgMethodNotFound(metaClass, valueCheckMethod)); - return BoolCastFunction{func, false}; - } - - auto nullCheckMethod = ste->nullCheckMethod(); - if (!nullCheckMethod.isEmpty()) { - const auto func = metaClass->findFunction(nullCheckMethod); - if (func.isNull()) - throw Exception(msgMethodNotFound(metaClass, nullCheckMethod)); - return BoolCastFunction{func, true}; - } - } - - auto mode = te->operatorBoolMode(); - if (useOperatorBoolAsNbNonZero() - ? mode != TypeSystem::BoolCast::Disabled : mode == TypeSystem::BoolCast::Enabled) { - const auto func = metaClass->findOperatorBool(); - if (!func.isNull()) - return BoolCastFunction{func, false}; - } - - mode = te->isNullMode(); - if (useIsNullAsNbNonZero() - ? mode != TypeSystem::BoolCast::Disabled : mode == TypeSystem::BoolCast::Enabled) { - const auto func = metaClass->findQtIsNullMethod(); - if (!func.isNull()) - return BoolCastFunction{func, true}; - } - return std::nullopt; -} - -std::optional<AbstractMetaType> - CppGenerator::findSmartPointerInstantiation(const SmartPointerTypeEntryCPtr &pointer, - const TypeEntryCPtr &pointee) const -{ - for (const auto &smp : api().instantiatedSmartPointers()) { - const auto &i = smp.type; - if (i.typeEntry() == pointer && i.instantiations().at(0).typeEntry() == pointee) - return i; - } - return {}; -} - void CppGenerator::clearTpFuncs() { + // Functions that should not be registered under a name in PyMethodDef, + // but under a special constant under slots. m_tpFuncs = { {u"__str__"_s, {}}, {u"__str__"_s, {}}, - {reprFunction(), {}}, {u"__iter__"_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 @@ -351,7 +256,7 @@ static const char includeQDebug[] = "#endif\n" "#include <QtCore/QDebug>\n"; -static QString chopType(QString s) +QString CppGenerator::chopType(QString s) { if (s.endsWith(u"_Type")) s.chop(5); @@ -463,11 +368,7 @@ static QSet<QString> useIntSet() static bool _shouldInheritInt(const AbstractMetaEnum &cppEnum) { - if (!cppEnum.fullName().startsWith(u"PySide6."_s)) - return true; - // static auto intSet = useIntSet(); - // return intSet.contains(cppEnum.fullName()); - return false; + return !cppEnum.fullName().startsWith(u"PySide6."_s); } static QString BuildEnumFlagInfo(const AbstractMetaEnum &cppEnum) @@ -495,15 +396,15 @@ static QString BuildEnumFlagInfo(const AbstractMetaEnum &cppEnum) static void writePyGetSetDefEntry(TextStream &s, const QString &name, const QString &getFunc, const QString &setFunc) { - s << "{const_cast<char *>(\"" << name << "\"), " << getFunc << ", " + s << "{const_cast<char *>(\"" << mangleName(name) << "\"), " << getFunc << ", " << (setFunc.isEmpty() ? NULL_PTR : setFunc) << ", nullptr, nullptr},\n"; } static bool generateRichComparison(const GeneratorContext &c) { - auto *metaClass = c.metaClass(); + const auto metaClass = c.metaClass(); if (c.forSmartPointer()) { - auto te = qSharedPointerCast<const SmartPointerTypeEntry>(metaClass->typeEntry()); + auto te = std::static_pointer_cast<const SmartPointerTypeEntry>(metaClass->typeEntry()); return te->smartPointerType() == TypeSystem::SmartPointerType::Shared; } @@ -514,17 +415,16 @@ void CppGenerator::generateIncludes(TextStream &s, const GeneratorContext &class const IncludeGroupList &includes, const AbstractMetaClassCList &innerClasses) const { - const AbstractMetaClass *metaClass = classContext.metaClass(); + const auto metaClass = classContext.metaClass(); // write license comment s << licenseComment() << '\n'; const bool normalClass = !classContext.forSmartPointer(); - if (normalClass && !avoidProtectedHack() && !metaClass->isNamespace() - && !metaClass->hasPrivateDestructor()) { - s << "//workaround to access protected functions\n"; - s << "#define protected public\n\n"; - } + // 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"}; @@ -540,7 +440,7 @@ void CppGenerator::generateIncludes(TextStream &s, const GeneratorContext &class s << includeQDebug; if (metaClass->hasToStringCapability()) s << "#include <QtCore/QBuffer>\n"; - if (metaClass->isQObject()) { + if (isQObject(metaClass)) { s << "#include <pysideqobject.h>\n" << "#include <pysidesignal.h>\n" << "#include <pysideproperty.h>\n" @@ -548,7 +448,6 @@ void CppGenerator::generateIncludes(TextStream &s, const GeneratorContext &class << "#include <pysidemetafunction.h>\n"; } s << "#include <pysideqenum.h>\n" - << "#include <pysideqflags.h>\n" << "#include <pysideqmetatype.h>\n" << "#include <pysideutils.h>\n" << "#include <feature_select.h>\n" @@ -571,13 +470,16 @@ void CppGenerator::generateIncludes(TextStream &s, const GeneratorContext &class if (!innerClasses.isEmpty()) { s << "\n// inner classes\n"; - for (const AbstractMetaClass *innerClass : innerClasses) { + for (const auto &innerClass : innerClasses) { GeneratorContext innerClassContext = contextForClass(innerClass); s << "#include \"" << HeaderGenerator::headerFileNameForContext(innerClassContext) << "\"\n"; } } + if (avoidProtectedHack()) + s << baseWrapperIncludes(classContext); + for (const auto &g : includes) s << g; @@ -588,35 +490,34 @@ void CppGenerator::generateIncludes(TextStream &s, const GeneratorContext &class s << "#include <" << i << ">\n"; } -static const char openTargetExternC[] = R"( -// Target --------------------------------------------------------- - -extern "C" { -)"; - -static const char closeExternC[] = "} // extern \"C\"\n\n"; - // Write methods definition -static void writePyMethodDefs(TextStream &s, const QString &className, - const QString &methodsDefinitions, bool generateCopy) +void CppGenerator::writePyMethodDefs(TextStream &s, const QString &className, + const QString &methodsDefinitions) { s << "static PyMethodDef " << className << "_methods[] = {\n" << indent - << methodsDefinitions << '\n'; - if (generateCopy) { - s << "{\"__copy__\", reinterpret_cast<PyCFunction>(" << className << "___copy__)" - << ", METH_NOARGS, nullptr},\n"; + << 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())); + } } - s << methodDefSentinel << outdent - << "};\n\n"; } -static bool hasHashFunction(const AbstractMetaClass *c) +bool CppGenerator::hasHashFunction(const AbstractMetaClassCPtr &c) { return !c->typeEntry()->hashFunction().isEmpty() || c->hasHashFunction(); } -static bool needsTypeDiscoveryFunction(const AbstractMetaClass *c) +static bool needsTypeDiscoveryFunction(const AbstractMetaClassCPtr &c) { return c->baseClass() != nullptr && (c->isPolymorphic() || !c->typeEntry()->polymorphicIdValue().isEmpty()); @@ -646,7 +547,7 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon } s.setLanguage(TextStream::Language::Cpp); - const AbstractMetaClass *metaClass = classContext.metaClass(); + AbstractMetaClassCPtr metaClass = classContext.metaClass(); const auto typeEntry = metaClass->typeEntry(); auto innerClasses = metaClass->innerClasses(); @@ -671,10 +572,10 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon // Use class base namespace { - const AbstractMetaClass *context = metaClass->enclosingClass(); + AbstractMetaClassCPtr context = metaClass->enclosingClass(); while (context) { if (context->isNamespace() && !context->enclosingClass() - && qSharedPointerCast<const NamespaceTypeEntry>(context->typeEntry())->generateUsing()) { + && std::static_pointer_cast<const NamespaceTypeEntry>(context->typeEntry())->generateUsing()) { s << "\nusing namespace " << context->qualifiedCppName() << ";\n"; break; } @@ -682,7 +583,7 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon } } - s << '\n' << typeNameFunc << '\n'; + s << '\n'; // class inject-code native/beginning if (!typeEntry->codeSnips().isEmpty()) { @@ -694,7 +595,7 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon // python conversion rules if (typeEntry->isValue()) { - auto vte = qSharedPointerCast<const ValueTypeEntry>(typeEntry); + auto vte = std::static_pointer_cast<const ValueTypeEntry>(typeEntry); if (vte->hasTargetConversionRule()) { s << "// Python Conversion\n"; s << vte->targetConversionRule() << '\n'; @@ -721,13 +622,15 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon 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); @@ -767,22 +670,22 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon smd << "static PyMethodDef " << methDefName << " = " << indent << defEntries.constFirst() << outdent << ";\n\n"; } - if (!m_tpFuncs.contains(rfunc->name())) + 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 (typeEntry->isValue()) { - writeCopyFunction(s, classContext); - signatureStream << fullPythonClassName(metaClass) << ".__copy__()\n"; - } - // Write single method definitions s << singleMethodDefinitions; @@ -818,7 +721,7 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon } // Write methods definition - writePyMethodDefs(s, className, methodsDefinitions, typeEntry->isValue()); + writePyMethodDefs(s, className, methodsDefinitions); // Write tp_s/getattro function const AttroCheck attroCheck = checkAttroFunctionNeeds(metaClass); @@ -831,25 +734,8 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon writeNbBoolFunction(classContext, f.value(), s); if (supportsNumberProtocol(metaClass)) { - 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; - + const auto numberProtocolOps = numberProtocolOperators(metaClass); + for (const auto &overloads : numberProtocolOps) { OverloadData overloadData(overloads, api()); writeMethodWrapper(s, overloadData, classContext); writeSignatureInfo(signatureStream, overloadData); @@ -928,11 +814,10 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon writeClassDefinition(s, metaClass, classContext); s << '\n'; - if (needsTypeDiscoveryFunction(metaClass)) + if (needsTypeDiscoveryFunction(metaClass)) { writeTypeDiscoveryFunction(s, metaClass); - - writeFlagsNumberMethodsDefinitions(s, classEnums); - s << '\n'; + s << '\n'; + } writeConverterFunctions(s, metaClass, classContext); writeAddedTypeSignatures(signatureStream, typeEntry); @@ -950,147 +835,6 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon } } -static bool hasParameterPredicate(const AbstractMetaFunctionCPtr &f) -{ - return !f->arguments().isEmpty(); -} - -void CppGenerator::generateSmartPointerClass(TextStream &s, const GeneratorContext &classContext) -{ - s.setLanguage(TextStream::Language::Cpp); - const AbstractMetaClass *metaClass = classContext.metaClass(); - const auto typeEntry = qSharedPointerCast<const SmartPointerTypeEntry>(metaClass->typeEntry()); - const bool hasPointeeClass = classContext.pointeeClass() != nullptr; - const auto smartPointerType = typeEntry->smartPointerType(); - const bool isValueHandle = smartPointerType ==TypeSystem::SmartPointerType::ValueHandle; - - IncludeGroup includes{u"Extra includes"_s, typeEntry->extraIncludes()}; - if (hasPointeeClass) - includes.append(classContext.pointeeClass()->typeEntry()->include()); - generateIncludes(s, classContext, {includes}); - - s << '\n' << typeNameFunc << '\n'; - - // Create string literal for smart pointer getter method. - QString rawGetter = typeEntry->getter(); - s << "static const char " << SMART_POINTER_GETTER << "[] = \"" << rawGetter << "\";"; - - // class inject-code native/beginning - if (!typeEntry->codeSnips().isEmpty()) { - writeClassCodeSnips(s, typeEntry->codeSnips(), - TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode, - classContext); - s << '\n'; - } - - StringStream smd(TextStream::Language::Cpp); - StringStream md(TextStream::Language::Cpp); - StringStream signatureStream(TextStream::Language::Cpp); - - s << openTargetExternC; - - const auto &functionGroups = getFunctionGroups(metaClass); - - // Skip all public methods of the smart pointer except for the special - // methods declared in the type entry. - - auto ctors = metaClass->queryFunctions(FunctionQueryOption::Constructors); - if (!hasPointeeClass && !isValueHandle) { // Cannot generate "int*" - auto end = std::remove_if(ctors.begin(), ctors.end(), hasParameterPredicate); - ctors.erase(end, ctors.end()); - } - - if (!ctors.isEmpty()) { - OverloadData overloadData(ctors, api()); - writeConstructorWrapper(s, overloadData, classContext); - writeSignatureInfo(signatureStream, overloadData); - } - - if (!typeEntry->resetMethod().isEmpty()) { - auto it = functionGroups.constFind(typeEntry->resetMethod()); - if (it == functionGroups.cend()) - throw Exception(msgCannotFindSmartPointerMethod(typeEntry, typeEntry->resetMethod())); - AbstractMetaFunctionCList resets = it.value(); - if (!hasPointeeClass && !isValueHandle) { // Cannot generate "int*" - auto end = std::remove_if(resets.begin(), resets.end(), hasParameterPredicate); - resets.erase(end, resets.end()); - } - if (!resets.isEmpty()) - writeMethodWrapper(s, md, signatureStream, resets, classContext); - } - - auto it = functionGroups.constFind(rawGetter); - if (it == functionGroups.cend() || it.value().size() != 1) - throw Exception(msgCannotFindSmartPointerGetter(typeEntry)); - - writeMethodWrapper(s, md, signatureStream, it.value(), classContext); - - QStringList optionalMethods; - if (!typeEntry->refCountMethodName().isEmpty()) - optionalMethods.append(typeEntry->refCountMethodName()); - const QString valueCheckMethod = typeEntry->valueCheckMethod(); - if (!valueCheckMethod.isEmpty() && !valueCheckMethod.startsWith(u"operator")) - optionalMethods.append(valueCheckMethod); - if (!typeEntry->nullCheckMethod().isEmpty()) - optionalMethods.append(typeEntry->nullCheckMethod()); - - for (const QString &optionalMethod : optionalMethods) { - auto it = functionGroups.constFind(optionalMethod); - if (it == functionGroups.cend() || it.value().size() != 1) - throw Exception(msgCannotFindSmartPointerMethod(typeEntry, optionalMethod)); - writeMethodWrapper(s, md, signatureStream, it.value(), classContext); - } - - const QString methodsDefinitions = md.toString(); - const QString singleMethodDefinitions = smd.toString(); - - const QString className = chopType(cpythonTypeName(typeEntry)); - - writeCopyFunction(s, classContext); - signatureStream << fullPythonClassName(metaClass) << ".__copy__()\n"; - - // Write single method definitions - s << singleMethodDefinitions; - - // Write methods definition - writePyMethodDefs(s, className, methodsDefinitions, true /* ___copy__ */); - - // Write tp_s/getattro function - const auto boolCastOpt = boolCast(metaClass); - writeSmartPointerGetattroFunction(s, classContext, boolCastOpt); - writeSmartPointerSetattroFunction(s, classContext); - - if (boolCastOpt.has_value()) - writeNbBoolFunction(classContext, boolCastOpt.value(), s); - - if (smartPointerType == TypeSystem::SmartPointerType::Shared) - writeSmartPointerRichCompareFunction(s, classContext); - - s << closeExternC; - - if (hasHashFunction(metaClass)) - writeHashFunction(s, classContext); - - // Write tp_traverse and tp_clear functions. - writeTpTraverseFunction(s, metaClass); - writeTpClearFunction(s, metaClass); - - writeClassDefinition(s, metaClass, classContext); - - s << '\n'; - - writeConverterFunctions(s, metaClass, classContext); - writeClassRegister(s, metaClass, classContext, signatureStream); - - // class inject-code native/end - if (!typeEntry->codeSnips().isEmpty()) { - writeClassCodeSnips(s, typeEntry->codeSnips(), - TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode, - classContext); - s << '\n'; - } -} - void CppGenerator::writeMethodWrapper(TextStream &s, TextStream &definitionStream, TextStream &signatureStream, const AbstractMetaFunctionCList &overloads, @@ -1132,7 +876,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; @@ -1157,9 +901,10 @@ QString CppGenerator::getVirtualFunctionReturnTypeName(const AbstractMetaFunctio // SbkType would return null when the type is a container. auto typeEntry = func->type().typeEntry(); if (typeEntry->isContainer()) { - const auto cte = qSharedPointerCast<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")"_s; @@ -1186,8 +931,8 @@ QString CppGenerator::getVirtualFunctionReturnTypeName(const AbstractMetaFunctio if (func->type().isPrimitive()) return u'"' + func->type().name() + u'"'; - return u"reinterpret_cast<PyTypeObject *>(Shiboken::SbkType< "_s - + typeEntry->qualifiedCppName() + u" >())->tp_name"_s; + return u"Shiboken::SbkType< "_s + + typeEntry->qualifiedCppName() + u" >()->tp_name"_s; } // When writing an overridden method of a wrapper class, write the part @@ -1232,17 +977,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 u"return;"_s; + 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; ; ) { @@ -1258,8 +1010,8 @@ QString CppGenerator::virtualMethodReturn(TextStream &s, const ApiExtractorResul offset = match.capturedStart(1); } DefaultValue defaultReturnExpr(DefaultValue::Custom, expr); - return u"return "_s + defaultReturnExpr.returnValue() - + u';'; + result.statement += defaultReturnExpr.returnValue() + u';'; + return result; } } } @@ -1271,22 +1023,19 @@ QString CppGenerator::virtualMethodReturn(TextStream &s, const ApiExtractorResul errorMsg = msgCouldNotFindMinimalConstructor(errorMsg, func->type().cppSignature(), errorMessage); - qCWarning(lcShiboken).noquote().nospace() << errorMsg; - s << "\n#error " << errorMsg << '\n'; + throw Exception(errorMsg); } - if (returnType.referenceType() == LValueReference) { - s << "static " << returnType.typeEntry()->qualifiedCppName() - << " result;\n"; - return u"return result;"_s; - } - return u"return "_s + defaultReturnExpr->returnValue() - + u';'; + + 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). -QPair<QString, QChar> CppGenerator::virtualMethodNativeArg(const AbstractMetaFunctionCPtr &func, - const AbstractMetaArgument &arg) +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'}; @@ -1312,7 +1061,7 @@ static const char PYTHON_ARGS_ARRAY[] = "pyArgArray"; void CppGenerator::writeVirtualMethodNativeVectorCallArgs(TextStream &s, const AbstractMetaFunctionCPtr &func, const AbstractMetaArgumentList &arguments, - const QList<int> &invalidateArgs) const + const QList<int> &invalidateArgs) { Q_ASSERT(!arguments.isEmpty()); s << "PyObject *" << PYTHON_ARGS_ARRAY <<'[' << arguments.size() << "] = {\n" << indent; @@ -1333,15 +1082,15 @@ void CppGenerator::writeVirtualMethodNativeVectorCallArgs(TextStream &s, if (!invalidateArgs.isEmpty()) s << '\n'; for (int index : invalidateArgs) { - s << "const bool invalidateArg" << index << " = " << PYTHON_ARGS_ARRAY << - '[' << index - 1 << "]->ob_refcnt == 1;\n"; + 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) const + const QList<int> &invalidateArgs) { s << "Shiboken::AutoDecRef " << PYTHON_ARGS << '('; if (arguments.isEmpty()) { @@ -1361,8 +1110,8 @@ void CppGenerator::writeVirtualMethodNativeArgs(TextStream &s, << indent << argConversions.join(u",\n"_s) << outdent << "\n));\n"; for (int index : std::as_const(invalidateArgs)) { - s << "bool invalidateArg" << index << " = PyTuple_GET_ITEM(" << PYTHON_ARGS - << ", " << index - 1 << ")->ob_refcnt == 1;\n"; + s << "bool invalidateArg" << index << " = Py_REFCNT(PyTuple_GET_ITEM(" << PYTHON_ARGS + << ", " << index - 1 << ")) == 1;\n"; } } @@ -1373,13 +1122,42 @@ static bool isArgumentNotRemoved(const AbstractMetaArgument &a) // PyObject_Vectorcall(): since 3.9 static const char vectorCallCondition[] = - "#if !defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x03090000\n"; + "#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(Py_LIMITED_API) && Py_LIMITED_API >= 0x030A0000) || (!defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x03090000)\n"; + "#if !defined(PYPY_VERSION) && ((defined(Py_LIMITED_API) && Py_LIMITED_API >= 0x030A0000) || !defined(Py_LIMITED_API))\n"; static const char inverseNoArgsCallCondition[] = - "#if (defined(Py_LIMITED_API) && Py_LIMITED_API < 0x030A0000) || (!defined(Py_LIMITED_API) && PY_VERSION_HEX < 0x03090000)\n"; + "#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, @@ -1394,13 +1172,16 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, Generator::OriginalTypeDescription) << "\n{\n" << indent; - const QString returnStatement = virtualMethodReturn(s, api(), func, - func->modifications()); + const auto returnStatement = virtualMethodReturn(api(), func, + func->modifications()); + + if (returnStatement.needsReference) + writeVirtualMethodStaticReturnVar(s, func); const bool isAbstract = func->isAbstract(); if (isAbstract && func->isModifiedRemoved()) { - qCWarning(lcShiboken, "%s", qPrintable(msgPureVirtualFunctionRemoved(func.data()))); - s << returnStatement << '\n' << outdent << "}\n\n"; + qCWarning(lcShiboken, "%s", qPrintable(msgPureVirtualFunctionRemoved(func.get()))); + s << returnStatement.statement << '\n' << outdent << "}\n\n"; return; } @@ -1429,7 +1210,7 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, s << "if (m_PyMethodCache[" << cacheIndex << "])" << (multi_line ? " {\n" : "\n") << indent; writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType, - returnStatement, false); + returnStatement.statement, false); s << outdent; if (multi_line) s << "}\n"; @@ -1437,34 +1218,33 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, 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) + u':'; + 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; if (useOverrideCaching(func->ownerClass())) s << "m_PyMethodCache[" << cacheIndex << "] = true;\n"; writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType, - returnStatement, true); + returnStatement.statement, true); s << outdent << "}\n\n"; //WS + if (!snips.isEmpty()) { + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionPyOverride, + TypeSystem::ShellCode, func, false, lastArg); + } + + writeVirtualMethodPythonOverride(s, func, snips, returnStatement); +} + +void CppGenerator::writeVirtualMethodPythonOverride(TextStream &s, + const AbstractMetaFunctionCPtr &func, + const CodeSnipList &snips, + const VirtualMethodReturn &returnStatement) const +{ writeConversionRule(s, func, TypeSystem::TargetLangCode, false); bool invalidateReturn = false; @@ -1487,7 +1267,7 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, auto arguments = func->arguments(); auto removedEnd = std::stable_partition(arguments.begin(), arguments.end(), isArgumentNotRemoved); - if (isAbstract) { // Base function is not called, indicate unused arguments. + if (func->isAbstract()) { // Base function is not called, indicate unused arguments. for (auto it = removedEnd; it != arguments.end(); ++it) s << sbkUnusedVariableCast(it->name()); } @@ -1533,7 +1313,7 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, if (argCount > 0) { s << "PyObject_Vectorcall(" << PYTHON_OVERRIDE_VAR << ", " << PYTHON_ARGS_ARRAY << ", " << argCount << ", nullptr));\n"; - for (int argIndex : qAsConst(invalidateArgs)) { + for (int argIndex : std::as_const(invalidateArgs)) { s << "if (invalidateArg" << argIndex << ")\n" << indent << "Shiboken::Object::invalidate(" << PYTHON_ARGS_ARRAY << '[' << (argIndex - 1) << "]);\n" << outdent; @@ -1558,12 +1338,13 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, s << "if (" << PYTHON_RETURN_VAR << ".isNull()) {\n" << indent << "// An error happened in python code!\n" - << "PyErr_Print();\n" - << returnStatement << "\n" << outdent + << "Shiboken::Errors::storePythonOverrideErrorOrPrint(\"" + << func->ownerClass()->name() << "\", funcName);\n" + << returnStatement.statement << "\n" << outdent << "}\n"; if (invalidateReturn) { - s << "bool invalidateArg0 = " << PYTHON_RETURN_VAR << "->ob_refcnt == 1;\n" + s << "bool invalidateArg0 = Py_REFCNT(" << PYTHON_RETURN_VAR << ") == 1;\n" << "if (invalidateArg0)\n" << indent << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR << ".object());\n" << outdent; @@ -1571,7 +1352,7 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, if (!func->isVoid()) { - if (func->modifiedTypeName() != cPyObjectT()) { + if (func->modifiedTypeName() != cPyObjectT) { s << "// Check return type\n"; @@ -1583,10 +1364,10 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, << PYTHON_RETURN_VAR << ");\n" << outdent << "if (!" << PYTHON_TO_CPP_VAR << ") {\n" << indent << "Shiboken::Warnings::warnInvalidReturnValue(\"" - << func->ownerClass()->name() << "\", \"" << funcName << "\", " + << func->ownerClass()->name() << "\", funcName, " << getVirtualFunctionReturnTypeName(func) << ", " << "Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);\n" - << returnStatement << '\n' << outdent + << returnStatement.statement << '\n' << outdent << "}\n"; } else { @@ -1605,10 +1386,10 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, s << " && " << PYTHON_RETURN_VAR << " != Py_None"; s << ") {\n" << indent << "Shiboken::Warnings::warnInvalidReturnValue(\"" - << func->ownerClass()->name() << "\", \"" << funcName << "\", " + << func->ownerClass()->name() << "\", funcName, " << getVirtualFunctionReturnTypeName(func) << ", " << "Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);\n" - << returnStatement << '\n' << outdent + << returnStatement.statement << '\n' << outdent << "}\n"; } @@ -1645,13 +1426,14 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, 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 += u"::"_s + metaEnum->enclosingClass()->qualifiedCppName(); + typeCast += getFullTypeName(metaEnum->enclosingClass()); typeCast += u"::"_s + metaEnum->name(); s << '(' << typeCast << ')'; } @@ -1665,6 +1447,28 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, 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 { @@ -1672,7 +1476,7 @@ 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" @@ -1715,68 +1519,15 @@ 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::writeFlagsConverterFunctions(TextStream &s, - const FlagsTypeEntryCPtr &flagsType, - const QString &enumTypeName, - const QString &flagsCppTypeName, - const QString &enumTypeCheck) const -{ - Q_ASSERT(flagsType); - const QString flagsTypeName = fixedCppTypeName(flagsType); - const QString flagsPythonType = cpythonTypeNameExt(flagsType); - - StringStream c(TextStream::Language::Cpp); - c << "*reinterpret_cast<" << flagsCppTypeName << " *>(cppOut) =\n" - << " " << flagsCppTypeName - << "(QFlag(int(PySide::QFlags::getValue(reinterpret_cast<PySideQFlagsObject *>(pyIn)))))" - << ";\n"; - writePythonToCppFunction(s, c.toString(), flagsTypeName, flagsTypeName); - - QString pyTypeCheck = u"PyObject_TypeCheck(pyIn, "_s + flagsPythonType + u')'; - writeIsPythonConvertibleToCppFunction(s, flagsTypeName, flagsTypeName, pyTypeCheck); - - c.clear(); - - c << "const int castCppIn = int(*reinterpret_cast<const " - << flagsCppTypeName << " *>(cppIn));\n" << "return " - << "reinterpret_cast<PyObject *>(PySide::QFlags::newObject(castCppIn, " - << flagsPythonType << "));\n"; - writeCppToPythonFunction(s, c.toString(), flagsTypeName, flagsTypeName); - s << '\n'; - - c.clear(); - c << "*reinterpret_cast<" << flagsCppTypeName << " *>(cppOut) =\n" - << " " << flagsCppTypeName - << "(QFlag(int(Shiboken::Enum::getValue(pyIn))));\n"; - - writePythonToCppFunction(s, c.toString(), enumTypeName, flagsTypeName); - writeIsPythonConvertibleToCppFunction(s, enumTypeName, flagsTypeName, enumTypeCheck); - - c.clear(); - c << "Shiboken::AutoDecRef pyLong(PyNumber_Long(pyIn));\n" - << "*reinterpret_cast<" << flagsCppTypeName << " *>(cppOut) =\n" - << " " << flagsCppTypeName - << "(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 = u"PyNumber_Check(pyIn) && "_s + enumTypeCheck; - writePythonToCppFunction(s, c.toString(), u"number"_s, flagsTypeName); - writeIsPythonConvertibleToCppFunction(s, u"number"_s, flagsTypeName, numberCondition); -} - static void generateDeprecatedValueWarnings(TextStream &c, const AbstractMetaEnum &metaEnum, bool useSurrogateName) @@ -1796,14 +1547,15 @@ static void generateDeprecatedValueWarnings(TextStream &c, << "\", \"" << v.name() << "\");\nbreak;\n" << outdent; } if (deprecatedValues.size() < metaEnum.values().size()) - c << "default:\n" << indent << "break;\n" << outdent << "}\n"; + c << "default:\n" << indent << "break;\n" << outdent; + c << "}\n"; } void CppGenerator::writeEnumConverterFunctions(TextStream &s, const AbstractMetaEnum &metaEnum) const { if (metaEnum.isPrivate() || metaEnum.isAnonymous()) return; - EnumTypeEntryPtr enumType = metaEnum.typeEntry(); + EnumTypeEntryCPtr enumType = metaEnum.typeEntry(); Q_ASSERT(enumType); QString typeName = fixedCppTypeName(enumType); QString enumPythonType = cpythonTypeNameExt(enumType); @@ -1824,6 +1576,8 @@ void CppGenerator::writeEnumConverterFunctions(TextStream &s, const AbstractMeta generateDeprecatedValueWarnings(c, metaEnum, useSurrogateName); c << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) = value;\n"; + + ConfigurableScope configScope(s, enumType); writePythonToCppFunction(s, c.toString(), typeName, typeName); QString pyTypeCheck = u"PyObject_TypeCheck(pyIn, "_s + enumPythonType + u')'; @@ -1836,16 +1590,36 @@ void CppGenerator::writeEnumConverterFunctions(TextStream &s, const AbstractMeta << "Shiboken::Enum::newItem(" << enumPythonType << ", castCppIn);\n"; writeCppToPythonFunction(s, c.toString(), typeName, typeName); s << '\n'; +} - // QFlags part. - if (auto flags = enumType->flags(); !flags.isNull()) { - const QString flagsCppTypeName = useSurrogateName - ? cppTypeName : getFullTypeName(flags).trimmed(); - writeFlagsConverterFunctions(s, flags, typeName, flagsCppTypeName, pyTypeCheck); +static void writePointerToPythonConverter(TextStream &c, + const AbstractMetaClassCPtr &metaClass, + const QString &typeName, + const QString &cpythonType) +{ + c << "auto *pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));\n" + << "if (pyOut) {\n" << indent + << "Py_INCREF(pyOut);\nreturn pyOut;\n" << outdent + << "}\n"; + + const QString nameFunc = metaClass->typeEntry()->polymorphicNameFunction(); + if (nameFunc.isEmpty() && !metaClass->hasVirtualDestructor()) { + c << "return Shiboken::Object::newObjectWithHeuristics(" + << cpythonType << ", const_cast<void *>(cppIn), false);\n"; + return; } + + c << "auto *tCppIn = reinterpret_cast<const " << typeName << R"( *>(cppIn); +const char *typeName = )"; + if (nameFunc.isEmpty()) + c << "typeid(*tCppIn).name();\n"; + else + c << nameFunc << "(tCppIn);\n"; + c << "return Shiboken::Object::newObjectForPointer(" + << cpythonType << ", const_cast<void *>(cppIn), false, typeName);\n"; } -void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClass *metaClass, +void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext) const { s << "// Type conversion functions.\n\n"; @@ -1886,34 +1660,11 @@ void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClas // 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" << indent - << "Py_INCREF(pyOut);\nreturn pyOut;\n" << outdent - << "}\n" - << "bool changedTypeName = false;\n" - << "auto tCppIn = reinterpret_cast<const " << typeName << R"( *>(cppIn); -const char *typeName = )"; - - const QString nameFunc = metaClass->typeEntry()->polymorphicNameFunction(); - if (nameFunc.isEmpty()) - c << "typeid(*tCppIn).name();\n"; - else - c << nameFunc << "(tCppIn);\n"; - c << R"(auto sbkType = Shiboken::ObjectType::typeForTypeName(typeName); -if (sbkType && 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); @@ -1942,7 +1693,7 @@ return result;)"; c << "auto *source = reinterpret_cast<const " << typeName << " *>(cppIn);\n"; } c << "return Shiboken::Object::newObject(" << cpythonType - << ", new ::" << classContext.effectiveClassName() << '(' + << ", new " << globalScopePrefix(classContext) << classContext.effectiveClassName() << '(' << (isUniquePointer ? "std::move(*source)" : "*source") << "), true, true);"; writeCppToPythonFunction(s, c.toString(), sourceTypeName, targetTypeName); @@ -1952,7 +1703,7 @@ return result;)"; s << "// Python to C++ copy conversion.\n"; sourceTypeName = metaClass->name(); - targetTypeName = sourceTypeName + QStringLiteral("_COPY"); + targetTypeName = sourceTypeName + "_COPY"_L1; c.clear(); QString pyInVariable = u"pyIn"_s; @@ -1961,7 +1712,7 @@ return result;)"; c << '*' << outPtr << " = *" << cpythonWrapperCPtr(typeEntry, pyInVariable) << ';'; } else { - auto ste = qSharedPointerCast<const SmartPointerTypeEntry>(typeEntry); + 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; @@ -1999,7 +1750,7 @@ return result;)"; QString toCppConv; QString toCppPreConv; if (conv->isConversionOperator()) { - const AbstractMetaClass *sourceClass = conv->ownerClass(); + const auto sourceClass = conv->ownerClass(); typeCheck = u"PyObject_TypeCheck(pyIn, "_s + cpythonTypeNameExt(sourceClass->typeEntry()) + u')'; toCppConv = u'*' + cpythonWrapperCPtr(sourceClass->typeEntry(), @@ -2033,14 +1784,14 @@ return result;)"; StringStream pc(TextStream::Language::Cpp); pc << getFullTypeNameWithoutModifiers(sourceType) << " cppIn" << minimalConstructorExpression(api(), sourceType) << ";\n"; - writeToCppConversion(pc, sourceType, nullptr, pyInVariable, + writeToCppConversion(pc, sourceType, pyInVariable, u"cppIn"_s); pc << ';'; toCppPreConv = pc.toString(); toCppConv.append(u"cppIn"_s); } else if (!sourceType.isWrapperType()) { StringStream tcc(TextStream::Language::Cpp); - writeToCppConversion(tcc, sourceType, metaClass, pyInVariable, + writeToCppConversion(tcc, sourceType, pyInVariable, u"/*BOZO-1061*/"_s); toCppConv = tcc.toString(); } @@ -2052,7 +1803,7 @@ return result;)"; } if (typeEntry->isValue()) { - auto vte = qSharedPointerCast<const ValueTypeEntry>(typeEntry); + auto vte = std::static_pointer_cast<const ValueTypeEntry>(typeEntry); writeCustomConverterFunctions(s, vte->customConversion()); } } @@ -2060,7 +1811,7 @@ return result;)"; void CppGenerator::writeCustomConverterFunctions(TextStream &s, const CustomConversionPtr &customConversion) const { - if (customConversion.isNull()) + if (!customConversion) return; const TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions(); if (toCppConversions.isEmpty()) @@ -2072,7 +1823,7 @@ void CppGenerator::writeCustomConverterFunctions(TextStream &s, s << '\n'; } -void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClass *metaClass, +void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext) const { const auto typeEntry = metaClass->typeEntry(); @@ -2096,9 +1847,8 @@ void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClass 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) @@ -2123,14 +1873,14 @@ void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClass Qt::SkipEmptyParts); while (!lst.isEmpty()) { QString signature = lst.join(u"::"_s); - writeConversions(smartPointerName + u'<' + signature + 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(); @@ -2140,17 +1890,15 @@ 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 (!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"; + 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); @@ -2185,7 +1933,7 @@ void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClass } if (typeEntry->isValue()) { - auto vte = qSharedPointerCast<const ValueTypeEntry>(typeEntry); + auto vte = std::static_pointer_cast<const ValueTypeEntry>(typeEntry); writeCustomConverterRegister(s, vte->customConversion(), u"converter"_s); } } @@ -2194,7 +1942,7 @@ void CppGenerator::writeCustomConverterRegister(TextStream &s, const CustomConversionPtr &customConversion, const QString &converterVar) { - if (customConversion.isNull()) + if (!customConversion) return; const TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions(); @@ -2215,46 +1963,7 @@ void CppGenerator::writeContainerConverterFunctions(TextStream &s, writePythonToCppConversionFunctions(s, containerType); } -// Helpers to collect all smart pointer pointee base classes -static AbstractMetaClassCList findSmartPointeeBaseClasses(const ApiExtractorResult &api, - const AbstractMetaType &smartPointerType) -{ - AbstractMetaClassCList result; - auto instantiationsTe = smartPointerType.instantiations().at(0).typeEntry(); - auto targetClass = AbstractMetaClass::findClass(api.classes(), instantiationsTe); - if (targetClass != nullptr) - result = targetClass->allTypeSystemAncestors(); - return result; -} - -void CppGenerator::writeSmartPointerConverterFunctions(TextStream &s, - const AbstractMetaType &smartPointerType) const -{ - const auto baseClasses = findSmartPointeeBaseClasses(api(), smartPointerType); - if (baseClasses.isEmpty()) - return; - - auto smartPointerTypeEntry = - qSharedPointerCast<const SmartPointerTypeEntry>(smartPointerType.typeEntry()); - - // TODO: Missing conversion to smart pointer pointer type: - - s << "// Register smartpointer conversion for all derived classes\n"; - for (auto *base : baseClasses) { - auto baseTe = base->typeEntry(); - if (smartPointerTypeEntry->matchesInstantiation(baseTe)) { - if (auto opt = findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) { - const auto smartTargetType = opt.value(); - s << "// SmartPointer derived class: " - << smartTargetType.cppSignature() << "\n"; - writePythonToCppConversionFunctions(s, smartPointerType, - smartTargetType, {}, {}, {}); - } - } - } -} - -bool CppGenerator::needsArgumentErrorHandling(const OverloadData &overloadData) const +bool CppGenerator::needsArgumentErrorHandling(const OverloadData &overloadData) { if (overloadData.maxArgs() > 0) return true; @@ -2263,15 +1972,16 @@ bool CppGenerator::needsArgumentErrorHandling(const OverloadData &overloadData) return false; auto rfunc = overloadData.referenceFunction(); return rfunc->functionType() == AbstractMetaFunction::ConstructorFunction - && rfunc->ownerClass()->isQObject(); + && isQObject(rfunc->ownerClass()); } -void CppGenerator::writeMethodWrapperPreamble(TextStream &s,const OverloadData &overloadData, +void CppGenerator::writeMethodWrapperPreamble(TextStream &s, + const OverloadData &overloadData, const GeneratorContext &context, - ErrorReturn errorReturn) const + 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(); @@ -2281,7 +1991,9 @@ 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(); @@ -2291,7 +2003,7 @@ void CppGenerator::writeMethodWrapperPreamble(TextStream &s,const OverloadData & s << qualifiedCppName << " >()))\n" << indent << errorReturn << outdent << '\n'; } // Declare pointer for the underlying C++ object. - s << "::" << context.effectiveClassName() << " *cptr{};\n"; + s << globalScopePrefix(context) << context.effectiveClassName() << " *cptr{};\n"; initPythonArguments = maxArgs > 0; @@ -2311,11 +2023,18 @@ void CppGenerator::writeMethodWrapperPreamble(TextStream &s,const OverloadData & initPythonArguments = minArgs != maxArgs || maxArgs > 1; } - if (needsArgumentErrorHandling(overloadData)) { - 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" << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR; @@ -2341,20 +2060,20 @@ void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &ov 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(u"args"_s); - s << sbkUnusedVariableCast(u"kwds"_s); + 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" @@ -2365,11 +2084,11 @@ 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)) { - s << sbkUnusedVariableCast(u"sbkSelf"_s) - << sbkUnusedVariableCast(u"type"_s) - << sbkUnusedVariableCast(u"myType"_s); + s << sbkUnusedVariableCast("sbkSelf") + << sbkUnusedVariableCast("type") + << sbkUnusedVariableCast("myType"); if (needsMetaObject) - s << sbkUnusedVariableCast(u"metaObject"_s); + s << sbkUnusedVariableCast("metaObject"); s << "Shiboken::Errors::setInstantiateAbstractClassDisabledWrapper(\"" << metaClass->qualifiedCppName() << "\");\n" << errorReturn << outdent << "}\n\n"; @@ -2401,19 +2120,29 @@ void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &ov s << '\n'; if (overloadData.maxArgs() > 0) - writeOverloadedFunctionDecisor(s, overloadData); + writeOverloadedFunctionDecisor(s, overloadData, errorReturn); + + // Handles Python Multiple Inheritance + QString pre = needsMetaObject ? u"bool usesPyMI = "_s : u""_s; + s << "\n// PyMI support\n" + << pre << "Shiboken::callInheritedInit(self, args, kwds, fullName);\n" + << "if (" << shibokenErrorsOccurred << ")\n" + << indent << errorReturn << outdent << "\n"; writeFunctionCalls(s, overloadData, classContext, errorReturn); s << '\n'; const QString typeName = classContext.forSmartPointer() ? classContext.preciseType().cppSignature() : metaClass->qualifiedCppName(); - s << "if (PyErr_Occurred() || !Shiboken::Object::setCppPointer(sbkSelf, Shiboken::SbkType< ::" - << typeName << " >(), cptr)) {\n" + 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 @@ -2435,8 +2164,9 @@ void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &ov << "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"; } @@ -2474,8 +2204,6 @@ void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &ov } s << "\n\nreturn 1;\n"; - if (needsArgumentErrorHandling(overloadData)) - writeErrorSection(s, overloadData, errorReturn); s<< outdent << "}\n\n"; } @@ -2498,9 +2226,9 @@ void CppGenerator::writeMethodWrapper(TextStream &s, const OverloadData &overloa } s << ")\n{\n" << indent; if (rfunc->ownerClass() == nullptr || overloadData.hasStaticFunction()) - s << sbkUnusedVariableCast(u"self"_s); + s << sbkUnusedVariableCast(PYTHON_SELF_VAR); if (hasKwdArgs) - s << sbkUnusedVariableCast(u"kwds"_s); + s << sbkUnusedVariableCast("kwds"); writeMethodWrapperPreamble(s, overloadData, classContext); @@ -2527,7 +2255,8 @@ void CppGenerator::writeMethodWrapper(TextStream &s, const OverloadData &overloa << "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 (PyErr_Occurred() && (PyErr_ExceptionMatches(PyExc_NotImplementedError)" + << "if (" << shibokenErrorsOccurred + << " && (PyErr_ExceptionMatches(PyExc_NotImplementedError)" << " || PyErr_ExceptionMatches(PyExc_AttributeError))) {\n" << indent << "PyErr_Clear();\n" << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");\n" @@ -2537,14 +2266,14 @@ void CppGenerator::writeMethodWrapper(TextStream &s, const OverloadData &overloa << "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 << ") {\n" << indent; + << "if (" << PYTHON_RETURN_VAR << " == nullptr) {\n" << indent; if (maxArgs > 0) - writeOverloadedFunctionDecisor(s, overloadData); + 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); + writeOverloadedFunctionDecisor(s, overloadData, ErrorReturn::Default); writeFunctionCalls(s, overloadData, classContext, ErrorReturn::Default); } @@ -2563,9 +2292,6 @@ void CppGenerator::writeMethodWrapper(TextStream &s, const OverloadData &overloa s << "Py_RETURN_NONE;\n"; } - if (needsArgumentErrorHandling(overloadData)) - writeErrorSection(s, overloadData, ErrorReturn::Default); - s<< outdent << "}\n\n"; } @@ -2573,7 +2299,7 @@ void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData & ErrorReturn errorReturn) { const auto rfunc = overloadData.referenceFunction(); - s << "PyTuple_GET_SIZE(args);\n" << sbkUnusedVariableCast(u"numArgs"_s); + s << "PyTuple_GET_SIZE(args);\n" << sbkUnusedVariableCast("numArgs"); int minArgs = overloadData.minArgs(); int maxArgs = overloadData.maxArgs(); @@ -2602,15 +2328,16 @@ void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData & // Disable argument count checks for QObject constructors to allow for // passing properties as KW args. - auto *owner = rfunc->ownerClass(); - bool isQObjectConstructor = owner != nullptr && owner->isQObject() + 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 - << "goto " << cpythonFunctionName(rfunc) << "_TypeError;\n" << outdent; + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" + << outdent; } const QList<int> invalidArgsLength = overloadData.invalidArgumentLengths(); @@ -2622,7 +2349,8 @@ void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData & s << "numArgs == " << invalidArgsLength.at(i); } s << ")\n" << indent - << "goto " << cpythonFunctionName(rfunc) << "_TypeError;\n" << outdent; + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" + << outdent; } s << '\n'; @@ -2633,7 +2361,7 @@ void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData & funcName = rfunc->name(); QString argsVar = overloadData.hasVarargs() ? u"nonvarargs"_s : u"args"_s; - s << "if (!"; + s << "if ("; if (usesNamedArguments) { s << "PyArg_ParseTuple(" << argsVar << ", \"|" << QByteArray(maxArgs, 'O') << ':' << funcName << '"'; @@ -2643,7 +2371,7 @@ void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData & } for (int i = 0; i < maxArgs; i++) s << ", &(" << PYTHON_ARGS << '[' << i << "])"; - s << "))\n" << indent << errorReturn << outdent << '\n'; + s << ") == 0)\n" << indent << errorReturn << outdent << '\n'; } void CppGenerator::writeCppSelfConversion(TextStream &s, const GeneratorContext &context, @@ -2654,23 +2382,15 @@ void CppGenerator::writeCppSelfConversion(TextStream &s, const GeneratorContext return; } - static const QString pythonSelfVar = u"self"_s; if (useWrapperClass) s << "static_cast<" << className << " *>("; - s << cpythonWrapperCPtr(context.metaClass(), pythonSelfVar); + s << cpythonWrapperCPtr(context.metaClass(), PYTHON_SELF_VAR); if (useWrapperClass) s << ')'; } -void CppGenerator::writeSmartPointerCppSelfConversion(TextStream &s, - const GeneratorContext &context) -{ - Q_ASSERT(context.forSmartPointer()); - s << cpythonWrapperCPtr(context.preciseType(), u"self"_s); -} - -static inline void writeCppSelfVarDef(TextStream &s, - CppGenerator::CppSelfDefinitionFlags flags = {}) +void CppGenerator::writeCppSelfVarDef(TextStream &s, + CppSelfDefinitionFlags flags) { if (flags.testFlag(CppGenerator::CppSelfAsReference)) s << "auto &" << CPP_SELF_VAR << " = *"; @@ -2678,22 +2398,10 @@ static inline void writeCppSelfVarDef(TextStream &s, s << "auto *" << CPP_SELF_VAR << " = "; } -void CppGenerator::writeSmartPointerCppSelfDefinition(TextStream &s, - const GeneratorContext &context, - ErrorReturn errorReturn, - CppSelfDefinitionFlags flags) -{ - Q_ASSERT(context.forSmartPointer()); - writeInvalidPyObjectCheck(s, u"self"_s, errorReturn); - writeCppSelfVarDef(s, flags); - writeSmartPointerCppSelfConversion(s, context); - s << ";\n"; -} - void CppGenerator::writeCppSelfDefinition(TextStream &s, const GeneratorContext &context, ErrorReturn errorReturn, - CppSelfDefinitionFlags flags) const + CppSelfDefinitionFlags flags) { Q_ASSERT(!(flags.testFlag(CppSelfAsReference) && flags.testFlag(HasStaticOverload))); if (context.forSmartPointer()) { @@ -2701,7 +2409,7 @@ void CppGenerator::writeCppSelfDefinition(TextStream &s, 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. @@ -2709,10 +2417,9 @@ void CppGenerator::writeCppSelfDefinition(TextStream &s, && cppWrapper.testFlag(AbstractMetaClass::CppProtectedHackWrapper); Q_ASSERT(!useWrapperClass || context.useWrapper()); const QString className = useWrapperClass - ? context.wrapperName() - : (u"::"_s + metaClass->qualifiedCppName()); + ? context.wrapperName() : getFullTypeName(metaClass); - writeInvalidPyObjectCheck(s, u"self"_s, errorReturn); + writeInvalidPyObjectCheck(s, PYTHON_SELF_VAR, errorReturn); if (flags.testFlag(CppSelfAsReference)) { writeCppSelfVarDef(s, flags); @@ -2745,7 +2452,7 @@ void CppGenerator::writeCppSelfDefinition(TextStream &s, const AbstractMetaFunctionCPtr &func, const GeneratorContext &context, ErrorReturn errorReturn, - CppSelfDefinitionFlags flags) const + CppSelfDefinitionFlags flags) { if (!func->ownerClass() || func->isConstructor()) return; @@ -2761,24 +2468,32 @@ void CppGenerator::writeCppSelfDefinition(TextStream &s, writeCppSelfDefinition(s, context, errorReturn, flags); } -void CppGenerator::writeErrorSection(TextStream &s, const OverloadData &overloadData, - ErrorReturn errorReturn) +QString CppGenerator::returnErrorWrongArguments(const OverloadData &overloadData, + ErrorReturn errorReturn) { const auto rfunc = overloadData.referenceFunction(); QString argsVar = overloadData.pythonFunctionWrapperUsesListOfArguments() ? u"args"_s : PYTHON_ARG; - s << '\n' << cpythonFunctionName(rfunc) << "_TypeError:\n" << indent - << "Shiboken::setErrorAboutWrongArguments(" << argsVar << ", fullName, errInfo);\n" - << errorReturn << outdent; + switch (errorReturn) { + case ErrorReturn::Default: + return u"Shiboken::returnWrongArguments("_s + argsVar + u", fullName, errInfo)"_s; + case ErrorReturn::Zero: + return u"Shiboken::returnWrongArguments_Zero("_s + argsVar + u", fullName, errInfo)"_s; + case ErrorReturn::MinusOne: + return u"Shiboken::returnWrongArguments_MinusOne("_s + argsVar + u", fullName, errInfo)"_s; + case ErrorReturn::Void: + Q_ASSERT(false); + } + return {}; } void CppGenerator::writeFunctionReturnErrorCheckSection(TextStream &s, ErrorReturn errorReturn, bool hasReturnValue) { - s << "if (PyErr_Occurred()"; + s << "if (" << shibokenErrorsOccurred; if (hasReturnValue) - s << " || !" << PYTHON_RETURN_VAR; + s << " || " << PYTHON_RETURN_VAR << " == nullptr"; s << ") {\n" << indent; if (hasReturnValue) s << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");\n"; @@ -2886,7 +2601,7 @@ static void checkTypeViability(const AbstractMetaFunctionCPtr &func) } void CppGenerator::writeTypeCheck(TextStream &s, - const QSharedPointer<OverloadDataNode> &overloadData, + const std::shared_ptr<OverloadDataNode> &overloadData, const QString &argumentName) { QSet<TypeEntryCPtr> numericTypes; @@ -2918,7 +2633,7 @@ qsizetype CppGenerator::writeArgumentConversion(TextStream &s, const QString &argName, const QString &pyArgName, ErrorReturn errorReturn, - const AbstractMetaClass *context, + const AbstractMetaClassCPtr &context, const QString &defaultValue, bool castArgumentAsUnused) const { @@ -2950,12 +2665,12 @@ static inline QString arrayHandleType(const AbstractMetaTypeList &nestedArrayTyp { switch (nestedArrayTypes.size()) { case 1: - return QStringLiteral("Shiboken::Conversions::ArrayHandle<") + 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()) + u'>'; } @@ -2994,7 +2709,7 @@ qsizetype CppGenerator::writePythonToCppTypeConversion(TextStream &s, const AbstractMetaType &type, const QString &pyIn, const QString &cppOut, - const AbstractMetaClass *context, + const AbstractMetaClassCPtr &context, const QString &defaultValue) const { TypeEntryCPtr typeEntry = type.typeEntry(); @@ -3030,7 +2745,7 @@ qsizetype CppGenerator::writePythonToCppTypeConversion(TextStream &s, // conversion for &cppOut s << ' ' << cppOutAux; // No default value for containers which can also be passed by pointer. - if (arg.type != GeneratorArgument::Type::Container) + if (arg.type != GeneratorArgument::Type::Container || type.indirections() == 0) writeMinimalConstructorExpression(s, api(), type, isPrimitive, defaultValue); s << ";\n" << typeName << " *" << cppOut << " = &" << cppOutAux; } @@ -3062,7 +2777,7 @@ qsizetype CppGenerator::writePythonToCppTypeConversion(TextStream &s, || 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; @@ -3157,7 +2872,9 @@ 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(); @@ -3167,7 +2884,7 @@ void CppGenerator::writeOverloadedFunctionDecisor(TextStream &s, const OverloadD 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'; } @@ -3184,8 +2901,9 @@ void CppGenerator::writeOverloadedFunctionDecisor(TextStream &s, const OverloadD } 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, @@ -3251,7 +2969,7 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, auto func = referenceFunction; for (const auto &child : children) { const auto defValFunc = child->getFunctionWithDefaultValue(); - if (!defValFunc.isNull()) { + if (defValFunc) { func = defValFunc; break; } @@ -3277,7 +2995,7 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, int sequenceArgCount = 0; while (od && !od->argType().isVarargs()) { const bool typeReplacedByPyObject = od->isTypeModified() - && od->modifiedArgType().name() == cPyObjectT(); + && od->modifiedArgType().name() == cPyObjectT; if (!typeReplacedByPyObject) { if (usePyArgs) pyArgName = pythonArgsAt(od->argPos()); @@ -3285,7 +3003,7 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, auto func = od->referenceFunction(); if (func->isConstructor() && func->arguments().size() == 1) { - const AbstractMetaClass *ownerClass = func->ownerClass(); + AbstractMetaClassCPtr ownerClass = func->ownerClass(); ComplexTypeEntryCPtr baseContainerType = ownerClass->typeEntry()->baseContainerType(); if (baseContainerType && baseContainerType == func->arguments().constFirst().type().typeEntry() && ownerClass->isCopyable()) { @@ -3341,7 +3059,7 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, s << indent << typeChecks.join(u"\n&& "_s) << outdent; } s << ") {\n" << indent; - writeOverloadedFunctionDecisorEngine(s, overloadData, child.data()); + writeOverloadedFunctionDecisorEngine(s, overloadData, child.get()); s << outdent << '}'; } s << '\n'; @@ -3370,12 +3088,16 @@ void CppGenerator::writeFunctionCalls(TextStream &s, const OverloadData &overloa static void writeDeprecationWarning(TextStream &s, const GeneratorContext &context, - const AbstractMetaFunctionCPtr &func) + const AbstractMetaFunctionCPtr &func, + CppGenerator::ErrorReturn errorReturn) { s << "Shiboken::Warnings::warnDeprecated(\""; - if (auto *cls = context.metaClass()) + if (const auto cls = context.metaClass()) s << cls->name() << "\", "; - s << '"' << func->signature().replace(u"::"_s, u"."_s) << "\");\n"; + // 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, @@ -3385,7 +3107,7 @@ void CppGenerator::writeSingleFunctionCall(TextStream &s, ErrorReturn errorReturn) const { if (func->isDeprecated()) - writeDeprecationWarning(s, context, func); + writeDeprecationWarning(s, context, func, errorReturn); if (func->functionType() == AbstractMetaFunction::EmptyFunction) { s << "Shiboken::Errors::setPrivateMethod(\"" @@ -3397,7 +3119,7 @@ void CppGenerator::writeSingleFunctionCall(TextStream &s, 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; @@ -3411,8 +3133,7 @@ void CppGenerator::writeSingleFunctionCall(TextStream &s, const AbstractMetaArgument &arg = func->arguments().at(argIdx); if (arg.isModifiedRemoved()) { if (!arg.defaultValueExpression().isEmpty()) { - const QString cppArgRemoved = CPP_ARG_REMOVED - + QString::number(argIdx); + const QString cppArgRemoved = CPP_ARG_REMOVED(argIdx); s << getFullTypeName(arg.type()) << ' ' << cppArgRemoved; s << " = " << arg.defaultValueExpression() << ";\n" << sbkUnusedVariableCast(cppArgRemoved); @@ -3434,10 +3155,9 @@ void CppGenerator::writeSingleFunctionCall(TextStream &s, continue; auto argType = getArgumentType(func, argIdx); int argPos = argIdx - removedArgs; - QString argName = CPP_ARG + QString::number(argPos); QString pyArgName = usePyArgs ? pythonArgsAt(argPos) : PYTHON_ARG; indirections[argIdx] = - writeArgumentConversion(s, argType, argName, pyArgName, errorReturn, + writeArgumentConversion(s, argType, CPP_ARG_N(argPos), pyArgName, errorReturn, func->implementingClass(), arg.defaultValueExpression(), func->isUserAdded()); } @@ -3446,7 +3166,7 @@ void CppGenerator::writeSingleFunctionCall(TextStream &s, 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, indirections, errorReturn); @@ -3497,9 +3217,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"; } @@ -3537,10 +3258,22 @@ void CppGenerator::writeCppToPythonFunction(TextStream &s, replaceCppToPythonVariables(code, getFullTypeName(ownerType), constRef); writeCppToPythonFunction(s, code, fixedCppTypeName(customConversion->ownerType())); } + +QString CppGenerator::containerNativeToTargetTypeName(const ContainerTypeEntryCPtr &type) +{ + QString result = type->targetLangApiName(); + if (result != cPyObjectT) { + result = containerCpythonBaseName(type); + if (result == cPySequenceT) + result = cPyListT; + } + return result; +} + void CppGenerator::writeCppToPythonFunction(TextStream &s, const AbstractMetaType &containerType) const { Q_ASSERT(containerType.typeEntry()->isContainer()); - auto cte = qSharedPointerCast<const ContainerTypeEntry>(containerType.typeEntry()); + 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 '" @@ -3558,16 +3291,18 @@ void CppGenerator::writeCppToPythonFunction(TextStream &s, const AbstractMetaTyp 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"; } @@ -3589,7 +3324,7 @@ void CppGenerator::writeIsPythonConvertibleToCppFunction(TextStream &s, << "return Shiboken::Conversions::nonePythonToCppNullPtr;\n" << outdent; } else { if (!condition.contains(u"pyIn")) - s << sbkUnusedVariableCast(u"pyIn"_s); + s << sbkUnusedVariableCast("pyIn"); } s << "if (" << condition << ")\n" << indent << "return " << pythonToCppFuncName << ";\n" << outdent @@ -3656,8 +3391,6 @@ void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, QString pyTypeName = toNative.sourceTypeName(); if (pyTypeName == u"Py_None" || pyTypeName == u"PyNone") typeCheck = u"%in == Py_None"_s; - else if (pyTypeName == u"SbkEnumType") - typeCheck = u"Shiboken::isShibokenEnum(%in)"_s; else if (pyTypeName == u"SbkObject") typeCheck = u"Shiboken::Object::checkType(%in)"_s; } @@ -3672,29 +3405,26 @@ void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, + cpythonTypeNameExt(toNative.sourceType()) + u')'; } typeCheck.replace(u"%in"_s, u"pyIn"_s); - processCodeSnip(typeCheck); + processCodeSnip(typeCheck, targetType->qualifiedCppName()); writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, typeCheck); } void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, const AbstractMetaType &containerType) const { Q_ASSERT(containerType.typeEntry()->isContainer()); - auto cte = qSharedPointerCast<const ContainerTypeEntry>(containerType.typeEntry()); - if (!cte->hasCustomConversion()) { - //qFatal - return; - } - + const auto cte = std::static_pointer_cast<const ContainerTypeEntry>(containerType.typeEntry()); const auto customConversion = cte->customConversion(); - const TargetToNativeConversions &toCppConversions = - customConversion->targetToNativeConversions(); - if (toCppConversions.isEmpty()) { - //qFatal - return; - } + 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(); + QString code = conv.conversion(); const QString line = u"auto &cppOutRef = *reinterpret_cast<"_s + cppTypeName + u" *>(cppOut);"_s; CodeSnipAbstract::prependCode(&code, line); @@ -3722,7 +3452,8 @@ void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, const Abst 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); @@ -3730,7 +3461,7 @@ void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, const Abst typeCheck = u"false"_s; else typeCheck = typeCheck + u"pyIn)"_s; - writeIsPythonConvertibleToCppFunction(s, typeName, typeName, typeCheck); + writeIsPythonConvertibleToCppFunction(s, sourceTypeName, typeName, typeCheck); s << '\n'; } @@ -3771,17 +3502,20 @@ static bool forceQObjectNamedArguments(const AbstractMetaFunctionCPtr &func) { if (func->functionType() != AbstractMetaFunction::ConstructorFunction) return false; - auto *owner = func->ownerClass(); + const auto owner = func->ownerClass(); Q_ASSERT(owner); - if (!owner->isQObject()) + 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) const +void CppGenerator::writeNamedArgumentResolution(TextStream &s, + const AbstractMetaFunctionCPtr &func, + bool usePyArgs, + const OverloadData &overloadData, + ErrorReturn errorReturn) { const AbstractMetaArgumentList &args = OverloadData::getArgumentsWithDefaultValues(func); const bool hasDefaultArguments = !args.isEmpty(); @@ -3790,10 +3524,10 @@ void CppGenerator::writeNamedArgumentResolution(TextStream &s, const AbstractMet 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" << indent + s << "if (kwds != nullptr && PyDict_Size(kwds) > 0) {\n" << indent << "errInfo.reset(kwds);\n" << "Py_INCREF(errInfo.object());\n" - << "goto " << cpythonFunctionName(func) << "_TypeError;\n" + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" << outdent << "}\n"; } return; @@ -3812,19 +3546,19 @@ void CppGenerator::writeNamedArgumentResolution(TextStream &s, const AbstractMet QString pyKeyName = u"key_"_s + arg.name(); s << "static PyObject *const " << pyKeyName << " = Shiboken::String::createStaticString(\"" << arg.name() << "\");\n" - << "if (PyDict_Contains(kwds, " << pyKeyName << ")) {\n" << indent + << "if (PyDict_Contains(kwds, " << pyKeyName << ") != 0) {\n" << indent << "value = PyDict_GetItem(kwds, " << pyKeyName << ");\n" - << "if (value && " << pyArgName << ") {\n" << indent - << "errInfo.reset(" << pyKeyName << ");\n" + << "if (value != nullptr && " << pyArgName << " != nullptr ) {\n" + << indent << "errInfo.reset(" << pyKeyName << ");\n" << "Py_INCREF(errInfo.object());\n" - << "goto " << cpythonFunctionName(func) << "_TypeError;\n" - << outdent << "}\nif (value) {\n" << indent + << "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 - << "goto " << cpythonFunctionName(func) << "_TypeError;\n" << outdent - << outdent + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" + << outdent << outdent << "}\nPyDict_DelItem(kwds_dup, " << pyKeyName << ");\n" << outdent << "}\n"; } @@ -3834,8 +3568,8 @@ void CppGenerator::writeNamedArgumentResolution(TextStream &s, const AbstractMet // 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() && func->ownerClass()->isQObject())) - s << "goto " << cpythonFunctionName(func) << "_TypeError;\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" @@ -3847,7 +3581,7 @@ QString CppGenerator::argumentNameFromIndex(const ApiExtractorResult &api, { switch (argIndex) { case -1: - return u"self"_s; + return PYTHON_SELF_VAR; case 0: return PYTHON_RETURN_VAR; case 1: { // Single argument? @@ -3860,7 +3594,7 @@ QString CppGenerator::argumentNameFromIndex(const ApiExtractorResult &api, return pythonArgsAt(argIndex - 1); } -const AbstractMetaClass * +AbstractMetaClassCPtr CppGenerator::argumentClassFromIndex(const ApiExtractorResult &api, const AbstractMetaFunctionCPtr &func, int argIndex) { @@ -3885,7 +3619,7 @@ CppGenerator::argumentClassFromIndex(const ApiExtractorResult &api, auto te = type.typeEntry(); if (type.isVoid() || !te->isComplex()) throw Exception(msgInvalidArgumentModification(func, argIndex)); - auto *result = AbstractMetaClass::findClass(api.classes(), te); + const auto result = AbstractMetaClass::findClass(api.classes(), te); if (!result) throw Exception(msgClassNotFound(te)); return result; @@ -3911,6 +3645,11 @@ 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, @@ -3984,7 +3723,7 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr if (hasConversionRule) userArgs << arg.name() + CONV_RULE_OUT_VAR_SUFFIX; else if (!arg.defaultValueExpression().isEmpty()) - userArgs.append(CPP_ARG_REMOVED + QString::number(i)); + userArgs.append(CPP_ARG_REMOVED(i)); } else { if (hasConversionRule) { userArgs.append(arg.name() + CONV_RULE_OUT_VAR_SUFFIX); @@ -3992,14 +3731,16 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr const int idx = arg.argumentIndex() - removedArgs; const auto deRef = argumentIndirections.at(i); QString argName = AbstractMetaType::dereferencePrefix(deRef) - + CPP_ARG + QString::number(idx); + + CPP_ARG_N(idx); userArgs.append(argName); } } // "Pass unique ptr by value" pattern: Apply std::move() auto type = arg.type(); - if (type.isUniquePointer() && type.passByValue()) + 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 @@ -4021,7 +3762,7 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr if (hasConversionRule) otherArgs.prepend(arg.name() + CONV_RULE_OUT_VAR_SUFFIX); else - otherArgs.prepend(CPP_ARG_REMOVED + QString::number(i)); + otherArgs.prepend(CPP_ARG_REMOVED(i)); } if (otherArgsModified) userArgs << otherArgs; @@ -4069,21 +3810,21 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr Q_ASSERT(owner == context.metaClass()); if (func->functionType() == AbstractMetaFunction::CopyConstructorFunction && maxArgs == 1) { - mc << "new ::" << context.effectiveClassName() + mc << "new " << globalScopePrefix(context) << context.effectiveClassName() << "(*" << CPP_ARG0 << ')'; } else { const QString ctorCall = context.effectiveClassName() + u'(' + userArgs.join(u", "_s) + u')'; - if (usePySideExtensions() && owner->isQObject()) { + if (usePySideExtensions() && isQObject(owner)) { s << "void *addr = PySide::nextQObjectMemoryAddr();\n"; - uva << "if (addr) {\n" << indent - << "cptr = new (addr) ::" << ctorCall << ";\n" - << "PySide::setNextQObjectMemoryAddr(nullptr);\n" << outdent + uva << "if (addr != nullptr) {\n" << indent + << "cptr = new (addr) " << globalScopePrefix(context) << ctorCall + << ";\nPySide::setNextQObjectMemoryAddr(nullptr);\n" << outdent << "} else {\n" << indent - << "cptr = new ::" << ctorCall << ";\n" + << "cptr = new " << globalScopePrefix(context) << ctorCall << ";\n" << outdent << "}\n"; } else { - mc << "new ::" << ctorCall; + mc << "new " << globalScopePrefix(context) << ctorCall; } } } else { @@ -4097,7 +3838,7 @@ 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() @@ -4106,7 +3847,7 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr + 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() @@ -4121,7 +3862,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 { @@ -4137,13 +3878,13 @@ 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()) : - u"::"_s - + methodCallClassName) << "::"; + m_gsp + methodCallClassName) << "::"; mc << func->originalName() << "_protected"; } } else { @@ -4294,8 +4035,8 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr s << "Shiboken::Object::"; if (ownership == TypeSystem::TargetLangOwnership) { s << "getOwnership(" << pyArgName << ");"; - } else if (auto *ac = argumentClassFromIndex(api(), func, argIndex); - ac->hasVirtualDestructor()) { + } else if (auto ac = argumentClassFromIndex(api(), func, argIndex); + ac && ac->hasVirtualDestructor()) { s << "releaseOwnership(" << pyArgName << ");"; } else { s << "invalidate(" << pyArgName << ");"; @@ -4340,12 +4081,12 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr s << propagateException; } -QStringList CppGenerator::getAncestorMultipleInheritance(const AbstractMetaClass *metaClass) +QStringList CppGenerator::getAncestorMultipleInheritance(const AbstractMetaClassCPtr &metaClass) { QStringList result; 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"; @@ -4358,50 +4099,55 @@ 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 (qsizetype 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" << indent - << "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"; + << "{\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 << "offsets.insert(int(" << ancestor << "));\n"; - - s << "\noffsets.erase(0);\n\n" - << "std::copy(offsets.cbegin(), offsets.cend(), mi_offsets);\n" << outdent + 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 AbstractMetaClass *metaClass) +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 auto &allAncestors = metaClass->allTypeSystemAncestors(); - for (const AbstractMetaClass *baseClass : allAncestors) { + for (const auto &baseClass : allAncestors) { if (!firstClass) s << "else "; s << "if (desiredType == " << cpythonTypeNameExt(baseClass->typeEntry()) << ")\n" << indent - << "return static_cast< ::" << baseClass->qualifiedCppName() << " *>(me);\n" + << "return static_cast< " << getFullTypeName(baseClass) << " *>(me);\n" << outdent; firstClass = false; } @@ -4417,79 +4163,28 @@ void CppGenerator::writePrimitiveConverterInitialization(TextStream &s, << converter << " = Shiboken::Conversions::createConverter("; if (!type->hasTargetLangApiType()) s << "nullptr"; - else if (type->targetLangApiName() == cPyObjectT()) + 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); } -static void registerEnumConverterScopes(TextStream &s, QString signature) +static void registerConverterInScopes(TextStream &s, QStringView signature, + QAnyStringView varName = converterVar) { while (true) { - s << "Shiboken::Conversions::registerConverterName(converter, \"" - << signature << "\");\n"; - const int qualifierPos = signature.indexOf(u"::"); - if (qualifierPos != -1) - signature.remove(0, qualifierPos + 2); - else + s << registerConverterName(signature, varName); + const auto qualifierPos = signature.indexOf("::"_L1); + if (qualifierPos == -1) break; + signature = signature.sliced(qualifierPos + 2); } } -void CppGenerator::writeFlagsConverterInitialization(TextStream &s, - const FlagsTypeEntryCPtr &flags) -{ - static const char enumPythonVar[] = "FType"; - - const QString qualifiedCppName = flags->qualifiedCppName(); - s << "// Register converter for flag '" << qualifiedCppName << "'.\n{\n" - << indent; - QString typeName = fixedCppTypeName(flags); - s << "SbkConverter *converter = Shiboken::Conversions::createConverter(" - << enumPythonVar << ',' << '\n' << indent - << cppToPythonFunctionName(typeName, typeName) << ");\n" << outdent; - - const QString enumTypeName = fixedCppTypeName(flags->originator()); - QString toCpp = pythonToCppFunctionName(enumTypeName, typeName); - QString isConv = convertibleToCppFunctionName(enumTypeName, typeName); - writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv); - toCpp = pythonToCppFunctionName(typeName, typeName); - isConv = convertibleToCppFunctionName(typeName, typeName); - writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv); - toCpp = pythonToCppFunctionName(u"number"_s, typeName); - isConv = convertibleToCppFunctionName(u"number"_s, typeName); - writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv); - s << "Shiboken::Enum::setTypeConverter(" << enumPythonVar - << ", converter, true);\n"; - // Replace "QFlags<Class::Option>" by "Class::Options" - QString signature = qualifiedCppName; - if (qualifiedCppName.startsWith(u"QFlags<") && qualifiedCppName.endsWith(u'>')) { - signature.chop(1); - signature.remove(0, 7); - const int lastQualifierPos = signature.lastIndexOf(u"::"); - if (lastQualifierPos != -1) { - signature.replace(lastQualifierPos + 2, signature.size() - lastQualifierPos - 2, - flags->flagsName()); - } else { - signature = flags->flagsName(); - } - } - - registerEnumConverterScopes(s, signature); - - // PYSIDE-1673: Also register "QFlags<Class::Option>" purely for - // the purpose of finding the converter by QVariant::typeName() - // in the QVariant conversion code. - s << "Shiboken::Conversions::registerConverterName(converter, \"" - << flags->name() << "\");\n" - << outdent << "}\n"; -} - void CppGenerator::writeEnumConverterInitialization(TextStream &s, const AbstractMetaEnum &metaEnum) { if (metaEnum.isPrivate() || metaEnum.isAnonymous()) @@ -4511,81 +4206,71 @@ void CppGenerator::writeEnumConverterInitialization(TextStream &s, const Abstrac const QString isConv = convertibleToCppFunctionName(typeName, typeName); writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv); s << "Shiboken::Enum::setTypeConverter(" << enumPythonVar - << ", converter, false);\n"; + << ", converter);\n"; - registerEnumConverterScopes(s, enumType->qualifiedCppName()); + registerConverterInScopes(s, enumType->qualifiedCppName()); + if (auto flags = enumType->flags()) + s << "// Register converter for flag '" << flags->qualifiedCppName() << "'.\n" + << registerConverterName(flags->name()) // QMetaType + << registerConverterName(flags->originalName()); // Signals with flags s << outdent << "}\n"; - - if (auto flags = enumType->flags(); !flags.isNull()) - writeFlagsConverterInitialization(s, flags); } -QString 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"; - } - 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"; + s << '&' << targetTypeName << "_Type"; } - const QString converterObj = converterObject(type); - writeAddPythonToCppConversion(s, converterObj, toCpp, isConv); - return converterObj; -} -void CppGenerator::writeSmartPointerConverterInitialization(TextStream &s, const AbstractMetaType &type) const -{ - const QByteArray cppSignature = type.cppSignature().toUtf8(); - auto writeConversionRegister = [&s](const AbstractMetaType &sourceType, const QString &targetTypeName, const QString &targetConverter) - { - const QString sourceTypeName = fixedCppTypeName(sourceType); - const QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); - const QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName); - - writeAddPythonToCppConversion(s, targetConverter, toCpp, isConv); - }; - - const auto classes = findSmartPointeeBaseClasses(api(), type); - if (classes.isEmpty()) - return; + const QString typeName = fixedCppTypeName(type); + s << ", " << cppToPythonFunctionName(typeName, targetTypeName) << ");\n"; - auto smartPointerTypeEntry = qSharedPointerCast<const SmartPointerTypeEntry>(type.typeEntry()); + 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); + } - s << "// Register SmartPointer converter for type '" << cppSignature << "'." << '\n' - << "///////////////////////////////////////////////////////////////////////////////////////\n\n"; + 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); + } - for (auto *base : classes) { - auto baseTe = base->typeEntry(); - if (auto opt = findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) { - const auto smartTargetType = opt.value(); - s << "// Convert to SmartPointer derived class: [" - << smartTargetType.cppSignature() << "]\n"; - const QString converter = u"Shiboken::Conversions::getConverter(\""_s - + smartTargetType.cppSignature() + u"\")"_s; - writeConversionRegister(type, fixedCppTypeName(smartTargetType), converter); - } else { - s << "// Class not found:" << type.instantiations().at(0).cppSignature(); + 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; +} + +QString CppGenerator::typeInitStruct(const TypeEntryCPtr &te) +{ + return cppApiVariableName(te->targetLangPackage()) + u'[' + + getTypeIndexVariableName(te) + u']'; } void CppGenerator::writeExtendedConverterInitialization(TextStream &s, @@ -4594,23 +4279,23 @@ void CppGenerator::writeExtendedConverterInitialization(TextStream &s, { s << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName() << ".\n"; - for (const AbstractMetaClass *sourceClass : conversions) { - const QString converterVar = cppApiVariableName(externalType->targetLangPackage()) + u'[' - + 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()) + 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)) @@ -4620,7 +4305,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() @@ -4629,7 +4314,7 @@ 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)) @@ -4640,7 +4325,7 @@ bool CppGenerator::supportsSequenceProtocol(const AbstractMetaClass *metaClass) 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()) @@ -4658,29 +4343,24 @@ 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_init; @@ -4689,7 +4369,6 @@ void CppGenerator::writeClassDefinition(TextStream &s, QString tp_hash; QString tp_call; const QString className = chopType(cpythonTypeName(metaClass)); - QString baseClassName; AbstractMetaFunctionCList ctors; const auto &allCtors = metaClass->queryFunctions(FunctionQueryOption::AnyConstructor); for (const auto &f : allCtors) { @@ -4699,13 +4378,10 @@ void CppGenerator::writeClassDefinition(TextStream &s, } } - if (!metaClass->baseClass()) - baseClassName = u"SbkObject_TypeF()"_s; - bool onlyPrivCtor = !metaClass->hasNonPrivateConstructor(); const bool isQApp = usePySideExtensions() - && metaClass->inheritsFrom(u"QCoreApplication"_s); + && inheritsFrom(metaClass, u"QCoreApplication"_s); QString tp_flags = u"Py_TPFLAGS_DEFAULT"_s; if (!metaClass->attributes().testFlag(AbstractMetaClass::FinalCppClass)) @@ -4757,25 +4433,31 @@ void CppGenerator::writeClassDefinition(TextStream &s, 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); @@ -4789,8 +4471,8 @@ void CppGenerator::writeClassDefinition(TextStream &s, if (hasHashFunction(metaClass)) tp_hash = u'&' + cpythonBaseName(metaClass) + u"_HashFunc"_s; - const auto callOp = metaClass->findFunction(u"operator()"); - if (!callOp.isNull() && !callOp->isModifiedRemoved()) + const auto callOp = metaClass->findFunction("operator()"); + if (callOp && !callOp->isModifiedRemoved()) tp_call = u'&' + cpythonFunctionName(callOp); const QString typePtr = u"_"_s + className @@ -4801,7 +4483,7 @@ void CppGenerator::writeClassDefinition(TextStream &s, << "}\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(u"__str__"_s)) @@ -4825,7 +4507,6 @@ 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); } @@ -4840,12 +4521,12 @@ void CppGenerator::writeClassDefinition(TextStream &s, } 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); @@ -4862,14 +4543,14 @@ void CppGenerator::writeMappingMethods(TextStream &s, } 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); @@ -4893,25 +4574,25 @@ void CppGenerator::writeSequenceMethods(TextStream &s, static const QHash<QString, QString> &sqFuncs() { static const QHash<QString, QString> result = { - {u"__concat__"_s, u"sq_concat"_s}, - {u"__contains__"_s, u"sq_contains"_s}, - {u"__getitem__"_s, u"sq_item"_s}, - {u"__getslice__"_s, u"sq_slice"_s}, - {u"__len__"_s, u"sq_length"_s}, - {u"__setitem__"_s, u"sq_ass_item"_s}, - {u"__setslice__"_s, u"sq_ass_slice"_s} + {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()) { + if (func) { funcs.insert(seq.name, u'&' + cpythonFunctionName(func)); hasFunctions = true; } @@ -4929,38 +4610,34 @@ void CppGenerator::writeTypeAsSequenceDefinition(TextStream &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{ - {u"__mlen__"_s, u"mp_length"_s}, - {u"__mgetitem__"_s, u"mp_subscript"_s}, - {u"__msetitem__"_s, u"mp_ass_subscript"_s}, + {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()) { + if (func) { const QString entry = u"reinterpret_cast<void *>(&"_s + cpythonFunctionName(func) + u')'; funcs.insert(m.name, entry); - } else { - funcs.insert(m.name, 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()); } } @@ -4968,105 +4645,102 @@ void CppGenerator::writeTypeAsMappingDefinition(TextStream &s, static const QHash<QString, QString> &nbFuncs() { static const QHash<QString, QString> result = { - {u"__add__"_s, u"nb_add"_s}, - {u"__sub__"_s, u"nb_subtract"_s}, - {u"__mul__"_s, u"nb_multiply"_s}, - {u"__div__"_s, u"nb_divide"_s}, - {u"__mod__"_s, u"nb_remainder"_s}, - {u"__neg__"_s, u"nb_negative"_s}, - {u"__pos__"_s, u"nb_positive"_s}, - {u"__invert__"_s, u"nb_invert"_s}, - {u"__lshift__"_s, u"nb_lshift"_s}, - {u"__rshift__"_s, u"nb_rshift"_s}, - {u"__and__"_s, u"nb_and"_s}, - {u"__xor__"_s, u"nb_xor"_s}, - {u"__or__"_s, u"nb_or"_s}, - {u"__iadd__"_s, u"nb_inplace_add"_s}, - {u"__isub__"_s, u"nb_inplace_subtract"_s}, - {u"__imul__"_s, u"nb_inplace_multiply"_s}, - {u"__idiv__"_s, u"nb_inplace_divide"_s}, - {u"__imod__"_s, u"nb_inplace_remainder"_s}, - {u"__ilshift__"_s, u"nb_inplace_lshift"_s}, - {u"__irshift__"_s, u"nb_inplace_rshift"_s}, - {u"__iand__"_s, u"nb_inplace_and"_s}, - {u"__ixor__"_s, u"nb_inplace_xor"_s}, - {u"__ior__"_s, u"nb_inplace_or"_s}, - {boolT(), u"nb_nonzero"_s} + {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 + u"___nb_bool"_s); + 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 == u"__div__" || nbName == u"__idiv__") - continue; // excludeFromPy3K const auto nbIt = nb.constFind(nbName); - if (nbIt != nb.constEnd()) { - const QString fixednbName = nbName == boolT() - ? u"nb_bool"_s : it.value(); - s << "{Py_" << fixednbName << ", reinterpret_cast<void *>(" - << nbIt.value() << ")},\n"; - } - } - - auto nbIt = nb.constFind(u"__div__"_s); - if (nbIt != nb.constEnd()) - s << "{Py_nb_true_divide, reinterpret_cast<void *>(" << nbIt.value() << ")},\n"; - - nbIt = nb.constFind(u"__idiv__"_s); - 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; + 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()) @@ -5079,16 +4753,18 @@ void CppGenerator::writeCopyFunction(TextStream &s, const GeneratorContext &cont 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); @@ -5102,7 +4778,7 @@ QString CppGenerator::cppFieldAccess(const AbstractMetaField &metaField, void CppGenerator::writeGetterFunction(TextStream &s, const AbstractMetaField &metaField, - const GeneratorContext &context) const + const GeneratorContext &context) { writeGetterFunctionStart(s, cpythonGetterFunctionName(metaField)); @@ -5144,7 +4820,7 @@ void CppGenerator::writeGetterFunction(TextStream &s, << "pyOut = reinterpret_cast<PyObject *>(Shiboken::Object::findColocatedChild(" << "reinterpret_cast<SbkObject *>(self), " << cpythonTypeNameExt(fieldType) << "));\n" - << "if (pyOut) {\n" << indent + << "if (pyOut != nullptr) {\n" << indent << "Py_IncRef(pyOut);\n" << "return pyOut;\n" << outdent << "}\n"; @@ -5156,7 +4832,14 @@ void CppGenerator::writeGetterFunction(TextStream &s, << "Py_IncRef(pyOut);" << "\n" << "return pyOut;" << "\n" << outdent << "}\n"; - // Create and register new wrapper + // 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" @@ -5169,27 +4852,29 @@ 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) { 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" << indent - << "Py_XDECREF(pyResult);\nreturn {};\n" << outdent + 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, ErrorReturn::Zero); @@ -5211,7 +4896,7 @@ void CppGenerator::writeSetterFunctionPreamble(TextStream &s, const QString &nam void CppGenerator::writeSetterFunction(TextStream &s, const AbstractMetaField &metaField, - const GeneratorContext &context) const + const GeneratorContext &context) { const AbstractMetaType &fieldType = metaField.type(); writeSetterFunctionPreamble(s, metaField.name(), cpythonSetterFunctionName(metaField), @@ -5243,16 +4928,18 @@ 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) { - 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" << indent + << "if (" << shibokenErrorsOccurred << ")\n" << indent << "return -1;\n" << outdent << CPP_SELF_VAR << "->" << property.write() << "(cppOut);\n" << "return 0;\n" << outdent << "}\n\n"; @@ -5260,7 +4947,7 @@ void CppGenerator::writeSetterFunction(TextStream &s, const QPropertySpec &prope void CppGenerator::writeRichCompareFunctionHeader(TextStream &s, const QString &baseName, - const GeneratorContext &context) const + const GeneratorContext &context) { s << "static PyObject * "; s << baseName << "_richcompare(PyObject *self, PyObject *" << PYTHON_ARG @@ -5272,13 +4959,10 @@ void CppGenerator::writeRichCompareFunctionHeader(TextStream &s, << sbkUnusedVariableCast(PYTHON_TO_CPP_VAR) << '\n'; } -static const char richCompareComment[] = - "// PYSIDE-74: By default, we redirect to object's tp_richcompare (which is `==`, `!=`).\n"; - void CppGenerator::writeRichCompareFunction(TextStream &s, const GeneratorContext &context) const { - const AbstractMetaClass *metaClass = context.metaClass(); + const auto metaClass = context.metaClass(); QString baseName = cpythonBaseName(metaClass); writeRichCompareFunctionHeader(s, baseName, context); @@ -5363,7 +5047,8 @@ void CppGenerator::writeRichCompareFunction(TextStream &s, << (op == AbstractMetaFunction::OperatorEqual ? "Py_False" : "Py_True") << ";\n" << "Py_INCREF(" << PYTHON_RETURN_VAR << ");\n" << outdent; } else { - s << indent << "goto " << baseName << "_RichComparison_TypeError;\n" << outdent; + s << indent << "return Shiboken::returnFromRichCompare(" + << PYTHON_RETURN_VAR << ");\n" << outdent; } s << "}\n\n"; @@ -5372,130 +5057,9 @@ void CppGenerator::writeRichCompareFunction(TextStream &s, s << "default:\n" << indent << richCompareComment << "return FallbackRichCompare(self, " << PYTHON_ARG << ", op);\n" - << "goto " << baseName << "_RichComparison_TypeError;\n" << outdent - << outdent << "}\n\n"; - - writeRichCompareFunctionFooter(s, baseName); -} - -void CppGenerator::writeRichCompareFunctionFooter(TextStream &s, - const QString &baseName) -{ - s << "if (" << PYTHON_RETURN_VAR << " && !PyErr_Occurred())\n" << indent - << "return " << PYTHON_RETURN_VAR << ";\n" << outdent - << baseName << "_RichComparison_TypeError:\n" - << "Shiboken::Errors::setOperatorNotImplemented();\n" - << ErrorReturn::Default << '\n' << outdent << "}\n\n"; -} - -using ComparisonOperatorList = QList<AbstractMetaFunction::ComparisonOperatorType>; - -// Return the available comparison operators for smart pointers -static ComparisonOperatorList smartPointeeComparisons(const GeneratorContext &context) -{ - Q_ASSERT(context.forSmartPointer()); - auto te = context.preciseType().instantiations().constFirst().typeEntry(); - if (isExtendedCppPrimitive(te)) { // Primitive pointee types have all - return {AbstractMetaFunction::OperatorEqual, - AbstractMetaFunction::OperatorNotEqual, - AbstractMetaFunction::OperatorLess, - AbstractMetaFunction::OperatorLessEqual, - AbstractMetaFunction::OperatorGreater, - AbstractMetaFunction::OperatorGreaterEqual}; - } - - auto *pointeeClass = context.pointeeClass(); - if (!pointeeClass) - return {}; - - ComparisonOperatorList result; - const auto &comparisons = - pointeeClass->operatorOverloads(OperatorQueryOption::SymmetricalComparisonOp); - for (const auto &f : comparisons) { - const auto ct = f->comparisonOperatorType().value(); - if (!result.contains(ct)) - result.append(ct); - } - return result; -} - -void CppGenerator::writeSmartPointerRichCompareFunction(TextStream &s, - const GeneratorContext &context) const -{ - static const char selfPointeeVar[] = "cppSelfPointee"; - static const char cppArg0PointeeVar[] = "cppArg0Pointee"; - - const AbstractMetaClass *metaClass = context.metaClass(); - QString baseName = cpythonBaseName(metaClass); - writeRichCompareFunctionHeader(s, baseName, context); - - s << "if ("; - writeTypeCheck(s, context.preciseType(), PYTHON_ARG); - s << ") {\n" << indent; - writeArgumentConversion(s, context.preciseType(), CPP_ARG0, - PYTHON_ARG, ErrorReturn::Default, metaClass); - - const auto te = context.preciseType().typeEntry(); - Q_ASSERT(te->isSmartPointer()); - const auto ste = qSharedPointerCast<const SmartPointerTypeEntry>(te); - - s << "const auto *" << selfPointeeVar << " = " << CPP_SELF_VAR - << '.' << ste->getter() << "();\n"; - s << "const auto *" << cppArg0PointeeVar << " = " << CPP_ARG0 - << '.' << ste->getter() << "();\n"; - - // If we have an object without any comparisons, only generate a simple - // equality check by pointee address - auto availableOps = smartPointeeComparisons(context); - const bool comparePointeeAddressOnly = availableOps.isEmpty(); - if (comparePointeeAddressOnly) { - availableOps << AbstractMetaFunction::OperatorEqual - << AbstractMetaFunction::OperatorNotEqual; - } else { - // For value types with operators, we complain about nullptr - s << "if (" << selfPointeeVar << " == nullptr || " << cppArg0PointeeVar - << " == nullptr) {\n" << indent - << "PyErr_SetString(PyExc_NotImplementedError, \"nullptr passed to comparison.\");\n" - << ErrorReturn::Default << '\n' << outdent << "}\n"; - } - - s << "bool " << CPP_RETURN_VAR << "= false;\n" - << "switch (op) {\n"; - for (auto op : availableOps) { - s << "case " << AbstractMetaFunction::pythonRichCompareOpCode(op) << ":\n" - << indent << CPP_RETURN_VAR << " = "; - if (comparePointeeAddressOnly) { - s << selfPointeeVar << ' ' << AbstractMetaFunction::cppComparisonOperator(op) - << ' ' << cppArg0PointeeVar << ";\n"; - } else { - // Shortcut for equality: Check pointee address - if (op == AbstractMetaFunction::OperatorEqual - || op == AbstractMetaFunction::OperatorLessEqual - || op == AbstractMetaFunction::OperatorGreaterEqual) { - s << selfPointeeVar << " == " << cppArg0PointeeVar << " || "; - } - // Generate object's comparison - s << "*" << selfPointeeVar << ' ' - << AbstractMetaFunction::cppComparisonOperator(op) << " *" - << cppArg0PointeeVar << ";\n"; - } - s << "break;\n" << outdent; - - } - if (availableOps.size() < 6) { - s << "default:\n" << indent - << richCompareComment - << "return FallbackRichCompare(self, " << PYTHON_ARG << ", op);\n" << outdent; - } - s << "}\n" << PYTHON_RETURN_VAR << " = " << CPP_RETURN_VAR - << " ? Py_True : Py_False;\n" - << "Py_INCREF(" << PYTHON_RETURN_VAR << ");\n"; - - s << outdent << "} else {\n" << indent - << "goto " << baseName << "_RichComparison_TypeError;\n" - << outdent << "}\n"; - - writeRichCompareFunctionFooter(s, baseName); + << outdent << outdent << "}\n\n" + << "return Shiboken::returnFromRichCompare(" << PYTHON_RETURN_VAR << ");\n" << outdent + << "}\n\n"; } // Return a flag combination for PyMethodDef @@ -5516,9 +5080,9 @@ QByteArrayList CppGenerator::methodDefinitionParameters(const OverloadData &over } // METH_STATIC causes a crash when used for global functions (also from // invisible namespaces). - auto *ownerClass = overloadData.referenceFunction()->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()) result.append(QByteArrayLiteral("METH_STATIC")); if (overloadData.hasClassMethod()) @@ -5542,6 +5106,17 @@ QList<PyMethodDefEntry> return result; } +QString CppGenerator::pythonSignature(const AbstractMetaType &type) const +{ + if (type.isSmartPointer() && !type.instantiations().isEmpty()) { + const auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(type.typeEntry()); + const auto instantiationTe = type.instantiations().constFirst().typeEntry(); + if (auto opt = api().findSmartPointerInstantiation(ste, instantiationTe)) + return opt->specialized->typeEntry()->qualifiedTargetLangName(); + } + return type.pythonSignature(); +} + // Format the type signature of a function parameter QString CppGenerator::signatureParameter(const AbstractMetaArgument &arg) const { @@ -5553,17 +5128,22 @@ QString CppGenerator::signatureParameter(const AbstractMetaArgument &arg) const metaType = *viewOn; s << arg.name() << ':'; - QStringList signatures(metaType.pythonSignature()); + QStringList signatures(pythonSignature(metaType)); // Implicit conversions (C++): Check for converting constructors // "QColor(Qt::GlobalColor)" or conversion operators const AbstractMetaFunctionCList conversions = api().implicitConversions(metaType); for (const auto &f : conversions) { - if (f->isConstructor() && !f->arguments().isEmpty()) - 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 << pythonSignature(f->arguments().constFirst().type()); + } + } else if (f->isConversionOperator()) { signatures << f->ownerClass()->fullName(); + } } const qsizetype size = signatures.size(); @@ -5577,12 +5157,6 @@ QString CppGenerator::signatureParameter(const AbstractMetaArgument &arg) const if (size > 1) s << ']'; - if (!arg.defaultValueExpression().isEmpty()) { - s << '='; - QString e = arg.defaultValueExpression(); - e.replace(u"::"_s, u"."_s); - s << e; - } return result; } @@ -5599,18 +5173,22 @@ 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 << u"self"_s; + args << PYTHON_SELF_VAR; const auto &arguments = f->arguments(); for (qsizetype i = 0, size = arguments.size(); i < size; ++i) { const auto n = i + 1; + const auto &arg = arguments.at(i); if (!f->argumentRemoved(n)) { QString t = f->pyiTypeReplaced(n); if (t.isEmpty()) { - t = signatureParameter(arguments.at(i)); + t = signatureParameter(arg); } else { t.prepend(u':'); - t.prepend(arguments.at(i).name()); + t.prepend(arg.name()); } + QString defaultValue = arg.defaultValueExpression(); + if (!defaultValue.isEmpty()) + t += u'=' + defaultValue.replace(u"::"_s, u"."_s); args.append(t); } } @@ -5622,7 +5200,7 @@ void CppGenerator::writeSignatureInfo(TextStream &s, const OverloadData &overloa QString returnType = f->pyiTypeReplaced(0); // pyi or modified type if (returnType.isEmpty() && !f->isVoid()) - returnType = f->type().pythonSignature(); + returnType = pythonSignature(f->type()); if (!returnType.isEmpty()) s << "->" << returnType; @@ -5630,43 +5208,44 @@ void CppGenerator::writeSignatureInfo(TextStream &s, const OverloadData &overloa } } -void CppGenerator::writeEnumsInitialization(TextStream &s, AbstractMetaEnumList &enums, - ErrorReturn errorReturn) const +void CppGenerator::writeEnumsInitialization(TextStream &s, AbstractMetaEnumList &enums) { if (enums.isEmpty()) return; - bool preambleWrittenE = false; - bool preambleWrittenF = false; + bool preambleWritten = false; + bool etypeUsed = false; + for (const AbstractMetaEnum &cppEnum : std::as_const(enums)) { if (cppEnum.isPrivate()) continue; - if (!preambleWrittenE) { + if (!preambleWritten) { s << "// Initialization of enums.\n" + << "Shiboken::AutoDecRef tpDict{};\n" << "PyTypeObject *EType{};\n\n"; - preambleWrittenE = true; + preambleWritten = true; } - if (!preambleWrittenF && cppEnum.typeEntry()->flags()) { - s << "// Initialization of enums, flags part.\n" - << "PyTypeObject *FType{};\n\n"; - preambleWrittenF = true; - } - writeEnumInitialization(s, cppEnum, errorReturn); + 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 == u"None" || name == u"False" || name == u"True") - name += u'_'; - 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, - ErrorReturn errorReturn) const +bool CppGenerator::writeEnumInitialization(TextStream &s, const AbstractMetaEnum &cppEnum) { - const AbstractMetaClass *enclosingClass = cppEnum.targetLangEnclosingClass(); - bool hasUpperEnclosingClass = enclosingClass - && enclosingClass->targetLangEnclosingClass() != nullptr; + const auto enclosingClass = cppEnum.targetLangEnclosingClass(); + const bool hasUpperEnclosingClass = enclosingClass + && enclosingClass->targetLangEnclosingClass(); EnumTypeEntryCPtr enumTypeEntry = cppEnum.typeEntry(); QString enclosingObjectVariable; if (enclosingClass) @@ -5680,96 +5259,115 @@ void CppGenerator::writeEnumInitialization(TextStream &s, const AbstractMetaEnum s << (cppEnum.isAnonymous() ? "anonymous enum identified by enum value" : "enum"); s << " '" << cppEnum.name() << "'.\n"; - QString enumVarTypeObj = cpythonTypeNameExt(enumTypeEntry); - if (!cppEnum.isAnonymous()) { - int packageLevel = packageName().count(u'.') + 1; - FlagsTypeEntryPtr flags = enumTypeEntry->flags(); - if (!flags.isNull()) { - // 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(u'.') + 1); - s << "FType = PySide::QFlags::create(\"" - << packageLevel << ':' << fullPath << flags->flagsName() << "\", \n" << indent - << cpythonEnumName(cppEnum) << "_number_slots);\n" << outdent - << cpythonTypeNameExt(flags) << " = FType;\n"; + const QString userType = cppEnum.typeEntry()->cppType(); + const bool isSigned = cppEnum.isSigned() && !userType.contains(u"unsigned"_s); + const bool isAccessible = !avoidProtectedHack() || !cppEnum.isProtected(); + const auto enumValues = cppEnum.nonRejectedValues(); + + const QString prefix = cppEnum.name(); + + const QString intType = userType.isEmpty() ? cppEnum.underlyingType() : userType; + + // Create a list of values + const QString initializerValues = prefix + u"_InitializerValues"_s; + const QString initializerName = prefix + u"_Initializer"_s; + + // Build maybe array of enum names. + if (cppEnum.enumKind() != AnonymousEnum) { + s << "const char *" << initializerName << "[] = {\n" << indent; + for (const auto &enumValue : enumValues) { + QString name = mangleName(enumValue.name()); + s << '\"' << name << "\",\n"; } + s << "nullptr};\n" << outdent; + } - s << "EType = Shiboken::Enum::" - << ((enclosingClass - || hasUpperEnclosingClass) ? "createScopedEnum" : "createGlobalEnum") - << '(' << enclosingObjectVariable << ',' << '\n' << indent - << '"' << cppEnum.name() << "\",\n" - << '"' << packageLevel << ':' << getClassTargetFullName(cppEnum) << "\",\n" - << '"' << cppEnum.qualifiedCppName() << '"'; - if (flags) - s << ",\nFType"; - s << ");\n" << outdent - << "if (!EType)\n" - << indent << errorReturn << outdent << '\n'; - } - - 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 = cppEnum.typeEntry()->cppType(); - if (enumValueText.isEmpty()) - enumValueText = u"Shiboken::Enum::EnumValueType"_s; - enumValueText += u'('; - if (cppEnum.enclosingClass()) - enumValueText += cppEnum.enclosingClass()->qualifiedCppName() + u"::"_s; - // Fully qualify the value which is required for C++ 11 enum classes. - if (!cppEnum.isAnonymous()) - enumValueText += cppEnum.name() + u"::"_s; - enumValueText += enumValue.name(); - enumValueText += u')'; - } else { - enumValueText += enumValue.value().toString(); + if (usedIntType != intType) + s << "// \"" << usedIntType << "\" used instead of \"" << intType << "\"\n"; + + // Calculating formatting columns + QString enumValuePrefix; + if (isAccessible) { + if (cppEnum.enclosingClass()) + enumValuePrefix += cppEnum.enclosingClass()->qualifiedCppName() + u"::"_s; + if (!cppEnum.isAnonymous()) + enumValuePrefix += cppEnum.name() + u"::"_s; + } + + // Build array of enum values + if (enumValues.isEmpty()) { + s << "const " << usedIntType << " *" << initializerValues << "{};\n"; + } else { + QStringList values; + values.reserve(enumValues.size()); + s << "constexpr " << usedIntType << ' ' << initializerValues << "[] = {\n" << indent; + for (qsizetype idx = 0, last = enumValues.size() - 1; idx <= last; ++idx) { + const auto &enumValue = enumValues.at(idx); + QString line = usedIntType + u'(' + (isAccessible + ? enumValuePrefix + enumValue.name() + : enumValue.value().toString()) + u')'; + if (idx != last) + line += u','; + values.append(line); } - const QString mangledName = mangleName(enumValue.name()); - switch (cppEnum.enumKind()) { - case AnonymousEnum: + 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" << indent - << "PyObject *anonEnumItem = PyLong_FromLong(" << enumValueText << ");\n" - << "if (PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(" - << enclosingObjectVariable - << ")->tp_dict, \"" << mangledName << "\", anonEnumItem) < 0)\n" - << indent << errorReturn << outdent - << "Py_DECREF(anonEnumItem);\n" << outdent - << "}\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" << indent << errorReturn << outdent; + 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 << '(' << "EType" << ',' << '\n' << indent - << enclosingObjectVariable << ", \"" << mangledName << "\", " - << enumValueText << "))\n" << errorReturn << outdent; - break; - case EnumClass: - s << "if (!Shiboken::Enum::createScopedEnumItem(" - << "EType" << ",\n" << indent - << "EType" << ", \"" << mangledName << "\", " - << enumValueText << "))\n" << errorReturn << outdent; - break; } } - s << "// PYSIDE-1735: Resolving the whole enum class at the end for API compatibility.\n" - << "EType = morphLastEnumToPython();\n" - << enumVarTypeObj << " = EType;\n"; + + 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" - << cpythonTypeNameExt(cppEnum.typeEntry()->flags()) << " =\n" - << indent << "mapFlagsToSameEnum(FType, EType);\n" << outdent; + << cpythonTypeNameExtSet(cppEnum.typeEntry()->flags()) << " =\n" + << indent << "EType;\n" << outdent; } writeEnumConverterInitialization(s, cppEnum); @@ -5777,9 +5375,11 @@ void CppGenerator::writeEnumInitialization(TextStream &s, const AbstractMetaEnum 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(); @@ -5801,136 +5401,11 @@ void CppGenerator::writeSignalInitialization(TextStream &s, const AbstractMetaCl } } - s << "PySide::Signal::registerSignals(pyType, &::" + s << "PySide::Signal::registerSignals(pyType, &" << m_gsp << metaClass->qualifiedCppName() << "::staticMetaObject);\n"; } -void CppGenerator::writeFlagsToLong(TextStream &s, const AbstractMetaEnum &cppEnum) -{ - FlagsTypeEntryPtr 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) -{ - FlagsTypeEntryPtr flagsEntry = cppEnum.typeEntry()->flags(); - if (flagsEntry.isNull()) - 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, u"and"_s, u"&"_s); - writeFlagsBinaryOperator(s, cppEnum, u"or"_s, u"|"_s); - writeFlagsBinaryOperator(s, cppEnum, u"xor"_s, u"^"_s); - - writeFlagsUnaryOperator(s, cppEnum, u"invert"_s, u"~"_s); - 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) -{ - FlagsTypeEntryPtr flagsEntry = cppEnum.typeEntry()->flags(); - Q_ASSERT(!flagsEntry.isNull()); - - 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, u"cppResult"_s); - s << ";\n" << outdent << "}\n\n"; -} - -void CppGenerator::writeFlagsUnaryOperator(TextStream &s, const AbstractMetaEnum &cppEnum, - const QString &pyOpName, - const QString &cppOpName, bool boolResult) -{ - FlagsTypeEntryPtr flagsEntry = cppEnum.typeEntry()->flags(); - Q_ASSERT(flagsEntry); - - s << "PyObject *" << cpythonEnumName(cppEnum) << "___" << pyOpName - << "__(PyObject *self, PyObject *" << PYTHON_ARG << ")\n{\n" << indent; - if (cppOpName == u"~") - s << sbkUnusedVariableCast(PYTHON_ARG); - - 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, u"cppResult"_s); - 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. @@ -5941,7 +5416,8 @@ QString CppGenerator::getSimpleClassInitFunctionName(const AbstractMetaClass *me return initFunctionName; } -QString CppGenerator::getSimpleClassStaticFieldsInitFunctionName(const AbstractMetaClass *metaClass) +QString + CppGenerator::getSimpleClassStaticFieldsInitFunctionName(const AbstractMetaClassCPtr &metaClass) { return u"init_"_s + getSimpleClassInitFunctionName(metaClass) + u"StaticFields"_s; @@ -5972,8 +5448,8 @@ 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 {}; @@ -5990,14 +5466,69 @@ 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 { ComplexTypeEntryCPtr classTypeEntry = metaClass->typeEntry(); - const AbstractMetaClass *enc = metaClass->targetLangEnclosingClass(); + AbstractMetaClassCPtr enc = metaClass->targetLangEnclosingClass(); QString enclosingObjectVariable = enc ? u"enclosingClass"_s : u"module"_s; QString pyTypeName = cpythonTypeName(metaClass); @@ -6005,23 +5536,26 @@ void CppGenerator::writeClassRegister(TextStream &s, // 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) + u"_Type_bases"_s; - const auto &baseClasses = metaClass->typeSystemBaseClasses(); - if (metaClass->baseClassNames().size() > 1) { - s << "PyObject *" << pyTypeBasesVariable - << " = PyTuple_Pack(" << baseClasses.size() << ',' << '\n' << indent; - for (qsizetype i = 0, size = baseClasses.size(); i < size; ++i) { - if (i) - s << ",\n"; - s << "reinterpret_cast<PyObject *>(" - << cpythonTypeNameExt(baseClasses.at(i)->typeEntry()) << ')'; - } - s << ");\n\n" << outdent; + const QStringList pyBases = pyBaseTypes(metaClass); + s << "Shiboken::AutoDecRef " << pyTypeBasesVariable << "(PyTuple_Pack(" + << pyBases.size() << ",\n" << indent; + for (qsizetype i = 0, size = pyBases.size(); i < size; ++i) { + if (i) + s << ",\n"; + s << pyBases.at(i); } + s << "));\n\n" << outdent; // Create type and insert it in the module or enclosing class. const QString typePtr = u"_"_s + chopType(pyTypeName) @@ -6053,29 +5587,11 @@ void CppGenerator::writeClassRegister(TextStream &s, 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"; - } + s << "&Shiboken::callCppDestructor< " << globalScopePrefix(classContext) + << dtorClassName << " >,\n"; // 7:baseTypes - if (metaClass->baseClassNames().size() > 1) - s << pyTypeBasesVariable << ',' << '\n'; - else - s << "0,\n"; + s << pyTypeBasesVariable << ".object()," << '\n'; // 8:wrapperflags QByteArrayList wrapperFlags; @@ -6083,6 +5599,8 @@ void CppGenerator::writeClassRegister(TextStream &s, 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 @@ -6095,16 +5613,15 @@ void CppGenerator::writeClassRegister(TextStream &s, if (usePySideExtensions() && !classContext.forSmartPointer()) s << "SbkObjectType_SetPropertyStrings(pyType, " << chopType(pyTypeName) << "_PropertyStrings);\n"; - - if (!classContext.forSmartPointer()) - s << cpythonTypeNameExt(classTypeEntry) << " = pyType;\n\n"; - else - s << cpythonTypeNameExt(classContext.preciseType()) << " = pyType;\n\n"; + s << globalTypeVarExpr << " = pyType;\n\n"; // Register conversions for the type. writeConverterRegister(s, metaClass, classContext); s << '\n'; + if (classContext.forSmartPointer()) + writeSmartPointerConverterInitialization(s, classContext.preciseType()); + // class inject-code target/beginning if (!classTypeEntry->codeSnips().isEmpty()) { writeClassCodeSnips(s, classTypeEntry->codeSnips(), @@ -6114,7 +5631,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) { @@ -6131,8 +5648,9 @@ void CppGenerator::writeClassRegister(TextStream &s, // Set typediscovery struct or fill the struct of another one if (needsTypeDiscoveryFunction(metaClass)) { - s << "Shiboken::ObjectType::setTypeDiscoveryFunctionV2(" << cpythonTypeName(metaClass) - << ", &" << cpythonBaseName(metaClass) << "_typeDiscovery);\n\n"; + s << "Shiboken::ObjectType::setTypeDiscoveryFunctionV2(\n" << indent + << cpythonTypeName(metaClass) + << ", &" << cpythonBaseName(metaClass) << "_typeDiscovery);" << outdent << "\n\n"; } AbstractMetaEnumList classEnums = metaClass->enums(); @@ -6142,7 +5660,7 @@ void CppGenerator::writeClassRegister(TextStream &s, s << "// Pass the ..._EnumFlagInfo to the class.\n" << "SbkObjectType_SetEnumFlagInfo(pyType, " << chopType(pyTypeName) << "_EnumFlagInfo);\n\n"; - writeEnumsInitialization(s, classEnums, ErrorReturn::Void); + writeEnumsInitialization(s, classEnums); if (metaClass->hasSignals()) writeSignalInitialization(s, metaClass); @@ -6162,25 +5680,40 @@ void CppGenerator::writeClassRegister(TextStream &s, writeInitQtMetaTypeFunctionBody(s, classContext); } - if (usePySideExtensions() && metaClass->isQObject()) { + if (usePySideExtensions() && isQObject(metaClass)) { s << "Shiboken::ObjectType::setSubTypeInitHook(pyType, &PySide::initQObjectSubType);\n" - << "PySide::initDynamicMetaObject(pyType, &::" - << metaClass->qualifiedCppName() << "::staticMetaObject, sizeof("; - if (shouldGenerateCppWrapper(metaClass)) - s << wrapperName(metaClass); - else - s << "::" << metaClass->qualifiedCppName(); - s << "));\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() @@ -6189,7 +5722,7 @@ void CppGenerator::writeStaticFieldInitialization(TextStream &s, const AbstractM s << ");\n"; } } - s << '\n' << outdent << "}\n"; + s << "return type;\n" << outdent << "}\n"; } enum class QtRegisterMetaType @@ -6197,7 +5730,7 @@ enum class QtRegisterMetaType None, Pointer, Value }; -static bool hasQtMetaTypeRegistrationSpec(const AbstractMetaClass *c) +static bool hasQtMetaTypeRegistrationSpec(const AbstractMetaClassCPtr &c) { return c->typeEntry()->qtMetaTypeRegistration() != TypeSystem::QtMetaTypeRegistration::Unspecified; @@ -6206,7 +5739,7 @@ static bool hasQtMetaTypeRegistrationSpec(const AbstractMetaClass *c) // 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 AbstractMetaClass *c) +QtRegisterMetaType qtMetaTypeRegistration(const AbstractMetaClassCPtr &c) { if (c->isNamespace()) return QtRegisterMetaType::None; @@ -6225,7 +5758,7 @@ QtRegisterMetaType qtMetaTypeRegistration(const AbstractMetaClass *c) // Is there a "base" specification in some base class, meaning only the // base class is to be registered? - if (auto *base = recurseClassHierarchy(c, hasQtMetaTypeRegistrationSpec)) { + if (auto base = recurseClassHierarchy(c, hasQtMetaTypeRegistrationSpec)) { const auto baseSpec = base->typeEntry()->qtMetaTypeRegistration(); if (baseSpec == TypeSystem::QtMetaTypeRegistration::BaseEnabled) return QtRegisterMetaType::None; @@ -6233,7 +5766,7 @@ QtRegisterMetaType qtMetaTypeRegistration(const AbstractMetaClass *c) // Default. if (isObject) - return c->isQObject() ? QtRegisterMetaType::None : QtRegisterMetaType::Pointer; + return isQObject(c) ? QtRegisterMetaType::None : QtRegisterMetaType::Pointer; return !c->isAbstract() && c->isDefaultConstructible() ? QtRegisterMetaType::Value : QtRegisterMetaType::None; @@ -6241,7 +5774,7 @@ QtRegisterMetaType qtMetaTypeRegistration(const AbstractMetaClass *c) 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()) @@ -6249,7 +5782,7 @@ 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() + u"::"_s + nameVariants.constLast()); @@ -6268,55 +5801,67 @@ void CppGenerator::writeInitQtMetaTypeFunctionBody(TextStream &s, const Generato case QtRegisterMetaType::None: break; case QtRegisterMetaType::Pointer: - s << "qRegisterMetaType< ::" << className << " *>();\n"; + s << "qRegisterMetaType< " << m_gsp << className << " *>();\n"; break; case QtRegisterMetaType::Value: for (const QString &name : std::as_const(nameVariants)) - s << "qRegisterMetaType< ::" << className << " >(\"" << name << "\");\n"; + s << "qRegisterMetaType< " << m_gsp << className << " >(\"" << name << "\");\n"; break; } for (const AbstractMetaEnum &metaEnum : metaClass->enums()) { if (!metaEnum.isPrivate() && !metaEnum.isAnonymous()) { for (const QString &name : std::as_const(nameVariants)) { - s << "qRegisterMetaType< ::" + 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 - << sbkUnusedVariableCast(u"cptr"_s) - << sbkUnusedVariableCast(u"instanceType"_s); + << sbkUnusedVariableCast("cptr") + << sbkUnusedVariableCast("instanceType"); if (!polymorphicExpr.isEmpty()) { - polymorphicExpr = polymorphicExpr.replace(u"%1"_s, - u" reinterpret_cast< ::"_s - + metaClass->qualifiedCppName() - + u" *>(cptr)"_s); - s << " if (" << polymorphicExpr << ")\n" << indent + replacePolymorphicIdPlaceHolders(metaClass, &polymorphicExpr); + s << "if (" << polymorphicExpr << ")\n" << indent << "return cptr;\n" << outdent; } else if (metaClass->isPolymorphic()) { const auto &ancestors = metaClass->allTypeSystemAncestors(); - for (auto *ancestor : ancestors) { - if (ancestor->baseClass()) + for (const auto &ancestor : ancestors) { + if (ancestor->baseClass() && !ancestor->typeEntry()->isPolymorphicBase()) continue; if (ancestor->isPolymorphic()) { - s << "if (instanceType == Shiboken::SbkType< ::" + s << "if (instanceType == Shiboken::SbkType< " << m_gsp << ancestor->qualifiedCppName() << " >())\n" << indent - << "return dynamic_cast< ::" << metaClass->qualifiedCppName() - << " *>(reinterpret_cast< ::"<< ancestor->qualifiedCppName() + << "return dynamic_cast< " << getFullTypeName(metaClass) + << " *>(reinterpret_cast< "<< getFullTypeName(ancestor) << " *>(cptr));\n" << outdent; } else { qCWarning(lcShiboken).noquote().nospace() @@ -6330,7 +5875,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; @@ -6340,7 +5886,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"; @@ -6350,7 +5896,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. @@ -6360,10 +5906,10 @@ 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, u"self"_s) << ";\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"; } @@ -6380,7 +5926,7 @@ void CppGenerator::writeSetattroFunction(TextStream &s, AttroCheck attroCheck, Q_ASSERT(func); s << "{\n" << indent << "auto " << CPP_SELF_VAR << " = " - << cpythonWrapperCPtr(metaClass, u"self"_s) << ";\n"; + << cpythonWrapperCPtr(metaClass, PYTHON_SELF_VAR) << ";\n"; writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode, context); s << outdent << "}\n"; @@ -6389,26 +5935,7 @@ void CppGenerator::writeSetattroFunction(TextStream &s, AttroCheck attroCheck, writeSetattroDefaultReturn(s); } -static const char smartPtrComment[] = - "// Try to find the 'name' attribute, by retrieving the PyObject for " - "the corresponding C++ object held by the smart pointer.\n"; - -void CppGenerator::writeSmartPointerSetattroFunction(TextStream &s, - const GeneratorContext &context) const -{ - Q_ASSERT(context.forSmartPointer()); - writeSetattroDefinition(s, context.metaClass()); - s << smartPtrComment - << "if (auto *rawObj = PyObject_CallMethod(self, " << SMART_POINTER_GETTER - << ", 0)) {\n" << indent - << "if (PyObject_HasAttr(rawObj, name) != 0)\n" << indent - << "return PyObject_GenericSetAttr(rawObj, name, value);\n" << outdent - << "Py_DECREF(rawObj);\n" << outdent - << "}\n"; - writeSetattroDefaultReturn(s); -} - -void CppGenerator::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; @@ -6418,10 +5945,10 @@ 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 = u"PySide::getMetaDataFromQObject("_s - + cpythonWrapperCPtr(qobjectClass, u"self"_s) + result = u"PySide::getHiddenDataFromQObject("_s + + cpythonWrapperCPtr(qobjectClass, PYTHON_SELF_VAR) + u", self, name)"_s; } return result; @@ -6431,14 +5958,14 @@ 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() + const QString getattrFunc = usePySideExtensions() && isQObject(metaClass) ? qObjectGetAttroFunction() : u"PyObject_GenericGetAttr(self, name)"_s; if (attroCheck.testFlag(AttroCheckFlag::GetattroOverloads)) { @@ -6450,11 +5977,14 @@ void CppGenerator::writeGetattroFunction(TextStream &s, AttroCheck attroCheck, << "if (Shiboken::Object::isUserType(self)) {\n" << indent; // 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" << indent; + << "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 - << "return Py_TYPE(meth)->tp_descr_get(meth, self, nullptr);\n" << outdent - << "return PyFunction_Check(meth) ? PyMethod_New(meth, self)\n" + 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"; @@ -6480,7 +6010,7 @@ void CppGenerator::writeGetattroFunction(TextStream &s, AttroCheck attroCheck, Q_ASSERT(func); s << "{\n" << indent << "auto " << CPP_SELF_VAR << " = " - << cpythonWrapperCPtr(metaClass, u"self"_s) << ";\n"; + << cpythonWrapperCPtr(metaClass, PYTHON_SELF_VAR) << ";\n"; writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode, context); s << outdent << "}\n"; @@ -6489,50 +6019,6 @@ void CppGenerator::writeGetattroFunction(TextStream &s, AttroCheck attroCheck, s << "return " << getattrFunc << ";\n" << outdent << "}\n\n"; } -void CppGenerator::writeSmartPointerGetattroFunction(TextStream &s, - const GeneratorContext &context, - const BoolCastFunctionOptional &boolCast) -{ - Q_ASSERT(context.forSmartPointer()); - const AbstractMetaClass *metaClass = context.metaClass(); - writeGetattroDefinition(s, metaClass); - s << "PyObject *tmp = PyObject_GenericGetAttr(self, name);\n" - << "if (tmp)\n" << indent << "return tmp;\n" << outdent - << "if (PyErr_ExceptionMatches(PyExc_AttributeError) == 0)\n" - << indent << "return nullptr;\n" << outdent - << "PyErr_Clear();\n"; - - if (boolCast.has_value()) { - writeSmartPointerCppSelfDefinition(s, context); - s << "if ("; - writeNbBoolExpression(s, boolCast.value(), true /* invert */); - s << ") {\n" << indent - << R"(PyTypeObject *tp = Py_TYPE(self); -PyErr_Format(PyExc_AttributeError, "Attempt to retrieve '%s' from null object '%s'.", - Shiboken::String::toCString(name), tp->tp_name); -return nullptr; -)" << outdent << "}\n"; - } - - // This generates the code which dispatches access to member functions - // and fields from the smart pointer to its pointee. - s << smartPtrComment - << "if (auto *rawObj = PyObject_CallMethod(self, " - << SMART_POINTER_GETTER << ", 0)) {\n" << indent - << "if (auto *attribute = PyObject_GetAttr(rawObj, name))\n" - << indent << "tmp = attribute;\n" << outdent - << "Py_DECREF(rawObj);\n" << outdent - << "}\n" - << "if (!tmp) {\n" << indent - << R"(PyTypeObject *tp = Py_TYPE(self); -PyErr_Format(PyExc_AttributeError, - "'%.50s' object has no attribute '%.400s'", - tp->tp_name, Shiboken::String::toCString(name)); -)" << outdent - << "}\n" - << "return tmp;\n" << outdent << "}\n\n"; -} - void CppGenerator::writeNbBoolExpression(TextStream &s, const BoolCastFunction &f, bool invert) { @@ -6549,7 +6035,7 @@ void CppGenerator::writeNbBoolExpression(TextStream &s, const BoolCastFunction & void CppGenerator::writeNbBoolFunction(const GeneratorContext &context, const BoolCastFunction &f, - TextStream &s) const + TextStream &s) { s << "static int " << cpythonBaseName(context.metaClass()) << "___nb_bool(PyObject *self)\n" << "{\n" << indent; @@ -6571,30 +6057,59 @@ void CppGenerator::writeNbBoolFunction(const GeneratorContext &context, // Write declaration and invocation of the init function for the module init // function. -void CppGenerator::writeInitFunc(TextStream &declStr, TextStream &callStr, - const QString &initFunctionName, - const TypeEntryCPtr &enclosingEntry) -{ - const bool hasParent = - enclosingEntry && enclosingEntry->type() != TypeEntry::TypeSystemType; - declStr << "void init_" << initFunctionName << "(PyObject *" - << (hasParent ? "enclosingClass" : "module") << ");\n"; - callStr << "init_" << initFunctionName; - if (hasParent) { - callStr << "(reinterpret_cast<PyTypeObject *>(" - << cpythonTypeNameExt(enclosingEntry) << ")->tp_dict);\n"; +static void writeInitFuncDecl(TextStream &declStr, + const QString &functionName) +{ + declStr << "PyTypeObject *" << functionName << "(PyObject *enclosing);\n"; +} + +// Write declaration and invocation of the init function for the module init +// function. +void CppGenerator::writeInitFuncCall(TextStream &callStr, + const QString &functionName, + const TypeEntryCPtr &enclosingEntry, + const QString &pythonName, bool lazy) +{ + const bool hasParent = enclosingEntry && enclosingEntry->type() != TypeEntry::TypeSystemType; + if (!lazy) { + const QString enclosing = hasParent + ? "reinterpret_cast<PyObject *>("_L1 + cpythonTypeNameExt(enclosingEntry) + u')' + : "module"_L1; + callStr << functionName << '(' << enclosing << ");\n"; + } else if (hasParent) { + const QString &enclosingName = enclosingEntry->name(); + const auto parts = QStringView{enclosingName}.split(u"::", Qt::SkipEmptyParts); + const QString namePathPrefix = enclosingEntry->name().replace("::"_L1, "."_L1); + callStr << "Shiboken::Module::AddTypeCreationFunction(" + << "module, \"" << parts[0] << "\", " + << functionName << ", \"" << namePathPrefix << '.' << pythonName << "\");\n"; } else { - callStr << "(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); @@ -6603,8 +6118,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()) @@ -6620,50 +6135,61 @@ bool CppGenerator::finishGeneration() } AbstractMetaClassCList classesWithStaticFields; - for (auto cls : api().classes()){ + for (const auto &cls : api().classes()){ auto te = cls->typeEntry(); if (shouldGenerate(te)) { - writeInitFunc(s_classInitDecl, s_classPythonDefines, - getSimpleClassInitFunctionName(cls), - targetLangEnclosingEntry(te)); + const bool hasConfigCondition = te->hasConfigCondition(); + if (hasConfigCondition) { + s_classInitDecl << te->configCondition() << '\n'; + s_classPythonDefines << te->configCondition() << '\n'; + } + const QString initFunc = initFuncPrefix + getSimpleClassInitFunctionName(cls); + writeInitFuncDecl(s_classInitDecl, initFunc); + writeInitFuncCall(s_classPythonDefines, initFunc, + targetLangEnclosingEntry(te), cls->name()); if (cls->hasStaticFields()) { - s_classInitDecl << "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. for (const auto &smp : api().instantiatedSmartPointers()) { GeneratorContext context = contextForSmartPointer(smp.specialized, smp.type); - auto *enclosingClass = context.metaClass()->enclosingClass(); - auto enclosingTypeEntry = enclosingClass != nullptr - ? enclosingClass->typeEntry() - : targetLangEnclosingEntry(smp.type.typeEntry()); - writeInitFunc(s_classInitDecl, s_classPythonDefines, - getInitFunctionName(context), - enclosingTypeEntry); - includes << smp.type.instantiations().constFirst().typeEntry()->include(); + const auto enclosingClass = context.metaClass()->enclosingClass(); + auto enclosingTypeEntry = targetLangEnclosingEntry(smp.specialized->typeEntry()); + + const QString initFunc = initFuncPrefix + getInitFunctionName(context); + writeInitFuncDecl(s_classInitDecl, initFunc); + writeInitFuncCall(s_classPythonDefines, + initFunc, enclosingTypeEntry, smp.specialized->name()); + includes.insert(smp.type.instantiations().constFirst().typeEntry()->include()); } for (auto &instantiatedContainer : api().instantiatedContainers()) { + includes.insert(instantiatedContainer.typeEntry()->include()); for (const auto &inst : instantiatedContainer.instantiations()) - includes << inst.typeEntry()->include(); + 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 << te->include(); + includes.insert(te->include()); for (const auto &metaClass : it.value()) - includes << metaClass->typeEntry()->include(); + includes.insert(metaClass->typeEntry()->include()); } const QList<CustomConversionPtr> &typeConversions = getPrimitiveCustomConversions(); for (const auto &c : typeConversions) { - if (auto te = c->ownerType(); !te.isNull()) - includes << te->include(); + if (auto te = c->ownerType()) + includes.insert(te->include()); } QString moduleFileName(outputDirectory() + u'/' + subDirectoryForPackage(packageName())); @@ -6695,13 +6221,13 @@ bool CppGenerator::finishGeneration() } s << "#include \"" << getModuleHeaderFileName() << '"' << "\n\n"; - for (const Include &include : std::as_const(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) @@ -6710,7 +6236,7 @@ bool CppGenerator::finishGeneration() TypeDatabase *typeDb = TypeDatabase::instance(); TypeSystemTypeEntryCPtr moduleEntry = typeDb->defaultTypeSystemType(); - Q_ASSERT(!moduleEntry.isNull()); + Q_ASSERT(moduleEntry); s << '\n'; // Extra includes @@ -6725,29 +6251,34 @@ bool CppGenerator::finishGeneration() s << '\n'; } + // FIXME PYSIDE-7: Remove backwards compatible structure s << "// Current module's type array.\n" - << "PyTypeObject **" << cppApiVariableName() << " = nullptr;\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 << "static PyObject *attrName = Shiboken::PyName::qtStaticMetaObject();\n" - << "for (int i = 0, imax = SBK_" << moduleName() - << "_IDX_COUNT; i < imax; i++) {\n" << indent - << "PyObject *pyType = reinterpret_cast<PyObject *>(" << cppApiVariableName() << "[i]);\n" - << "if (pyType && PyObject_HasAttr(pyType, attrName))\n" << indent + << "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 " @@ -6755,7 +6286,7 @@ bool CppGenerator::finishGeneration() << s_globalFunctionImpl.toString() << '\n' << "static PyMethodDef " << moduleName() << "_methods[] = {\n" << indent << s_globalFunctionDef.toString() - << methodDefSentinel << outdent << "};\n\n" + << METHOD_DEF_SENTINEL << outdent << "};\n\n" << "// Classes initialization functions " << "------------------------------------------------------------\n" << s_classInitDecl.toString() << '\n'; @@ -6776,7 +6307,6 @@ bool CppGenerator::finishGeneration() << "} // namespace Shiboken\n\n"; } - writeFlagsNumberMethodsDefinitions(s, globalEnums); s << '\n'; } @@ -6784,7 +6314,7 @@ 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"; } @@ -6796,7 +6326,7 @@ bool CppGenerator::finishGeneration() 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); @@ -6816,31 +6346,22 @@ bool CppGenerator::finishGeneration() 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()) { - opaqueContainers.insert(container, - writeOpaqueContainerConverterFunctions(s, container)); + auto data = writeOpaqueContainerConverterFunctions(s, container, + &valueConverters); + opaqueContainers.insert(container, data); } } s << '\n'; } - // Implicit smart pointers conversions - const auto &smartPointersList = api().instantiatedSmartPointers(); - if (!smartPointersList.isEmpty()) { - s << "// SmartPointers converters.\n\n"; - for (const auto &smp : smartPointersList) { - s << "// C++ to Python conversion for smart pointer type '" - << smp.type.cppSignature() << "'.\n"; - writeSmartPointerConverterFunctions(s, smp.type); - } - s << '\n'; - } - s << "static struct PyModuleDef moduledef = {\n" << " /* m_base */ PyModuleDef_HEAD_INIT,\n" << " /* m_name */ \"" << moduleName() << "\",\n" @@ -6855,6 +6376,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_" @@ -6864,8 +6387,8 @@ bool CppGenerator::finishGeneration() << indent << "return " << globalModuleVar << ";\n" << outdent; // 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 @@ -6881,9 +6404,30 @@ bool CppGenerator::finishGeneration() 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"; + if (usePySideExtensions()) + s << "QT_WARNING_PUSH\nQT_WARNING_DISABLE_DEPRECATED\n"; + s << "// The backward compatible alias with upper case indexes.\n" + << cppApiVariableNameOld() << " = reinterpret_cast<PyTypeObject **>(cppApi);\n"; + if (usePySideExtensions()) + s << "QT_WARNING_POP\n"; + s << '\n'; } s << "// Create an array of primitive type converters for the current module.\n" @@ -6893,8 +6437,13 @@ 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()) { @@ -6908,7 +6457,7 @@ bool CppGenerator::finishGeneration() if (!containers.isEmpty()) { s << '\n'; for (const AbstractMetaType &container : containers) { - const QString converterObj = writeContainerConverterInitialization(s, container); + const QString converterObj = writeContainerConverterInitialization(s, container, api()); const auto it = opaqueContainers.constFind(container); if (it != opaqueContainers.constEnd()) { writeSetPythonToCppPointerConversion(s, converterObj, @@ -6927,14 +6476,6 @@ bool CppGenerator::finishGeneration() s << '\n'; } - if (!smartPointersList.isEmpty()) { - s << '\n'; - for (const auto &smp : smartPointersList) { - writeSmartPointerConverterInitialization(s, smp.type); - s << '\n'; - } - } - if (!extendedConverters.isEmpty()) { s << '\n'; for (ExtendedConverterData::const_iterator it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) { @@ -6943,7 +6484,7 @@ bool CppGenerator::finishGeneration() } } - writeEnumsInitialization(s, globalEnums, ErrorReturn::Default); + writeEnumsInitialization(s, globalEnums); s << "// Register primitive types converters.\n"; const PrimitiveTypeEntryCList &primitiveTypeList = primitiveTypes(); @@ -6953,14 +6494,7 @@ bool CppGenerator::finishGeneration() if (!pte->referencesType()) continue; TypeEntryCPtr referencedType = basicReferencedTypeEntry(pte); - QString converter = converterObject(referencedType); - QStringList cppSignature = pte->qualifiedCppName().split(u"::"_s, Qt::SkipEmptyParts); - while (!cppSignature.isEmpty()) { - QString signature = cppSignature.join(u"::"_s); - s << "Shiboken::Conversions::registerConverterName(" - << converter << ", \"" << signature << "\");\n"; - cppSignature.removeFirst(); - } + registerConverterInScopes(s, pte->qualifiedCppName(), converterObject(referencedType)); } s << '\n'; @@ -6972,27 +6506,29 @@ bool CppGenerator::finishGeneration() // of the previously registered types (PYSIDE-1529). if (!classesWithStaticFields.isEmpty()) { s << "\n// Static field initialization\n"; - for (auto cls : std::as_const(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 : 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"; } @@ -7000,7 +6536,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"; @@ -7026,17 +6562,17 @@ static bool useParentHeuristics(const ApiExtractorResult &api, { if (!ComplexTypeEntry::isParentManagementEnabled()) // FIXME PYSIDE 7: Remove this return true; - auto *owner = func->ownerClass(); - if (owner == nullptr) + const auto owner = func->ownerClass(); + if (!owner) return false; - auto ownerEntry = owner->parentManagementEntry(); - if (ownerEntry.isNull()) + auto ownerEntry = parentManagementEntry(owner); + if (!ownerEntry) return false; auto argTypeEntry = argType.typeEntry(); if (!argTypeEntry->isComplex()) return false; - auto *argClass = AbstractMetaClass::findClass(api.classes(), argTypeEntry); - return argClass != nullptr && argClass->parentManagementEntry() == ownerEntry; + const auto argClass = AbstractMetaClass::findClass(api.classes(), argTypeEntry); + return argClass && parentManagementEntry(argClass) == ownerEntry; } bool CppGenerator::writeParentChildManagement(TextStream &s, const AbstractMetaFunctionCPtr &func, @@ -7075,7 +6611,7 @@ bool CppGenerator::writeParentChildManagement(TextStream &s, const AbstractMetaF if (parentIndex == 0) { parentVariable = PYTHON_RETURN_VAR; } else if (parentIndex == -1) { - parentVariable = u"self"_s; + parentVariable = PYTHON_SELF_VAR; } else { parentVariable = usePyArgs ? pythonArgsAt(parentIndex - 1) : PYTHON_ARG; @@ -7085,7 +6621,7 @@ bool CppGenerator::writeParentChildManagement(TextStream &s, const AbstractMetaF if (childIndex == 0) { childVariable = PYTHON_RETURN_VAR; } else if (childIndex == -1) { - childVariable = u"self"_s; + childVariable = PYTHON_SELF_VAR; } else { childVariable = usePyArgs ? pythonArgsAt(childIndex - 1) : PYTHON_ARG; @@ -7142,9 +6678,9 @@ void CppGenerator::writeReturnValueHeuristics(TextStream &s, const AbstractMetaF } } -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{\n" << indent; @@ -7168,7 +6704,7 @@ void CppGenerator::writeHashFunction(TextStream &s, const GeneratorContext &cont void CppGenerator::writeDefaultSequenceMethods(TextStream &s, const GeneratorContext &context) const { - const AbstractMetaClass *metaClass = context.metaClass(); + const auto metaClass = context.metaClass(); ErrorReturn errorReturn = ErrorReturn::Zero; // __len__ @@ -7235,14 +6771,20 @@ void CppGenerator::writeIndexError(TextStream &s, const QString &errorMsg, << 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); @@ -7257,15 +6799,21 @@ const auto idx = str.indexOf('('); auto *typeName = Py_TYPE(self)->tp_name; if (idx >= 0) )" << indent << "str.replace(0, idx, typeName);\n" << outdent - << "str = str.trimmed();\n" - << "PyObject *mod = PyDict_GetItem(Py_TYPE(self)->tp_dict, Shiboken::PyMagicName::module());\n"; + << "str = str.trimmed();\n" + << "Shiboken::AutoDecRef tpDict(PepType_GetDict(Py_TYPE(self)));\n" + << "PyObject *mod = PyDict_GetItem(tpDict.object(), Shiboken::PyMagicName::module());\n"; // PYSIDE-595: The introduction of heap types has the side effect that the module name // is always prepended to the type name. Therefore the strchr check: s << "if (mod != nullptr && std::strchr(typeName, '.') == nullptr)\n" << indent << "return Shiboken::String::fromFormat(\"<%s.%s at %p>\"," " Shiboken::String::toCString(mod), str.constData(), self);\n" << outdent - << "return Shiboken::String::fromFormat(\"<%s at %p>\", str.constData(), self);\n" - << outdent << "}\n} // extern C\n\n"; + << "return Shiboken::String::fromFormat(\"<%s at %p>\", str.constData(), self);\n"; + writeReprFunctionFooter(s); return funcName; } + +void CppGenerator::writeReprFunctionFooter(TextStream &s) +{ + s << outdent << "}\n} // extern C\n\n"; +} diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.h b/sources/shiboken6/generator/shiboken/cppgenerator.h index b2e99b7bb..5920c9a3a 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator.h +++ b/sources/shiboken6/generator/shiboken/cppgenerator.h @@ -5,13 +5,15 @@ #define CPPGENERATOR_H #include "shibokengenerator.h" -#include "abstractmetalang_enums.h" #include "include.h" #include "modifications_typedefs.h" #include <QtCore/QFlags> +#include <QtCore/QSet> #include <QtCore/QHash> -#include <QtCore/QSharedPointer> + +#include <memory> +#include <utility> class OverloadDataNode; class OverloadDataRootNode; @@ -43,66 +45,67 @@ public: protected: QString fileNameForContext(const GeneratorContext &context) const override; - static QList<AbstractMetaFunctionCList> - filterGroupedOperatorFunctions(const AbstractMetaClass *metaClass, - OperatorQueryOptions query); void generateClass(TextStream &s, const GeneratorContext &classContext) override; bool finishGeneration() override; private: - struct BoolCastFunction + struct VirtualMethodReturn { - AbstractMetaFunctionCPtr function; - bool invert = false; // Function is isNull() (invert result). + QString statement; + bool needsReference = false; }; - using BoolCastFunctionOptional = std::optional<BoolCastFunction>; + void generateSmartPointerClass(TextStream &s, const GeneratorContext &classContext); void generateIncludes(TextStream &s, const GeneratorContext &classContext, const IncludeGroupList &includes = {}, const AbstractMetaClassCList &innerClasses = {}) const; - static void writeInitFunc(TextStream &declStr, TextStream &callStr, - const QString &initFunctionName, - const TypeEntryCPtr &enclosingEntry = {}); + static void writeInitFuncCall(TextStream &callStr, + const QString &functionName, + const TypeEntryCPtr &enclosingEntry, + const QString &pythonName, bool lazy = true); static void writeCacheResetNative(TextStream &s, const GeneratorContext &classContext); void writeConstructorNative(TextStream &s, const GeneratorContext &classContext, const AbstractMetaFunctionCPtr &func) const; - void writeDestructorNative(TextStream &s, const GeneratorContext &classContext) const; + static void writeDestructorNative(TextStream &s, const GeneratorContext &classContext); QString getVirtualFunctionReturnTypeName(const AbstractMetaFunctionCPtr &func) const; - static QPair<QString, QChar> - virtualMethodNativeArg(const AbstractMetaFunctionCPtr &func, + static std::pair<QString, QChar> virtualMethodNativeArg(const AbstractMetaFunctionCPtr &func, const AbstractMetaArgument &arg); - void writeVirtualMethodNativeVectorCallArgs(TextStream &s, - const AbstractMetaFunctionCPtr &func, - const AbstractMetaArgumentList &arguments, - const QList<int> &invalidateArgs) const; - void writeVirtualMethodNativeArgs(TextStream &s, - const AbstractMetaFunctionCPtr &func, - const AbstractMetaArgumentList &arguments, - const QList<int> &invalidateArgs) const; - void writeVirtualMethodNative(TextStream &s, const AbstractMetaFunctionCPtr &func, + static void writeVirtualMethodNativeVectorCallArgs(TextStream &s, + const AbstractMetaFunctionCPtr &func, + const AbstractMetaArgumentList &arguments, + const QList<int> &invalidateArgs); + static void writeVirtualMethodNativeArgs(TextStream &s, + const AbstractMetaFunctionCPtr &func, + const AbstractMetaArgumentList &arguments, + const QList<int> &invalidateArgs); + void writeVirtualMethodNative(TextStream &s, + const AbstractMetaFunctionCPtr &func, int cacheIndex) const; + void writeVirtualMethodPythonOverride(TextStream &s, + const AbstractMetaFunctionCPtr &func, + const CodeSnipList &snips, + const VirtualMethodReturn &returnStatement) const; + void writeUserAddedPythonOverride(TextStream &s, + const AbstractMetaFunctionCPtr &func) const; void writeVirtualMethodCppCall(TextStream &s, const AbstractMetaFunctionCPtr &func, const QString &funcName, const QList<CodeSnip> &snips, const AbstractMetaArgument *lastArg, const TypeEntryCPtr &retType, const QString &returnStatement, bool hasGil) const; - static QString virtualMethodReturn(TextStream &s, const ApiExtractorResult &api, - const AbstractMetaFunctionCPtr &func, - const FunctionModificationList &functionModifications); + + static VirtualMethodReturn virtualMethodReturn(const ApiExtractorResult &api, + const AbstractMetaFunctionCPtr &func, + const FunctionModificationList &functionModifications); void writeMetaObjectMethod(TextStream &s, const GeneratorContext &classContext) const; static void writeMetaCast(TextStream &s, const GeneratorContext &classContext); - void writeFlagsConverterFunctions(TextStream &s, const FlagsTypeEntryCPtr &flagsType, - const QString &enumTypeName, - const QString &flagsCppTypeName, - const QString &enumTypeCheck) const; void writeEnumConverterFunctions(TextStream &s, const AbstractMetaEnum &metaEnum) const; - void writeConverterFunctions(TextStream &s, const AbstractMetaClass *metaClass, + void writeConverterFunctions(TextStream &s, const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext) const; void writeCustomConverterFunctions(TextStream &s, const CustomConversionPtr &customConversion) const; - void writeConverterRegister(TextStream &s, const AbstractMetaClass *metaClass, + void writeConverterRegister(TextStream &s, const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext) const; static void writeCustomConverterRegister(TextStream &s, const CustomConversionPtr &customConversion, @@ -122,16 +125,21 @@ private: OpaqueContainerData writeOpaqueContainerConverterFunctions(TextStream &s, - const AbstractMetaType &containerType) const; + const AbstractMetaType &containerType, + QSet<AbstractMetaType> *valueTypes) const; + void writeOpaqueContainerValueConverter(TextStream &s, + const AbstractMetaType &valueType) const; void writeSmartPointerConverterFunctions(TextStream &s, const AbstractMetaType &smartPointerType) const; - bool needsArgumentErrorHandling(const OverloadData &overloadData) const; - void writeMethodWrapperPreamble(TextStream &s, const OverloadData &overloadData, - const GeneratorContext &context, - ErrorReturn errorReturn = ErrorReturn::Default) const; - void writeConstructorWrapper(TextStream &s, const OverloadData &overloadData, + static bool needsArgumentErrorHandling(const OverloadData &overloadData); + static void writeMethodWrapperPreamble(TextStream &s, + const OverloadData &overloadData, + const GeneratorContext &context, + ErrorReturn errorReturn = ErrorReturn::Default); + void writeConstructorWrapper(TextStream &s, + const OverloadData &overloadData, const GeneratorContext &classContext) const; void writeMethodWrapper(TextStream &s, const OverloadData &overloadData, const GeneratorContext &classContext) const; @@ -147,22 +155,29 @@ private: bool useWrapperClass); static void writeSmartPointerCppSelfConversion(TextStream &s, const GeneratorContext &context); + + static void writeCppSelfVarDef(TextStream &s, CppSelfDefinitionFlags flags = {}); static void writeSmartPointerCppSelfDefinition(TextStream &s, const GeneratorContext &, ErrorReturn errorReturn = ErrorReturn::Default, CppSelfDefinitionFlags flags = {}); - void writeCppSelfDefinition(TextStream &s, - const AbstractMetaFunctionCPtr &func, - const GeneratorContext &context, - ErrorReturn errorReturn = ErrorReturn::Default, - CppSelfDefinitionFlags flags = {}) const; - void writeCppSelfDefinition(TextStream &s, - const GeneratorContext &context, - ErrorReturn errorReturn = ErrorReturn::Default, - CppSelfDefinitionFlags flags = {}) const; - - static void writeErrorSection(TextStream &s, const OverloadData &overloadData, + static void writeCppSelfDefinition(TextStream &s, + const AbstractMetaFunctionCPtr &func, + const GeneratorContext &context, + ErrorReturn errorReturn = ErrorReturn::Default, + CppSelfDefinitionFlags flags = {}); + static void writeCppSelfDefinition(TextStream &s, + const GeneratorContext &context, + ErrorReturn errorReturn = ErrorReturn::Default, + CppSelfDefinitionFlags flags = {}); + + static void writeErrorSection(TextStream &s, + const OverloadData &overloadData, ErrorReturn errorReturn); + + static QString returnErrorWrongArguments(const OverloadData &overloadData, + ErrorReturn errorReturn); + static void writeFunctionReturnErrorCheckSection(TextStream &s, ErrorReturn errorReturn, bool hasReturnValue = true); @@ -176,18 +191,22 @@ private: bool isNumber = false, bool rejectNull = false); static void writeTypeCheck(TextStream &s, const QString &customType, const QString &argumentName); - static void writeTypeCheck(TextStream& s, const QSharedPointer<OverloadDataNode> &overloadData, + static void writeTypeCheck(TextStream& s, const std::shared_ptr<OverloadDataNode> &overloadData, const QString &argumentName); - static void writeTypeDiscoveryFunction(TextStream &s, const AbstractMetaClass *metaClass); + static void replacePolymorphicIdPlaceHolders(const AbstractMetaClassCPtr &metaClass, + QString *id); + static void writeTypeDiscoveryFunction(TextStream &s, + const AbstractMetaClassCPtr &metaClass); - void writeSetattroDefinition(TextStream &s, const AbstractMetaClass *metaClass) const; + static void writeSetattroDefinition(TextStream &s, const AbstractMetaClassCPtr &metaClass); static void writeSetattroDefaultReturn(TextStream &s); - void writeSmartPointerSetattroFunction(TextStream &s, - const GeneratorContext &context) const; - void writeSetattroFunction(TextStream &s, AttroCheck attroCheck, + static void writeSmartPointerSetattroFunction(TextStream &s, + const GeneratorContext &context); + void writeSetattroFunction(TextStream &s, + AttroCheck attroCheck, const GeneratorContext &context) const; - static void writeGetattroDefinition(TextStream &s, const AbstractMetaClass *metaClass); + static void writeGetattroDefinition(TextStream &s, const AbstractMetaClassCPtr &metaClass); static void writeSmartPointerGetattroFunction(TextStream &s, const GeneratorContext &context, const BoolCastFunctionOptional &boolCast); @@ -195,11 +214,10 @@ private: const GeneratorContext &context) const; QString qObjectGetAttroFunction() const; - void writeNbBoolFunction(const GeneratorContext &context, - const BoolCastFunction &f, - TextStream &s) const; - static void writeNbBoolExpression(TextStream &s, const BoolCastFunction &f, - bool invert = false); + static void writeNbBoolFunction(const GeneratorContext &context, + const BoolCastFunction &f, + TextStream &s); + static void writeNbBoolExpression(TextStream &s, const BoolCastFunction &f, bool invert = false); /** * Writes Python to C++ conversions for arguments on Python wrappers. @@ -216,7 +234,7 @@ private: qsizetype writeArgumentConversion(TextStream &s, const AbstractMetaType &argType, const QString &argName, const QString &pyArgName, ErrorReturn errorReturn, - const AbstractMetaClass *context = nullptr, + const AbstractMetaClassCPtr &context = {}, const QString &defaultValue = QString(), bool castArgumentAsUnused = false) const; @@ -238,7 +256,7 @@ private: const AbstractMetaType &type, const QString &pyIn, const QString &cppOut, - const AbstractMetaClass *context = nullptr, + const AbstractMetaClassCPtr &context = {}, const QString &defaultValue = {}) const; /// Writes the conversion rule for arguments of regular and virtual methods. @@ -266,7 +284,8 @@ private: * \param s text stream to write * \param overloadData the overload data describing all the possible overloads for the function/method */ - void writeOverloadedFunctionDecisor(TextStream &s, const OverloadData &overloadData) const; + void writeOverloadedFunctionDecisor(TextStream &s, const OverloadData &overloadData, + ErrorReturn errorReturn) const; /// Recursive auxiliar method to the other writeOverloadedFunctionDecisor. void writeOverloadedFunctionDecisorEngine(TextStream &s, const OverloadData &overloadData, @@ -305,6 +324,8 @@ private: QString targetTypeName = QString()) const; void writeCppToPythonFunction(TextStream &s, const CustomConversionPtr &customConversion) const; void writeCppToPythonFunction(TextStream &s, const AbstractMetaType &containerType) const; + /// Main target type name of a container (for naming the functions). + static QString containerNativeToTargetTypeName(const ContainerTypeEntryCPtr &type); /// Writes a Python to C++ conversion function. void writePythonToCppFunction(TextStream &s, const QString &code, const QString &sourceTypeName, @@ -334,6 +355,10 @@ private: void writePythonToCppConversionFunctions(TextStream &s, const AbstractMetaType &containerType) const; + void writePythonToCppConversionFunction(TextStream &s, + const AbstractMetaType &containerType, + const TargetToNativeConversion &conv) const; + static void writeAddPythonToCppConversion(TextStream &s, const QString &converterVar, const QString &pythonToCppFunc, const QString &isConvertibleFunc); @@ -342,15 +367,18 @@ private: const QString &pythonToCppFunc, const QString &isConvertibleFunc); - void writeNamedArgumentResolution(TextStream &s, const AbstractMetaFunctionCPtr &func, - bool usePyArgs, const OverloadData &overloadData) const; + static void writeNamedArgumentResolution(TextStream &s, + const AbstractMetaFunctionCPtr &func, + bool usePyArgs, + const OverloadData &overloadData, + ErrorReturn errorReturn); /// Returns a string containing the name of an argument for the given function and argument index. static QString argumentNameFromIndex(const ApiExtractorResult &api, const AbstractMetaFunctionCPtr &func, int argIndex); /// Returns the class for an ownership modification of the argument. /// Throws if the argument is not a class or cannot be found. - static const AbstractMetaClass * + static AbstractMetaClassCPtr argumentClassFromIndex(const ApiExtractorResult &api, const AbstractMetaFunctionCPtr &func, int argIndex); @@ -360,111 +388,102 @@ private: ErrorReturn errorReturn) const; static QString getInitFunctionName(const GeneratorContext &context) ; - static QString getSimpleClassInitFunctionName(const AbstractMetaClass *metaClass) ; - static QString getSimpleClassStaticFieldsInitFunctionName(const AbstractMetaClass *metaClass); + static QString getSimpleClassInitFunctionName(const AbstractMetaClassCPtr &metaClass); + static QString + getSimpleClassStaticFieldsInitFunctionName(const AbstractMetaClassCPtr &metaClass); static void writeSignatureStrings(TextStream &s, const QString &signatures, const QString &arrayName, const char *comment); + void writeInitInheritance(TextStream &s) const; void writeClassRegister(TextStream &s, - const AbstractMetaClass *metaClass, + const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext, const QString &signatures) const; - QString destructorClassName(const AbstractMetaClass *metaClass, - const GeneratorContext &classContext) const; + static QStringList pyBaseTypes(const AbstractMetaClassCPtr &metaClass); + static QString destructorClassName(const AbstractMetaClassCPtr &metaClass, + const GeneratorContext &classContext); static void writeStaticFieldInitialization(TextStream &s, - const AbstractMetaClass *metaClass); + const AbstractMetaClassCPtr &metaClass); void writeClassDefinition(TextStream &s, - const AbstractMetaClass *metaClass, + const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext); QByteArrayList methodDefinitionParameters(const OverloadData &overloadData) const; QList<PyMethodDefEntry> methodDefinitionEntries(const OverloadData &overloadData) const; void writeSignatureInfo(TextStream &s, const OverloadData &overloads) const; QString signatureParameter(const AbstractMetaArgument &arg) const; + QString pythonSignature(const AbstractMetaType &type) const; /// Writes the implementation of all methods part of python sequence protocol void writeSequenceMethods(TextStream &s, - const AbstractMetaClass *metaClass, + const AbstractMetaClassCPtr &metaClass, const GeneratorContext &context) const; static void writeTypeAsSequenceDefinition(TextStream &s, - const AbstractMetaClass *metaClass); + const AbstractMetaClassCPtr &metaClass); /// Writes the PyMappingMethods structure for types that supports the python mapping protocol. static void writeTypeAsMappingDefinition(TextStream &s, - const AbstractMetaClass *metaClass); + const AbstractMetaClassCPtr &metaClass); void writeMappingMethods(TextStream &s, - const AbstractMetaClass *metaClass, + const AbstractMetaClassCPtr &metaClass, const GeneratorContext &context) const; - void writeTypeAsNumberDefinition(TextStream &s, const AbstractMetaClass *metaClass) const; - - static void writeTpTraverseFunction(TextStream &s, const AbstractMetaClass *metaClass); - static void writeTpClearFunction(TextStream &s, const AbstractMetaClass *metaClass); - - void writeCopyFunction(TextStream &s, const GeneratorContext &context) const; - - QString cppFieldAccess(const AbstractMetaField &metaField, - const GeneratorContext &context) const; - void writeGetterFunction(TextStream &s, - const AbstractMetaField &metaField, - const GeneratorContext &context) const; - void writeGetterFunction(TextStream &s, - const QPropertySpec &property, - const GeneratorContext &context) const; - void writeSetterFunctionPreamble(TextStream &s, - const QString &name, - const QString &funcName, - const AbstractMetaType &type, - const GeneratorContext &context) const; - void writeSetterFunction(TextStream &s, - const AbstractMetaField &metaField, - const GeneratorContext &context) const; - void writeSetterFunction(TextStream &s, - const QPropertySpec &property, - const GeneratorContext &context) const; - - void writeRichCompareFunctionHeader(TextStream &s, - const QString &baseName, - const GeneratorContext &context) const; - static void writeRichCompareFunctionFooter(TextStream &s, - const QString &baseName); + void writeTypeAsNumberDefinition(TextStream &s, const AbstractMetaClassCPtr &metaClass) const; + + static void writeTpTraverseFunction(TextStream &s, const AbstractMetaClassCPtr &metaClass); + static void writeTpClearFunction(TextStream &s, const AbstractMetaClassCPtr &metaClass); + + static QString writeCopyFunction(TextStream &s, TextStream &definitionStream, + TextStream &signatureStream, const GeneratorContext &context); + + static QString cppFieldAccess(const AbstractMetaField &metaField, + const GeneratorContext &context); + static void writeGetterFunction(TextStream &s, + const AbstractMetaField &metaField, + const GeneratorContext &context); + static void writeGetterFunction(TextStream &s, + const QPropertySpec &property, + const GeneratorContext &context); + static void writeSetterFunctionPreamble(TextStream &s, + const QString &name, + const QString &funcName, + const AbstractMetaType &type, + const GeneratorContext &context); + static void writeSetterFunction(TextStream &s, + const AbstractMetaField &metaField, + const GeneratorContext &context); + static void writeSetterFunction(TextStream &s, + const QPropertySpec &property, + const GeneratorContext &context); + + static void writeRichCompareFunctionHeader(TextStream &s, + const QString &baseName, + const GeneratorContext &context); void writeRichCompareFunction(TextStream &s, const GeneratorContext &context) const; void writeSmartPointerRichCompareFunction(TextStream &s, const GeneratorContext &context) const; - void writeEnumsInitialization(TextStream &s, AbstractMetaEnumList &enums, - ErrorReturn errorReturn) const; - void writeEnumInitialization(TextStream &s, const AbstractMetaEnum &metaEnum, - ErrorReturn errorReturn) const; + static void writeEnumsInitialization(TextStream &s, AbstractMetaEnumList &enums); + static bool writeEnumInitialization(TextStream &s, const AbstractMetaEnum &metaEnum); - static void writeSignalInitialization(TextStream &s, const AbstractMetaClass *metaClass); - - static void writeFlagsMethods(TextStream &s, const AbstractMetaEnum &cppEnum); - static void writeFlagsToLong(TextStream &s, const AbstractMetaEnum &cppEnum); - static void writeFlagsNonZero(TextStream &s, const AbstractMetaEnum &cppEnum); - static void writeFlagsNumberMethodsDefinition(TextStream &s, const AbstractMetaEnum &cppEnum); - static void writeFlagsNumberMethodsDefinitions(TextStream &s, - const AbstractMetaEnumList &enums); - static void writeFlagsBinaryOperator(TextStream &s, - const AbstractMetaEnum &cppEnum, - const QString &pyOpName, - const QString &cppOpName); - static void writeFlagsUnaryOperator(TextStream &s, - const AbstractMetaEnum &cppEnum, - const QString &pyOpName, - const QString &cppOpName, - bool boolResult = false); - - /// Writes the function that registers the multiple inheritance information for the classes that need it. - static void writeMultipleInheritanceInitializerFunction(TextStream &s, const AbstractMetaClass *metaClass); - /// Writes the implementation of special cast functions, used when we need to cast a class with multiple inheritance. - static void writeSpecialCastFunction(TextStream &s, const AbstractMetaClass *metaClass); + static void writeSignalInitialization(TextStream &s, const AbstractMetaClassCPtr &metaClass); + + /// Writes the function that registers the multiple inheritance information + /// for the classes that need it. + static void writeMultipleInheritanceInitializerFunction(TextStream &s, + const AbstractMetaClassCPtr &metaClass); + /// Writes the implementation of special cast functions, used when we need + /// to cast a class with multiple inheritance. + static void writeSpecialCastFunction(TextStream &s, const AbstractMetaClassCPtr &metaClass); static void writePrimitiveConverterInitialization(TextStream &s, const CustomConversionPtr &customConversion); - static void writeFlagsConverterInitialization(TextStream &s, const FlagsTypeEntryCPtr &enumType); static void writeEnumConverterInitialization(TextStream &s, const AbstractMetaEnum &metaEnum); - QString writeContainerConverterInitialization(TextStream &s, const AbstractMetaType &type) const; + static QString writeContainerConverterInitialization(TextStream &s, + const AbstractMetaType &type, + const ApiExtractorResult &api); void writeSmartPointerConverterInitialization(TextStream &s, const AbstractMetaType &ype) const; + + static QString typeInitStruct(const TypeEntryCPtr &te); static void writeExtendedConverterInitialization(TextStream &s, const TypeEntryCPtr &externalType, const AbstractMetaClassCList &conversions); @@ -485,24 +504,25 @@ private: * \return name of the multiple inheritance information initializer function or * an empty string if there is no multiple inheritance in its ancestry. */ - static QString multipleInheritanceInitializerFunctionName(const AbstractMetaClass *metaClass); + static QString multipleInheritanceInitializerFunctionName(const AbstractMetaClassCPtr &metaClass); /// Returns a list of all classes to which the given class could be cast. - static QStringList getAncestorMultipleInheritance(const AbstractMetaClass *metaClass); + static QStringList getAncestorMultipleInheritance(const AbstractMetaClassCPtr &metaClass); /// Returns true if the given class supports the python number protocol - bool supportsNumberProtocol(const AbstractMetaClass *metaClass) const; + static bool supportsNumberProtocol(const AbstractMetaClassCPtr &metaClass); /// Returns true if the given class supports the python sequence protocol - static bool supportsSequenceProtocol(const AbstractMetaClass *metaClass) ; + static bool supportsSequenceProtocol(const AbstractMetaClassCPtr &metaClass) ; /// Returns true if the given class supports the python mapping protocol - static bool supportsMappingProtocol(const AbstractMetaClass *metaClass) ; + static bool supportsMappingProtocol(const AbstractMetaClassCPtr &metaClass) ; /// Returns true if generator should produce getters and setters for the given class. - bool shouldGenerateGetSetList(const AbstractMetaClass *metaClass) const; + static bool shouldGenerateGetSetList(const AbstractMetaClassCPtr &metaClass); - void writeHashFunction(TextStream &s, const GeneratorContext &context) const; + static bool hasHashFunction(const AbstractMetaClassCPtr &c); + static void writeHashFunction(TextStream &s, const GeneratorContext &context); /// Write default implementations for sequence protocol void writeDefaultSequenceMethods(TextStream &s, const GeneratorContext &context) const; @@ -510,23 +530,36 @@ private: static void writeIndexError(TextStream &s, const QString &errorMsg, ErrorReturn errorReturn); - QString writeReprFunction(TextStream &s, const GeneratorContext &context, - uint indirections) const; - - BoolCastFunctionOptional boolCast(const AbstractMetaClass *metaClass) const; - bool hasBoolCast(const AbstractMetaClass *metaClass) const + static QString writeReprFunctionHeader(TextStream &s, const GeneratorContext &context); + static QString writeReprFunction(TextStream &s, + const GeneratorContext &context, + uint indirections); + static QString writeSmartPointerReprFunction(TextStream &s, + const GeneratorContext &context); + static QString writeSmartPointerDirFunction(TextStream &s, + TextStream &definitionStream, + TextStream &signatureStream, + const GeneratorContext &context); + static void writeReprFunctionFooter(TextStream &s); + static void writePyMethodDefs(TextStream &s, const QString &className, + const QString &methodsDefinitions); + + void writeModuleCodeSnips(TextStream &s, const CodeSnipList &codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language) const; + + static bool hasBoolCast(const AbstractMetaClassCPtr &metaClass) { return boolCast(metaClass).has_value(); } - std::optional<AbstractMetaType> - findSmartPointerInstantiation(const SmartPointerTypeEntryCPtr &pointer, - const TypeEntryCPtr &pointee) const; void clearTpFuncs(); + static QString chopType(QString s); QHash<QString, QString> m_tpFuncs; - - static const char *PYTHON_TO_CPPCONVERSION_STRUCT; + QHash<QString, QString> m_nbFuncs; }; Q_DECLARE_OPERATORS_FOR_FLAGS(CppGenerator::CppSelfDefinitionFlags) +TextStream &operator<<(TextStream &s, CppGenerator::ErrorReturn r); + #endif // CPPGENERATOR_H diff --git a/sources/shiboken6/generator/shiboken/cppgenerator_container.cpp b/sources/shiboken6/generator/shiboken/cppgenerator_container.cpp index 38cb5b3c1..00e0cabea 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator_container.cpp +++ b/sources/shiboken6/generator/shiboken/cppgenerator_container.cpp @@ -2,11 +2,13 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "cppgenerator.h" +#include "generatorstrings.h" #include <abstractmetalang.h> #include "apiextractorresult.h" #include "ctypenames.h" #include "containertypeentry.h" #include "textstream.h" +#include "typedatabase.h" #include <QtCore/QDebug> @@ -53,23 +55,37 @@ static void writeSlot(TextStream &s, const QString &privateObjType, // Write creation function from C++ reference, used by field accessors // and getters which are within extern "C" + +enum ContainerCreationFlag +{ + None = 0, + Const = 0x1, + Allocate = 0x2 +}; + +Q_DECLARE_FLAGS(ContainerCreationFlags, ContainerCreationFlag) +Q_DECLARE_OPERATORS_FOR_FLAGS(ContainerCreationFlags) + static void writeContainerCreationFunc(TextStream &s, const QString &funcName, const QString &typeFName, const QString &containerSignature, - bool isConst = false) + ContainerCreationFlags flags = {}) { // creation function from C++ reference, used by field accessors // which are within extern "C" s << "extern \"C\" PyObject *" << funcName << '('; - if (isConst) + if (flags.testFlag(ContainerCreationFlag::Const)) s << "const "; s << containerSignature << "* ct)\n{\n" << indent << "auto *container = PyObject_New(ShibokenContainer, " << typeFName << "());\n" << "auto *d = new ShibokenSequenceContainerPrivate<" << containerSignature << ">();\n"; - if (isConst) { + if (flags.testFlag(ContainerCreationFlag::Allocate)) { + s << "d->m_list = new " << containerSignature << "(*ct);\n" + << "d->m_ownsList = true;\n"; + } else if (flags.testFlag(ContainerCreationFlag::Const)) { s << "d->m_list = const_cast<" << containerSignature << " *>(ct);\n" << "d->m_const = true;\n"; } else { @@ -80,34 +96,22 @@ static void writeContainerCreationFunc(TextStream &s, << "}\n\n"; } -// Generate code for a type wrapping a C++ container instantiation -CppGenerator::OpaqueContainerData - CppGenerator::writeOpaqueContainerConverterFunctions(TextStream &s, - const AbstractMetaType &containerType) const +// Generate template specialization of value converter helper +void CppGenerator::writeOpaqueContainerValueConverter(TextStream &s, + const AbstractMetaType &valueType) const { - OpaqueContainerData result; - const auto &valueType = containerType.instantiations().constFirst(); - const auto containerTypeEntry = qSharedPointerCast<const ContainerTypeEntry>(containerType.typeEntry()); - result.name = containerTypeEntry->opaqueContainerName(valueType.typeEntry()->name()); - - const auto cppSignature = containerType.cppSignature(); - s << "\n// Binding for " << cppSignature << "\n\n"; - // Generate template specialization of value converter helper unless it is already there - const QString pyArg = u"pyArg"_s; - const QString cppArg = u"cppArg"_s; - const QString valueTypeName = valueType.cppSignature(); const QString checkFunction = cpythonCheckFunction(valueType); s << "template <>\nstruct ShibokenContainerValueConverter<" << valueTypeName << ">\n{\n"; // Type check - s << indent << "static bool checkValue(PyObject *" << pyArg << ")\n{\n" + s << indent << "static bool checkValue(PyObject *" << PYTHON_ARG << ")\n{\n" << indent << "return " << checkFunction; if (!checkFunction.contains(u'(')) s << '('; - s << pyArg << ");\n" + s << PYTHON_ARG << ");\n" << outdent << "}\n\n"; // C++ to Python @@ -119,42 +123,68 @@ CppGenerator::OpaqueContainerData s << valueTypeName << ' '; if (passByConstRef) s << '&'; - s << cppArg << ")\n{\n" << indent << "return "; - writeToPythonConversion(s, valueType, nullptr, cppArg); + s << CPP_ARG << ")\n{\n" << indent << "return "; + writeToPythonConversion(s, valueType, nullptr, CPP_ARG); s << ";\n" << outdent << "}\n\n"; // Python to C++ s << "static std::optional<" << valueTypeName << "> convertValueToCpp(PyObject *" - << pyArg << ")\n{\n" << indent; + << PYTHON_ARG << ")\n{\n" << indent; s << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR << ";\n" << "if (!("; - writeTypeCheck(s, valueType, pyArg), isNumber(valueType.typeEntry()); + writeTypeCheck(s, valueType, PYTHON_ARG), isNumber(valueType.typeEntry()); s << ")) {\n" << indent << "Shiboken::Errors::setWrongContainerType();\n" << "return {};\n" << outdent << "}\n"; - writePythonToCppTypeConversion(s, valueType, pyArg, cppArg, nullptr, {}); - s << "return " << cppArg << ";\n" << outdent << "}\n" << outdent << "};\n\n"; + writePythonToCppTypeConversion(s, valueType, PYTHON_ARG, CPP_ARG, nullptr, {}); + s << "return " << CPP_ARG << ";\n" << outdent << "}\n" << outdent << "};\n\n"; +} + +// Generate code for a type wrapping a C++ container instantiation +CppGenerator::OpaqueContainerData + CppGenerator::writeOpaqueContainerConverterFunctions(TextStream &s, + const AbstractMetaType &containerType, + QSet<AbstractMetaType> *valueTypes) const +{ + OpaqueContainerData result; + const auto &valueType = containerType.instantiations().constFirst(); + const auto containerTypeEntry = std::static_pointer_cast<const ContainerTypeEntry>(containerType.typeEntry()); + result.name = + containerTypeEntry->opaqueContainerName(containerType.instantiationCppSignatures()); + + const auto cppSignature = containerType.cppSignature(); + s << "\n// Binding for " << cppSignature << "\n\n"; + + if (!valueTypes->contains(valueType)) { + valueTypes->insert(valueType); + writeOpaqueContainerValueConverter(s, valueType); + } const QString privateObjType = u"ShibokenSequenceContainerPrivate<"_s + cppSignature + u'>'; // methods - const bool isStdVector = containerType.name() == u"std::vector"; + const QString &containerName = containerType.name(); + const bool isStdVector = containerName == u"std::vector"; + const auto kind = containerTypeEntry->containerKind(); + const bool isFixed = kind == ContainerTypeEntry::SpanContainer || containerName == u"std::array"; const QString methods = result.name + u"_methods"_s; s << "static PyMethodDef " << methods << "[] = {\n" << indent; - writeMethod(s, privateObjType, "push_back"); - writeMethod(s, privateObjType, "push_back", "append"); // Qt convention - writeNoArgsMethod(s, privateObjType, "clear"); - writeNoArgsMethod(s, privateObjType, "pop_back"); - writeNoArgsMethod(s, privateObjType, "pop_back", "removeLast"); // Qt convention - if (!isStdVector) { - writeMethod(s, privateObjType, "push_front"); - writeMethod(s, privateObjType, "push_front", "prepend"); // Qt convention - writeNoArgsMethod(s, privateObjType, "pop_front"); - writeMethod(s, privateObjType, "pop_front", "removeFirst"); // Qt convention + if (!isFixed) { + writeMethod(s, privateObjType, "push_back"); + writeMethod(s, privateObjType, "push_back", "append"); // Qt convention + writeNoArgsMethod(s, privateObjType, "clear"); + writeNoArgsMethod(s, privateObjType, "pop_back"); + writeNoArgsMethod(s, privateObjType, "pop_back", "removeLast"); // Qt convention + if (!isStdVector) { + writeMethod(s, privateObjType, "push_front"); + writeMethod(s, privateObjType, "push_front", "prepend"); // Qt convention + writeNoArgsMethod(s, privateObjType, "pop_front"); + writeMethod(s, privateObjType, "pop_front", "removeFirst"); // Qt convention + } + writeMethod(s, privateObjType, "reserve"); // SFINAE'd out for list + writeNoArgsMethod(s, privateObjType, "capacity"); } - writeMethod(s, privateObjType, "reserve"); - writeNoArgsMethod(s, privateObjType, "capacity"); writeNoArgsMethod(s, privateObjType, "data"); writeNoArgsMethod(s, privateObjType, "constData"); s << "{nullptr, nullptr, 0, nullptr} // Sentinel\n" @@ -164,7 +194,8 @@ CppGenerator::OpaqueContainerData const QString slotsList = result.name + u"_slots"_s; s << "static PyType_Slot " << slotsList << "[] = {\n" << indent; writeSlot(s, privateObjType, "Py_tp_init", "tpInit"); - writeSlot(s, privateObjType, "Py_tp_new", "tpNew"); + const auto *tpNew = containerTypeEntry->viewOn() == nullptr ? "tpNew" : "tpNewInvalid"; + writeSlot(s, privateObjType, "Py_tp_new", tpNew); writeSlot(s, privateObjType, "Py_tp_free", "tpFree"); writeSlot(s, "Py_tp_dealloc", "Sbk_object_dealloc"); // FIXME? writeSlot(s, "Py_tp_methods", methods.toUtf8().constData()); @@ -175,7 +206,8 @@ CppGenerator::OpaqueContainerData // spec const QString specName = result.name + u"_spec"_s; - const QString name = moduleName() + u'.' + result.name; + const QString name = TypeDatabase::instance()->defaultPackageName() + + u'.' + result.name; s << "static PyType_Spec " << specName << " = {\n" << indent << "\"" << name.count(u'.') << ':' << name << "\",\n" << "sizeof(ShibokenContainer),\n0,\nPy_TPFLAGS_DEFAULT,\n" @@ -186,7 +218,8 @@ CppGenerator::OpaqueContainerData s << "static inline PyTypeObject *" << typeCreationFName << "()\n{\n" << indent << "auto *result = reinterpret_cast<PyTypeObject *>(SbkType_FromSpec(&" << specName << "));\nPy_INCREF(Py_True);\n" - << "PyDict_SetItem(result->tp_dict, " + << "Shiboken::AutoDecRef tpDict(PepType_GetDict(result));\n" + << "PyDict_SetItem(tpDict.object(), " "Shiboken::PyMagicName::opaque_container(), Py_True);\n" << "return result;\n" << outdent << "}\n\n"; @@ -197,32 +230,37 @@ CppGenerator::OpaqueContainerData << "();\nreturn type;\n" << outdent << "}\n\n"; // creation functions from C++ references + ContainerCreationFlags flags; + if (kind == ContainerTypeEntry::SpanContainer) + flags.setFlag(ContainerCreationFlag::Allocate); + writeContainerCreationFunc(s, u"create"_s + result.name, typeFName, - containerType.cppSignature()); + containerType.cppSignature(), flags); + flags.setFlag(ContainerCreationFlag::Const); writeContainerCreationFunc(s, u"createConst"_s + result.name, typeFName, - containerType.cppSignature(), true); + containerType.cppSignature(), flags); // Check function result.checkFunctionName = result.name + u"_Check"_s; - s << "extern \"C\" int " << result.checkFunctionName << "(PyObject *" << pyArg - << ")\n{\n" << indent << "return " << pyArg << " != nullptr && " - << pyArg << " != Py_None && " << pyArg << "->ob_type == " + s << "extern \"C\" int " << result.checkFunctionName << "(PyObject *" << PYTHON_ARG + << ")\n{\n" << indent << "return " << PYTHON_ARG << " != nullptr && " + << PYTHON_ARG << " != Py_None && " << PYTHON_ARG << "->ob_type == " << typeFName << "();\n" << outdent << "}\n\n"; // SBK converter Python to C++ result.pythonToConverterFunctionName = u"PythonToCpp"_s + result.name; s << "extern \"C\" void " << result.pythonToConverterFunctionName - << "(PyObject *" << pyArg << ", void *cppOut)\n{\n" << indent + << "(PyObject *" << PYTHON_ARG << ", void *cppOut)\n{\n" << indent << "auto *d = ShibokenSequenceContainerPrivate<" << cppSignature - << ">::get(" << pyArg << ");\n" + << ">::get(" << PYTHON_ARG << ");\n" << "*reinterpret_cast<" << cppSignature << "**>(cppOut) = d->m_list;\n" << outdent << "}\n\n"; // SBK check function for converting Python to C++ that returns the converter result.converterCheckFunctionName = u"is"_s + result.name + u"PythonToCppConvertible"_s; s << "extern \"C\" PythonToCppFunc " << result.converterCheckFunctionName - << "(PyObject *" << pyArg << ")\n{\n" << indent << "if (" - << result.checkFunctionName << '(' << pyArg << "))\n" << indent + << "(PyObject *" << PYTHON_ARG << ")\n{\n" << indent << "if (" + << result.checkFunctionName << '(' << PYTHON_ARG << "))\n" << indent << "return " << result.pythonToConverterFunctionName << ";\n" << outdent << "return {};\n" << outdent << "}\n\n"; diff --git a/sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp b/sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp new file mode 100644 index 000000000..44b76f181 --- /dev/null +++ b/sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp @@ -0,0 +1,476 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "cppgenerator.h" +#include "generatorstrings.h" +#include "generatorcontext.h" +#include <apiextractorresult.h> +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <codesnip.h> +#include <exception.h> +#include <messages.h> +#include <textstream.h> +#include <overloaddata.h> +#include <smartpointertypeentry.h> + +#include <QtCore/QDebug> + +using namespace Qt::StringLiterals; + +static const char smartPtrComment[] = + "// Try to find the 'name' attribute, by retrieving the PyObject for " + "the corresponding C++ object held by the smart pointer.\n"; + +static QString smartPointerGetter(const GeneratorContext &context) +{ + const auto te = context.metaClass()->typeEntry(); + Q_ASSERT(te->isSmartPointer()); + return std::static_pointer_cast<const SmartPointerTypeEntry>(te)->getter(); +} + +struct callGetter +{ + explicit callGetter(const GeneratorContext &context) : m_context(context) {} + + const GeneratorContext &m_context; +}; + +TextStream &operator<<(TextStream &str, const callGetter &c) +{ + str << "PyObject_CallMethod(self, \"" << smartPointerGetter(c.m_context) << "\", 0)"; + return str; +} + +// Helpers to collect all smart pointer pointee base classes +static AbstractMetaClassCList + findSmartPointeeBaseClasses(const ApiExtractorResult &api, + const AbstractMetaType &smartPointerType) +{ + AbstractMetaClassCList result; + auto instantiationsTe = smartPointerType.instantiations().at(0).typeEntry(); + auto targetClass = AbstractMetaClass::findClass(api.classes(), instantiationsTe); + if (targetClass != nullptr) + result = targetClass->allTypeSystemAncestors(); + return result; +} + +using ComparisonOperatorList = QList<AbstractMetaFunction::ComparisonOperatorType>; + +// Return the available comparison operators for smart pointers +static ComparisonOperatorList smartPointeeComparisons(const GeneratorContext &context) +{ + Q_ASSERT(context.forSmartPointer()); + auto te = context.preciseType().instantiations().constFirst().typeEntry(); + if (isExtendedCppPrimitive(te)) { // Primitive pointee types have all + return {AbstractMetaFunction::OperatorEqual, + AbstractMetaFunction::OperatorNotEqual, + AbstractMetaFunction::OperatorLess, + AbstractMetaFunction::OperatorLessEqual, + AbstractMetaFunction::OperatorGreater, + AbstractMetaFunction::OperatorGreaterEqual}; + } + + const auto pointeeClass = context.pointeeClass(); + if (!pointeeClass) + return {}; + + ComparisonOperatorList result; + const auto &comparisons = + pointeeClass->operatorOverloads(OperatorQueryOption::SymmetricalComparisonOp); + for (const auto &f : comparisons) { + const auto ct = f->comparisonOperatorType().value(); + if (!result.contains(ct)) + result.append(ct); + } + return result; +} + +static bool hasParameterPredicate(const AbstractMetaFunctionCPtr &f) +{ + return !f->arguments().isEmpty(); +} + +void CppGenerator::generateSmartPointerClass(TextStream &s, const GeneratorContext &classContext) +{ + s.setLanguage(TextStream::Language::Cpp); + AbstractMetaClassCPtr metaClass = classContext.metaClass(); + const auto typeEntry = std::static_pointer_cast<const SmartPointerTypeEntry>(metaClass->typeEntry()); + const bool hasPointeeClass = classContext.pointeeClass() != nullptr; + const auto smartPointerType = typeEntry->smartPointerType(); + const bool isValueHandle = smartPointerType ==TypeSystem::SmartPointerType::ValueHandle; + + IncludeGroup includes{u"Extra includes"_s, typeEntry->extraIncludes()}; + if (hasPointeeClass) + includes.append(classContext.pointeeClass()->typeEntry()->include()); + includes.includes.append({Include::IncludePath, u"sbksmartpointer.h"_s}); + generateIncludes(s, classContext, {includes}); + + s << '\n'; + + // class inject-code native/beginning + if (!typeEntry->codeSnips().isEmpty()) { + writeClassCodeSnips(s, typeEntry->codeSnips(), + TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode, + classContext); + s << '\n'; + } + + StringStream smd(TextStream::Language::Cpp); + StringStream md(TextStream::Language::Cpp); + StringStream signatureStream(TextStream::Language::Cpp); + + s << openTargetExternC; + + const auto &functionGroups = getFunctionGroups(metaClass); + + // Skip all public methods of the smart pointer except for the special + // methods declared in the type entry. + + auto ctors = metaClass->queryFunctions(FunctionQueryOption::Constructors); + if (!hasPointeeClass && !isValueHandle) { // Cannot generate "int*" + auto end = std::remove_if(ctors.begin(), ctors.end(), hasParameterPredicate); + ctors.erase(end, ctors.end()); + } + + if (!ctors.isEmpty()) { + OverloadData overloadData(ctors, api()); + writeConstructorWrapper(s, overloadData, classContext); + writeSignatureInfo(signatureStream, overloadData); + } + + if (!typeEntry->resetMethod().isEmpty()) { + auto it = functionGroups.constFind(typeEntry->resetMethod()); + if (it == functionGroups.cend()) + throw Exception(msgCannotFindSmartPointerMethod(typeEntry, typeEntry->resetMethod())); + AbstractMetaFunctionCList resets = it.value(); + if (!hasPointeeClass && !isValueHandle) { // Cannot generate "int*" + auto end = std::remove_if(resets.begin(), resets.end(), hasParameterPredicate); + resets.erase(end, resets.end()); + } + if (!resets.isEmpty()) + writeMethodWrapper(s, md, signatureStream, resets, classContext); + } + + auto it = functionGroups.constFind(typeEntry->getter()); + if (it == functionGroups.cend() || it.value().size() != 1) + throw Exception(msgCannotFindSmartPointerGetter(typeEntry)); + + writeMethodWrapper(s, md, signatureStream, it.value(), classContext); + + QStringList optionalMethods; + if (!typeEntry->refCountMethodName().isEmpty()) + optionalMethods.append(typeEntry->refCountMethodName()); + const QString valueCheckMethod = typeEntry->valueCheckMethod(); + if (!valueCheckMethod.isEmpty() && !valueCheckMethod.startsWith(u"operator")) + optionalMethods.append(valueCheckMethod); + if (!typeEntry->nullCheckMethod().isEmpty()) + optionalMethods.append(typeEntry->nullCheckMethod()); + + for (const QString &optionalMethod : optionalMethods) { + auto it = functionGroups.constFind(optionalMethod); + if (it == functionGroups.cend() || it.value().size() != 1) + throw Exception(msgCannotFindSmartPointerMethod(typeEntry, optionalMethod)); + writeMethodWrapper(s, md, signatureStream, it.value(), classContext); + } + + writeCopyFunction(s, md, signatureStream, classContext); + writeSmartPointerDirFunction(s, md, signatureStream, classContext); + + const QString methodsDefinitions = md.toString(); + const QString singleMethodDefinitions = smd.toString(); + + const QString className = chopType(cpythonTypeName(typeEntry)); + + // Write single method definitions + s << singleMethodDefinitions; + + // Write methods definition + writePyMethodDefs(s, className, methodsDefinitions); + + // Write tp_s/getattro function + const auto boolCastOpt = boolCast(metaClass); + writeSmartPointerGetattroFunction(s, classContext, boolCastOpt); + writeSmartPointerSetattroFunction(s, classContext); + + if (boolCastOpt.has_value()) + writeNbBoolFunction(classContext, boolCastOpt.value(), s); + + if (smartPointerType == TypeSystem::SmartPointerType::Shared) + writeSmartPointerRichCompareFunction(s, classContext); + + s << closeExternC; + + if (hasHashFunction(metaClass)) + writeHashFunction(s, classContext); + + // Write tp_traverse and tp_clear functions. + writeTpTraverseFunction(s, metaClass); + writeTpClearFunction(s, metaClass); + + writeClassDefinition(s, metaClass, classContext); + + s << '\n'; + + writeConverterFunctions(s, metaClass, classContext); + // Implicit smart pointers conversions + writeSmartPointerConverterFunctions(s, classContext.preciseType()); + writeClassRegister(s, metaClass, classContext, signatureStream); + + // class inject-code native/end + if (!typeEntry->codeSnips().isEmpty()) { + writeClassCodeSnips(s, typeEntry->codeSnips(), + TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode, + classContext); + s << '\n'; + } +} + +void CppGenerator::writeSmartPointerConverterFunctions(TextStream &s, + const AbstractMetaType &smartPointerType) const +{ + const auto baseClasses = findSmartPointeeBaseClasses(api(), smartPointerType); + if (baseClasses.isEmpty()) + return; + + auto smartPointerTypeEntry = + std::static_pointer_cast<const SmartPointerTypeEntry>(smartPointerType.typeEntry()); + + // TODO: Missing conversion to smart pointer pointer type: + + s << "// Register smartpointer conversion for all derived classes\n"; + for (const auto &base : baseClasses) { + auto baseTe = base->typeEntry(); + if (smartPointerTypeEntry->matchesInstantiation(baseTe)) { + if (auto opt = api().findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) { + const auto &smartTargetType = opt.value().type; + s << "// SmartPointer derived class: " + << smartTargetType.cppSignature() << "\n"; + writePythonToCppConversionFunctions(s, smartPointerType, + smartTargetType, {}, {}, {}); + } + } + } +} + +void CppGenerator::writeSmartPointerCppSelfConversion(TextStream &s, + const GeneratorContext &context) +{ + Q_ASSERT(context.forSmartPointer()); + s << cpythonWrapperCPtr(context.preciseType(), u"self"_s); +} + +void CppGenerator::writeSmartPointerCppSelfDefinition(TextStream &s, + const GeneratorContext &context, + ErrorReturn errorReturn, + CppSelfDefinitionFlags flags) +{ + Q_ASSERT(context.forSmartPointer()); + writeInvalidPyObjectCheck(s, u"self"_s, errorReturn); + writeCppSelfVarDef(s, flags); + writeSmartPointerCppSelfConversion(s, context); + s << ";\n"; +} + +void CppGenerator::writeSmartPointerConverterInitialization(TextStream &s, + const AbstractMetaType &type) const +{ + const QByteArray cppSignature = type.cppSignature().toUtf8(); + auto writeConversionRegister = [&s](const AbstractMetaType &sourceType, + const QString &targetTypeName, + const QString &targetConverter) + { + const QString sourceTypeName = fixedCppTypeName(sourceType); + const QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); + const QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName); + + writeAddPythonToCppConversion(s, targetConverter, toCpp, isConv); + }; + + const auto classes = findSmartPointeeBaseClasses(api(), type); + if (classes.isEmpty()) + return; + + auto smartPointerTypeEntry = std::static_pointer_cast<const SmartPointerTypeEntry>(type.typeEntry()); + + s << "// Register SmartPointer converter for type '" << cppSignature << "'." << '\n' + << "///////////////////////////////////////////////////////////////////////////////////////\n\n"; + + for (const auto &base : classes) { + auto baseTe = base->typeEntry(); + if (auto opt = api().findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) { + const auto &smartTargetType = opt.value().type; + s << "// Convert to SmartPointer derived class: [" + << smartTargetType.cppSignature() << "]\n"; + const QString converter = u"Shiboken::Conversions::getConverter(\""_s + + smartTargetType.cppSignature() + u"\")"_s; + writeConversionRegister(type, fixedCppTypeName(smartTargetType), converter); + } else { + s << "// Class not found:" << type.instantiations().at(0).cppSignature(); + } + } + + s << "///////////////////////////////////////////////////////////////////////////////////////" << '\n' << '\n'; +} + +void CppGenerator::writeSmartPointerRichCompareFunction(TextStream &s, + const GeneratorContext &context) const +{ + static const char selfPointeeVar[] = "cppSelfPointee"; + static const char cppArg0PointeeVar[] = "cppArg0Pointee"; + + const auto metaClass = context.metaClass(); + QString baseName = cpythonBaseName(metaClass); + writeRichCompareFunctionHeader(s, baseName, context); + + s << "if ("; + writeTypeCheck(s, context.preciseType(), PYTHON_ARG); + s << ") {\n" << indent; + writeArgumentConversion(s, context.preciseType(), CPP_ARG0, + PYTHON_ARG, ErrorReturn::Default, metaClass); + + const auto te = context.preciseType().typeEntry(); + Q_ASSERT(te->isSmartPointer()); + const auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(te); + + s << "const auto *" << selfPointeeVar << " = " << CPP_SELF_VAR + << '.' << ste->getter() << "();\n"; + s << "const auto *" << cppArg0PointeeVar << " = " << CPP_ARG0 + << '.' << ste->getter() << "();\n"; + + // If we have an object without any comparisons, only generate a simple + // equality check by pointee address + auto availableOps = smartPointeeComparisons(context); + const bool comparePointeeAddressOnly = availableOps.isEmpty(); + if (comparePointeeAddressOnly) { + availableOps << AbstractMetaFunction::OperatorEqual + << AbstractMetaFunction::OperatorNotEqual; + } else { + // For value types with operators, we complain about nullptr + s << "if (" << selfPointeeVar << " == nullptr || " << cppArg0PointeeVar + << " == nullptr) {\n" << indent + << "PyErr_SetString(PyExc_NotImplementedError, \"nullptr passed to comparison.\");\n" + << ErrorReturn::Default << '\n' << outdent << "}\n"; + } + + s << "bool " << CPP_RETURN_VAR << "= false;\n" + << "switch (op) {\n"; + for (auto op : availableOps) { + s << "case " << AbstractMetaFunction::pythonRichCompareOpCode(op) << ":\n" + << indent << CPP_RETURN_VAR << " = "; + if (comparePointeeAddressOnly) { + s << selfPointeeVar << ' ' << AbstractMetaFunction::cppComparisonOperator(op) + << ' ' << cppArg0PointeeVar << ";\n"; + } else { + // Shortcut for equality: Check pointee address + if (op == AbstractMetaFunction::OperatorEqual + || op == AbstractMetaFunction::OperatorLessEqual + || op == AbstractMetaFunction::OperatorGreaterEqual) { + s << selfPointeeVar << " == " << cppArg0PointeeVar << " || "; + } + // Generate object's comparison + s << "*" << selfPointeeVar << ' ' + << AbstractMetaFunction::cppComparisonOperator(op) << " *" + << cppArg0PointeeVar << ";\n"; + } + s << "break;\n" << outdent; + + } + if (availableOps.size() < 6) { + s << "default:\n" << indent + << richCompareComment + << "return FallbackRichCompare(self, " << PYTHON_ARG << ", op);\n" << outdent; + } + s << "}\n" << PYTHON_RETURN_VAR << " = " << CPP_RETURN_VAR + << " ? Py_True : Py_False;\n" + << "Py_INCREF(" << PYTHON_RETURN_VAR << ");\n" + << outdent << "}\n" + << "return Shiboken::returnFromRichCompare(" << PYTHON_RETURN_VAR << ");\n" + << outdent << "}\n\n"; +} + +void CppGenerator::writeSmartPointerSetattroFunction(TextStream &s, + const GeneratorContext &context) +{ + Q_ASSERT(context.forSmartPointer()); + writeSetattroDefinition(s, context.metaClass()); + s << smartPtrComment + << "if (auto *rawObj = " << callGetter(context) << ") {\n" << indent + << "if (PyObject_HasAttr(rawObj, name) != 0)\n" << indent + << "return PyObject_GenericSetAttr(rawObj, name, value);\n" << outdent + << "Py_DECREF(rawObj);\n" << outdent + << "}\n"; + writeSetattroDefaultReturn(s); +} + +void CppGenerator::writeSmartPointerGetattroFunction(TextStream &s, + const GeneratorContext &context, + const BoolCastFunctionOptional &boolCast) +{ + Q_ASSERT(context.forSmartPointer()); + const auto metaClass = context.metaClass(); + writeGetattroDefinition(s, metaClass); + s << "PyObject *tmp = PyObject_GenericGetAttr(self, name);\n" + << "if (tmp)\n" << indent << "return tmp;\n" << outdent + << "if (PyErr_ExceptionMatches(PyExc_AttributeError) == 0)\n" + << indent << "return nullptr;\n" << outdent + << "PyErr_Clear();\n"; + + if (boolCast.has_value()) { + writeSmartPointerCppSelfDefinition(s, context); + s << "if ("; + writeNbBoolExpression(s, boolCast.value(), true /* invert */); + s << ") {\n" << indent + << R"(PyTypeObject *tp = Py_TYPE(self); +PyErr_Format(PyExc_AttributeError, "Attempt to retrieve '%s' from null object '%s'.", + Shiboken::String::toCString(name), tp->tp_name); +return nullptr; +)" << outdent << "}\n"; + } + + // This generates the code which dispatches access to member functions + // and fields from the smart pointer to its pointee. + s << smartPtrComment + << "if (auto *rawObj = " << callGetter(context) << ") {\n" << indent + << "if (auto *attribute = PyObject_GetAttr(rawObj, name))\n" + << indent << "tmp = attribute;\n" << outdent + << "Py_DECREF(rawObj);\n" << outdent + << "}\n" + << "if (!tmp) {\n" << indent + << R"(PyTypeObject *tp = Py_TYPE(self); +PyErr_Format(PyExc_AttributeError, + "'%.50s' object has no attribute '%.400s'", + tp->tp_name, Shiboken::String::toCString(name)); +)" << outdent + << "}\n" + << "return tmp;\n" << outdent << "}\n\n"; +} + +QString CppGenerator::writeSmartPointerReprFunction(TextStream &s, + const GeneratorContext &context) +{ + const auto metaClass = context.metaClass(); + QString funcName = writeReprFunctionHeader(s, context); + s << "Shiboken::AutoDecRef pointee(" << callGetter(context) << ");\n" + << "return Shiboken::SmartPointer::repr(self, pointee);\n"; + writeReprFunctionFooter(s); + return funcName; +} + +QString CppGenerator::writeSmartPointerDirFunction(TextStream &s, TextStream &definitionStream, + TextStream &signatureStream, + const GeneratorContext &context) +{ + QString funcName = cpythonBaseName(context.metaClass()) + u"__dir__"_s; + + signatureStream << fullPythonClassName(context.metaClass()) << ".__dir__()\n"; + definitionStream << PyMethodDefEntry{u"__dir__"_s, funcName, {"METH_NOARGS"_ba}, {}} + << ",\n"; + + s << "extern \"C\"\n{\n" + << "static PyObject *" << funcName << "(PyObject *self)\n{\n" << indent + << "Shiboken::AutoDecRef pointee(" << callGetter(context) << ");\n" + << "return Shiboken::SmartPointer::dir(self, pointee);\n" + << outdent << "}\n} // extern C\n\n"; + return funcName; +} diff --git a/sources/shiboken6/generator/shiboken/ctypenames.h b/sources/shiboken6/generator/shiboken/ctypenames.h index 0444c99f2..f665b30ff 100644 --- a/sources/shiboken6/generator/shiboken/ctypenames.h +++ b/sources/shiboken6/generator/shiboken/ctypenames.h @@ -6,26 +6,26 @@ #include <QtCore/QString> -static inline QString boolT() { return QStringLiteral("bool"); } -static inline QString intT() { return QStringLiteral("int"); } -static inline QString unsignedT() { return QStringLiteral("unsigned"); } -static inline QString unsignedIntT() { return QStringLiteral("unsigned int"); } -static inline QString longT() { return QStringLiteral("long"); } -static inline QString unsignedLongT() { return QStringLiteral("unsigned long"); } -static inline QString shortT() { return QStringLiteral("short"); } -static inline QString unsignedShortT() { return QStringLiteral("unsigned short"); } -static inline QString unsignedCharT() { return QStringLiteral("unsigned char"); } -static inline QString longLongT() { return QStringLiteral("long long"); } -static inline QString unsignedLongLongT() { return QStringLiteral("unsigned long long"); } -static inline QString charT() { return QStringLiteral("char"); } -static inline QString floatT() { return QStringLiteral("float"); } -static inline QString doubleT() { return QStringLiteral("double"); } -static inline QString constCharPtrT() { return QStringLiteral("const char*"); } +constexpr auto boolT = QLatin1StringView("bool"); +constexpr auto intT = QLatin1StringView("int"); +constexpr auto unsignedT = QLatin1StringView("unsigned"); +constexpr auto unsignedIntT = QLatin1StringView("unsigned int"); +constexpr auto longT = QLatin1StringView("long"); +constexpr auto unsignedLongT = QLatin1StringView("unsigned long"); +constexpr auto shortT = QLatin1StringView("short"); +constexpr auto unsignedShortT = QLatin1StringView("unsigned short"); +constexpr auto unsignedCharT = QLatin1StringView("unsigned char"); +constexpr auto longLongT = QLatin1StringView("long long"); +constexpr auto unsignedLongLongT = QLatin1StringView("unsigned long long"); +constexpr auto charT = QLatin1StringView("char"); +constexpr auto floatT = QLatin1StringView("float"); +constexpr auto doubleT = QLatin1StringView("double"); +constexpr auto constCharPtrT = QLatin1StringView("const char*"); -static inline QString qByteArrayT() { return QStringLiteral("QByteArray"); } -static inline QString qMetaObjectT() { return QStringLiteral("QMetaObject"); } -static inline QString qObjectT() { return QStringLiteral("QObject"); } -static inline QString qStringT() { return QStringLiteral("QString"); } -static inline QString qVariantT() { return QStringLiteral("QVariant"); } +constexpr auto qByteArrayT = QLatin1StringView("QByteArray"); +constexpr auto qMetaObjectT = QLatin1StringView("QMetaObject"); +constexpr auto qObjectT = QLatin1StringView("QObject"); +constexpr auto qStringT = QLatin1StringView("QString"); +constexpr auto qVariantT = QLatin1StringView("QVariant"); #endif // CTYPENAMES_H diff --git a/sources/shiboken6/generator/shiboken/generatorstrings.h b/sources/shiboken6/generator/shiboken/generatorstrings.h new file mode 100644 index 000000000..9ce91e599 --- /dev/null +++ b/sources/shiboken6/generator/shiboken/generatorstrings.h @@ -0,0 +1,39 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef GENERATORSTRINGS_H +#define GENERATORSTRINGS_H + +#include <QtCore/QString> + +QString CPP_ARG_N(int i); +QString CPP_ARG_REMOVED(int i); + +constexpr auto CPP_RETURN_VAR = QLatin1StringView("cppResult"); +constexpr auto CPP_SELF_VAR = QLatin1StringView("cppSelf"); +constexpr auto CPP_ARG = QLatin1StringView("cppArg"); +constexpr auto NULL_PTR = QLatin1StringView("nullptr"); +constexpr auto PYTHON_ARG = QLatin1StringView("pyArg"); +constexpr auto PYTHON_ARGS = QLatin1StringView("pyArgs"); +constexpr auto PYTHON_OVERRIDE_VAR = QLatin1StringView("pyOverride"); +constexpr auto PYTHON_RETURN_VAR = QLatin1StringView("pyResult"); +constexpr auto PYTHON_SELF_VAR = QLatin1StringView("self"); +constexpr auto PYTHON_TO_CPP_VAR = QLatin1StringView("pythonToCpp"); + +constexpr auto CONV_RULE_OUT_VAR_SUFFIX = QLatin1StringView("_out"); +constexpr auto BEGIN_ALLOW_THREADS + = QLatin1StringView("PyThreadState *_save = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS"); +constexpr auto END_ALLOW_THREADS + = QLatin1StringView("PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS"); + +constexpr auto REPR_FUNCTION = QLatin1StringView("__repr__"); + +constexpr auto CPP_ARG0 = QLatin1StringView("cppArg0"); + +extern const char *const METHOD_DEF_SENTINEL; +extern const char *const PYTHON_TO_CPPCONVERSION_STRUCT; +extern const char *const openTargetExternC; +extern const char *const closeExternC; +extern const char *const richCompareComment; + +#endif // GENERATORSTRINGS_H diff --git a/sources/shiboken6/generator/shiboken/headergenerator.cpp b/sources/shiboken6/generator/shiboken/headergenerator.cpp index d8e745f3e..7cec9c38e 100644 --- a/sources/shiboken6/generator/shiboken/headergenerator.cpp +++ b/sources/shiboken6/generator/shiboken/headergenerator.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "headergenerator.h" +#include "configurablescope.h" #include "generatorcontext.h" #include <apiextractorresult.h> #include <abstractmetaargument.h> @@ -12,6 +13,7 @@ #include <abstractmetalang_helpers.h> #include <codesnip.h> #include <clangparser/compilersupport.h> +#include <exception.h> #include <typedatabase.h> #include <reporthandler.h> #include <textstream.h> @@ -19,6 +21,7 @@ #include "containertypeentry.h" #include "enumtypeentry.h" #include "flagstypeentry.h" +#include <messages.h> #include "namespacetypeentry.h" #include "primitivetypeentry.h" #include "typedefentry.h" @@ -36,6 +39,22 @@ using namespace Qt::StringLiterals; +struct IndexValue +{ + QString name; // "SBK_..." + int value; + QString comment; +}; + +TextStream &operator<<(TextStream &s, const IndexValue &iv) +{ + s << " " << AlignedField(iv.name, 56) << " = " << iv.value << ','; + if (!iv.comment.isEmpty()) + s << " // " << iv.comment; + s << '\n'; + return s; +} + // PYSIDE-504: Handling the "protected hack" // The problem: Creating wrappers when the class has private destructors. // You can see an example on Windows in qclipboard_wrapper.h and others. @@ -62,23 +81,26 @@ static bool alwaysGenerateDestructorDeclaration() return clang::compiler() == Compiler::Msvc; } -QString HeaderGenerator::headerFileNameForContext(const GeneratorContext &context) -{ - return fileNameForContextHelper(context, u"_wrapper.h"_s); -} +const char *HeaderGenerator::protectedHackDefine = R"(// Workaround to access protected functions +#ifndef protected +# define protected public +#endif + +)"; QString HeaderGenerator::fileNameForContext(const GeneratorContext &context) const { return headerFileNameForContext(context); } -void HeaderGenerator::writeCopyCtor(TextStream &s, const AbstractMetaClass *metaClass) const +void HeaderGenerator::writeCopyCtor(TextStream &s, + const AbstractMetaClassCPtr &metaClass) { s << wrapperName(metaClass) << "(const " << metaClass->qualifiedCppName() << "& self) : " << metaClass->qualifiedCppName() << "(self)\n{\n}\n\n"; } -static void writeProtectedEnums(TextStream &s, const AbstractMetaClass *metaClass) +static void writeProtectedEnums(TextStream &s, const AbstractMetaClassCPtr &metaClass) { const QString name = metaClass->qualifiedCppName(); for (const auto &e : metaClass->enums()) { @@ -87,134 +109,178 @@ static void writeProtectedEnums(TextStream &s, const AbstractMetaClass *metaClas } } -void HeaderGenerator::generateClass(TextStream &s, const GeneratorContext &classContextIn) +void HeaderGenerator::generateClass(TextStream &s, const GeneratorContext &classContext) { - GeneratorContext classContext = classContextIn; - const AbstractMetaClass *metaClass = classContext.metaClass(); - m_inheritedOverloads.clear(); + const AbstractMetaClassCPtr metaClass = classContext.metaClass(); // write license comment s << licenseComment(); QString wrapperName = classContext.effectiveClassName(); - QString outerHeaderGuard = getFilteredCppSignatureString(wrapperName).toUpper(); - QString innerHeaderGuard; + QString outerHeaderGuard = getFilteredCppSignatureString(wrapperName); // Header s << "#ifndef SBK_" << outerHeaderGuard << "_H\n"; s << "#define SBK_" << outerHeaderGuard << "_H\n\n"; if (!avoidProtectedHack()) - s << "#define protected public\n\n"; + s << protectedHackDefine; - //Includes - auto typeEntry = metaClass->typeEntry(); - s << typeEntry->include() << '\n'; + // Includes + s << metaClass->typeEntry()->include() << '\n'; for (auto &inst : metaClass->templateBaseClassInstantiations()) s << inst.typeEntry()->include(); - if (classContext.useWrapper() && avoidProtectedHack()) { - const auto includeGroups = classIncludes(metaClass); - for( const auto &includeGroup : includeGroups) - s << includeGroup; + if (classContext.useWrapper()) + writeWrapperClass(s, wrapperName, classContext); + + s << "#endif // SBK_" << outerHeaderGuard << "_H\n\n"; +} + +void HeaderGenerator::writeWrapperClass(TextStream &s, + const QString &wrapperName, + const GeneratorContext &classContext) const +{ + const auto metaClass = classContext.metaClass(); + + if (avoidProtectedHack()) { + const auto includeGroups = classIncludes(metaClass); + for( const auto &includeGroup : includeGroups) + s << includeGroup; } - if (classContext.useWrapper() && usePySideExtensions() && metaClass->isQObject()) + if (usePySideExtensions() && isQObject(metaClass)) s << "namespace PySide { class DynamicQMetaObject; }\n\n"; - while (classContext.useWrapper()) { - if (!innerHeaderGuard.isEmpty()) { - s << "# ifndef SBK_" << innerHeaderGuard << "_H\n"; - s << "# define SBK_" << innerHeaderGuard << "_H\n\n"; - s << "// Inherited base class:\n"; + writeWrapperClassDeclaration(s, wrapperName, classContext); + + // PYSIDE-500: Use also includes for inherited wrapper classes other + // modules, because without the protected hack, we sometimes need to + // cast inherited wrappers. CppGenerator generates include statements for + // the classes of the current module. For other modules, we insert the + // declarations as recursive headers, since wrapper headers are not + // installed. This keeps the file structure as simple as before the + // enhanced inheritance. + if (avoidProtectedHack()) { + const auto &baseClasses = allBaseClasses(classContext.metaClass()); + for (const auto &baseClass : baseClasses) { + const auto gen = baseClass->typeEntry()->codeGeneration(); + if (gen == TypeEntry::GenerateForSubclass) { // other module + const auto baseContext = contextForClass(baseClass); + if (baseContext.useWrapper()) + writeInheritedWrapperClassDeclaration(s, baseContext); + } } + } +} - // Class - s << "class " << wrapperName - << " : public " << metaClass->qualifiedCppName() - << "\n{\npublic:\n" << indent; - - // Make protected enums accessible - if (avoidProtectedHack()) { - recurseClassHierarchy(metaClass, [&s] (const AbstractMetaClass *metaClass) { - writeProtectedEnums(s, metaClass); - return false; - }); - } +void HeaderGenerator::writeInheritedWrapperClassDeclaration(TextStream &s, + const GeneratorContext &classContext) const +{ + const QString wrapperName = classContext.effectiveClassName(); + const QString innerHeaderGuard = + getFilteredCppSignatureString(wrapperName).toUpper(); - if (avoidProtectedHack() && metaClass->hasProtectedFields()) { - s << "\n// Make protected fields accessible\n"; - const QString name = metaClass->qualifiedCppName(); - for (const auto &f : metaClass->fields()) { - if (f.isProtected()) - s << "using " << name << "::" << f.originalName() << ";\n"; - } - s << '\n'; - } + s << "# ifndef SBK_" << innerHeaderGuard << "_H\n" + << "# define SBK_" << innerHeaderGuard << "_H\n\n" + << "// Inherited base class:\n"; - int maxOverrides = 0; - for (const auto &func : metaClass->functions()) { - const auto generation = functionGeneration(func); - writeFunction(s, func, generation); - // PYSIDE-803: Build a boolean cache for unused overrides. - if (generation.testFlag(FunctionGenerationFlag::VirtualMethod)) - maxOverrides++; - } - if (!maxOverrides) - maxOverrides = 1; - - //destructor - // PYSIDE-504: When C++ 11 is used, then the destructor must always be declared. - if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor() - || alwaysGenerateDestructorDeclaration()) { - if (avoidProtectedHack() && metaClass->hasPrivateDestructor()) - s << "// C++11: need to declare (unimplemented) destructor because " - "the base class destructor is private.\n"; - s << '~' << wrapperName << "();\n"; - } + writeWrapperClassDeclaration(s, wrapperName, classContext); - writeClassCodeSnips(s, typeEntry->codeSnips(), - TypeSystem::CodeSnipPositionDeclaration, TypeSystem::NativeCode, - classContext); + s << "# endif // SBK_" << innerHeaderGuard << "_H\n\n"; +} - if ((!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) - && usePySideExtensions() && metaClass->isQObject()) { - s << outdent << "public:\n" << indent << -R"(int qt_metacall(QMetaObject::Call call, int id, void **args) override; +void HeaderGenerator::writeWrapperClassDeclaration(TextStream &s, + const QString &wrapperName, + const GeneratorContext &classContext) const +{ + const AbstractMetaClassCPtr metaClass = classContext.metaClass(); + const auto typeEntry = metaClass->typeEntry(); + InheritedOverloadSet inheritedOverloads; + + // Class + s << "class " << wrapperName + << " : public " << metaClass->qualifiedCppName() + << "\n{\npublic:\n" << indent + << wrapperName << "(const " << wrapperName << " &) = delete;\n" + << wrapperName << "& operator=(const " << wrapperName << " &) = delete;\n" + << wrapperName << '(' << wrapperName << " &&) = delete;\n" + << wrapperName << "& operator=(" << wrapperName << " &&) = delete;\n\n"; + + // Make protected enums accessible + if (avoidProtectedHack()) { + recurseClassHierarchy(metaClass, [&s] (const AbstractMetaClassCPtr &metaClass) { + writeProtectedEnums(s, metaClass); + return false; + }); + } + + if (avoidProtectedHack() && metaClass->hasProtectedFields()) { + s << "\n// Make protected fields accessible\n"; + const QString name = metaClass->qualifiedCppName(); + for (const auto &f : metaClass->fields()) { + if (f.isProtected()) + s << "using " << name << "::" << f.originalName() << ";\n"; + } + s << '\n'; + } + + int maxOverrides = 0; + for (const auto &func : metaClass->functions()) { + const auto generation = functionGeneration(func); + writeFunction(s, func, &inheritedOverloads, generation); + // PYSIDE-803: Build a boolean cache for unused overrides. + if (generation.testFlag(FunctionGenerationFlag::VirtualMethod)) + maxOverrides++; + } + if (!maxOverrides) + maxOverrides = 1; + + //destructor + // PYSIDE-504: When C++ 11 is used, then the destructor must always be declared. + if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor() + || alwaysGenerateDestructorDeclaration()) { + if (avoidProtectedHack() && metaClass->hasPrivateDestructor()) + s << "// C++11: need to declare (unimplemented) destructor because " + "the base class destructor is private.\n"; + s << '~' << wrapperName << "()"; + if (metaClass->hasVirtualDestructor()) + s << " override"; + s << ";\n"; + } + + writeClassCodeSnips(s, typeEntry->codeSnips(), + TypeSystem::CodeSnipPositionDeclaration, TypeSystem::NativeCode, + classContext); + + if (shouldGenerateMetaObjectFunctions(metaClass)) { + s << R"( +const ::QMetaObject * metaObject() const override; +int qt_metacall(QMetaObject::Call call, int id, void **args) override; void *qt_metacast(const char *_clname) override; )"; - } + } - if (!m_inheritedOverloads.isEmpty()) { - s << "// Inherited overloads, because the using keyword sux\n"; - for (const auto &func : std::as_const(m_inheritedOverloads)) - writeMemberFunctionWrapper(s, func); - m_inheritedOverloads.clear(); - } + if (!inheritedOverloads.isEmpty()) { + s << "// Inherited overloads, because the using keyword sux\n"; + for (const auto &func : std::as_const(inheritedOverloads)) + writeMemberFunctionWrapper(s, func); + } - if (usePySideExtensions()) - s << "static void pysideInitQtMetaTypes();\n"; + if (usePySideExtensions()) + s << "static void pysideInitQtMetaTypes();\n"; - s << "void resetPyMethodCache();\n" - << outdent << "private:\n" << indent - << "mutable bool m_PyMethodCache[" << maxOverrides << "];\n" - << outdent << "};\n\n"; - if (!innerHeaderGuard.isEmpty()) - s << "# endif // SBK_" << innerHeaderGuard << "_H\n\n"; + s << "void resetPyMethodCache();\n" + << outdent << "private:\n" << indent; - // PYSIDE-500: Use also includes for inherited wrapper classes, because - // without the protected hack, we sometimes need to cast inherited wrappers. - // But we don't use multiple include files. Instead, they are inserted as recursive - // headers. This keeps the file structure as simple as before the enhanced inheritance. - metaClass = metaClass->baseClass(); - if (!metaClass || !avoidProtectedHack()) - break; - classContext = contextForClass(metaClass); - wrapperName = classContext.effectiveClassName(); - innerHeaderGuard = getFilteredCppSignatureString(wrapperName).toUpper(); + if (!metaClass->userAddedPythonOverrides().isEmpty()) { + for (const auto &f : metaClass->userAddedPythonOverrides()) + s << functionSignature(f, {}, {}, Generator::OriginalTypeDescription) << ";\n"; + s << '\n'; } - s << "#endif // SBK_" << outerHeaderGuard << "_H\n\n"; + s << "mutable bool m_PyMethodCache[" << maxOverrides << "];\n" + << outdent << "};\n\n"; } // Write an inline wrapper around a function @@ -224,8 +290,6 @@ void HeaderGenerator::writeMemberFunctionWrapper(TextStream &s, { Q_ASSERT(!func->isConstructor() && !func->isOperatorOverload()); s << "inline "; - if (func->isStatic()) - s << "static "; s << functionSignature(func, {}, postfix, Generator::OriginalTypeDescription) << " { "; if (!func->isVoid()) @@ -248,10 +312,10 @@ void HeaderGenerator::writeMemberFunctionWrapper(TextStream &s, const auto &type = arg.type(); TypeEntryCPtr enumTypeEntry; if (type.isFlags()) - enumTypeEntry = qSharedPointerCast<const FlagsTypeEntry>(type.typeEntry())->originator(); + enumTypeEntry = std::static_pointer_cast<const FlagsTypeEntry>(type.typeEntry())->originator(); else if (type.isEnum()) enumTypeEntry = type.typeEntry(); - if (!enumTypeEntry.isNull()) { + if (enumTypeEntry) { s << type.cppSignature() << '(' << arg.name() << ')'; } else if (type.passByValue() && type.isUniquePointer()) { s << stdMove(arg.name()); @@ -263,7 +327,8 @@ void HeaderGenerator::writeMemberFunctionWrapper(TextStream &s, } void HeaderGenerator::writeFunction(TextStream &s, const AbstractMetaFunctionCPtr &func, - FunctionGeneration generation) + InheritedOverloadSet *inheritedOverloads, + FunctionGeneration generation) const { // do not write copy ctors here. @@ -283,7 +348,7 @@ void HeaderGenerator::writeFunction(TextStream &s, const AbstractMetaFunctionCPt } const bool isVirtual = generation.testFlag(FunctionGenerationFlag::VirtualMethod); - if (isVirtual || generation.testFlag(FunctionGenerationFlag::QMetaObjectMethod)) { + if (isVirtual) { s << functionSignature(func, {}, {}, Generator::OriginalTypeDescription) << " override;\n"; } @@ -298,7 +363,7 @@ void HeaderGenerator::writeFunction(TextStream &s, const AbstractMetaFunctionCPt && !f->isAbstract() && !f->isStatic() && f->name() == func->name()) { - m_inheritedOverloads << f; + inheritedOverloads->insert(f); } } @@ -308,28 +373,14 @@ void HeaderGenerator::writeFunction(TextStream &s, const AbstractMetaFunctionCPt } } -static void _writeTypeIndexValue(TextStream &s, const QString &variableName, - int typeIndex) -{ - s << " " << AlignedField(variableName, 56) << " = " << typeIndex; -} - -static inline void _writeTypeIndexValueLine(TextStream &s, - const QString &variableName, - int typeIndex) -{ - _writeTypeIndexValue(s, variableName, typeIndex); - s << ",\n"; -} - // Find equivalent typedefs "using Foo=QList<int>", "using Bar=QList<int>" -static const AbstractMetaClass * +static AbstractMetaClassCPtr findEquivalentTemplateTypedef(const AbstractMetaClassCList &haystack, - const AbstractMetaClass *needle) + const AbstractMetaClassCPtr &needle) { - auto *templateBaseClass = needle->templateBaseClass(); + auto templateBaseClass = needle->templateBaseClass(); const auto &instantiations = needle->templateBaseClassInstantiations(); - for (auto *candidate : haystack) { + for (const auto &candidate : haystack) { if (candidate->isTypeDef() && candidate->templateBaseClass() == templateBaseClass && candidate->templateBaseClassInstantiations() == instantiations) { @@ -339,19 +390,20 @@ static const AbstractMetaClass * return nullptr; } -void HeaderGenerator::writeTypeIndexValueLine(TextStream &s, const ApiExtractorResult &api, - const TypeEntryCPtr &typeEntry) +void HeaderGenerator::collectTypeEntryTypeIndexes(const ApiExtractorResult &api, + const TypeEntryCPtr &typeEntry, + IndexValues *indexValues) { if (!typeEntry || !typeEntry->generateCode()) return; - s.setFieldAlignment(QTextStream::AlignLeft); const int typeIndex = typeEntry->sbkIndex(); - _writeTypeIndexValueLine(s, getTypeIndexVariableName(typeEntry), typeIndex); + indexValues->append({getTypeIndexVariableName(typeEntry), typeIndex, {}}); + if (typeEntry->isComplex()) { // For a typedef "using Foo=QList<int>", write a type index // SBK_QLIST_INT besides SBK_FOO which is then matched by function // argument. Check against duplicate typedefs for the same types. - const auto cType = qSharedPointerCast<const ComplexTypeEntry>(typeEntry); + const auto cType = std::static_pointer_cast<const ComplexTypeEntry>(typeEntry); if (cType->baseContainerType()) { auto metaClass = AbstractMetaClass::findClass(api.classes(), cType); Q_ASSERT(metaClass != nullptr); @@ -361,20 +413,21 @@ void HeaderGenerator::writeTypeIndexValueLine(TextStream &s, const ApiExtractorR metaClass) == nullptr) { const QString indexVariable = getTypeAlternateTemplateIndexVariableName(metaClass); - _writeTypeIndexValueLine(s, indexVariable, typeIndex); + indexValues->append({indexVariable, typeIndex, {}}); m_alternateTemplateIndexes.append(m_alternateTemplateIndexes); } } } if (typeEntry->isEnum()) { - auto ete = qSharedPointerCast<const EnumTypeEntry>(typeEntry); + auto ete = std::static_pointer_cast<const EnumTypeEntry>(typeEntry); if (ete->flags()) - writeTypeIndexValueLine(s, api, ete->flags()); + collectTypeEntryTypeIndexes(api, ete->flags(), indexValues); } } -void HeaderGenerator::writeTypeIndexValueLines(TextStream &s, const ApiExtractorResult &api, - const AbstractMetaClass *metaClass) +void HeaderGenerator::collectClassTypeIndexes(const ApiExtractorResult &api, + const AbstractMetaClassCPtr &metaClass, + IndexValues *indexValues) { auto typeEntry = metaClass->typeEntry(); if (!typeEntry->generateCode()) @@ -382,10 +435,10 @@ void HeaderGenerator::writeTypeIndexValueLines(TextStream &s, const ApiExtractor // enum indices are required for invisible namespaces as well. for (const AbstractMetaEnum &metaEnum : metaClass->enums()) { if (!metaEnum.isPrivate()) - writeTypeIndexValueLine(s, api, metaEnum.typeEntry()); + collectTypeEntryTypeIndexes(api, metaEnum.typeEntry(), indexValues); } if (NamespaceTypeEntry::isVisibleScope(typeEntry)) - writeTypeIndexValueLine(s, api, typeEntry); + collectTypeEntryTypeIndexes(api, typeEntry, indexValues); } // Format the typedefs for the typedef entries to be generated @@ -417,18 +470,18 @@ static void formatTypeDefEntries(TextStream &s) // Helpers for forward-declaring classes in the module header for the // specialization of the SbkType template functions. This is possible if the // class does not have inner types or enums which need to be known. -static bool canForwardDeclare(const AbstractMetaClass *c) +static bool canForwardDeclare(const AbstractMetaClassCPtr &c) { if (c->isNamespace() || !c->enums().isEmpty() || !c->innerClasses().isEmpty() || c->isTypeDef()) { return false; } - if (auto *encl = c->enclosingClass()) + if (auto encl = c->enclosingClass()) return encl->isNamespace(); return true; } -static void writeForwardDeclaration(TextStream &s, const AbstractMetaClass *c) +static void writeForwardDeclaration(TextStream &s, const AbstractMetaClassCPtr &c) { Q_ASSERT(!c->isNamespace()); const bool isStruct = c->attributes().testFlag(AbstractMetaClass::Struct); @@ -447,7 +500,7 @@ static void writeForwardDeclaration(TextStream &s, const AbstractMetaClass *c) // forward declarations to the module header. Ensure inline namespaces // are marked as such (else clang complains) and namespaces are ordered. struct NameSpace { - const AbstractMetaClass *nameSpace; + AbstractMetaClassCPtr nameSpace; AbstractMetaClassCList classes; }; @@ -458,7 +511,7 @@ static bool operator<(const NameSpace &n1, const NameSpace &n2) using NameSpaces = QList<NameSpace>; -static qsizetype indexOf(const NameSpaces &nsps, const AbstractMetaClass *needle) +static qsizetype indexOf(const NameSpaces &nsps, const AbstractMetaClassCPtr &needle) { for (qsizetype i = 0, count = nsps.size(); i < count; ++i) { if (nsps.at(i).nameSpace == needle) @@ -475,7 +528,7 @@ static void writeNamespaceForwardDeclarationRecursion(TextStream &s, qsizetype i if (root.nameSpace->isInlineNamespace()) s << "inline "; s << "namespace " << root.nameSpace->name() << " {\n" << indent; - for (auto *c : root.classes) + for (const auto &c : root.classes) writeForwardDeclaration(s, c); for (qsizetype i = 0, count = nameSpaces.size(); i < count; ++i) { @@ -490,15 +543,20 @@ static void writeForwardDeclarations(TextStream &s, { NameSpaces nameSpaces; - for (auto *c : classList) { - if (auto *encl = c->enclosingClass()) { + s << '\n'; + auto typeSystemEntry = TypeDatabase::instance()->defaultTypeSystemType(); + if (!typeSystemEntry->namespaceBegin().isEmpty()) + s << typeSystemEntry->namespaceBegin() << '\n'; + + for (const auto &c : classList) { + if (auto encl = c->enclosingClass()) { Q_ASSERT(encl->isNamespace()); auto idx = indexOf(nameSpaces, encl); if (idx != -1) { nameSpaces[idx].classes.append(c); } else { nameSpaces.append(NameSpace{encl, {c}}); - for (auto *enclNsp = encl->enclosingClass(); enclNsp != nullptr; + for (auto enclNsp = encl->enclosingClass(); enclNsp; enclNsp = enclNsp->enclosingClass()) { idx = indexOf(nameSpaces, enclNsp); if (idx == -1) @@ -518,103 +576,156 @@ static void writeForwardDeclarations(TextStream &s, if (nsp.nameSpace->enclosingClass() == nullptr) writeNamespaceForwardDeclarationRecursion(s, i, nameSpaces); } + + if (!typeSystemEntry->namespaceEnd().isEmpty()) + s << typeSystemEntry->namespaceEnd() << '\n'; } // Include parameters required for the module/private module header + +using ConditionalIncludeMap = QMap<QString, IncludeGroup>; + +static TextStream &operator<<(TextStream &s, const ConditionalIncludeMap &m) +{ + for (auto it = m.cbegin(), end = m.cend(); it != end; ++it) + s << it.key() << '\n' << it.value() << "#endif\n"; + return s; +} + struct ModuleHeaderParameters { AbstractMetaClassCList forwardDeclarations; std::set<Include> includes; + ConditionalIncludeMap conditionalIncludes; QString typeFunctions; }; -bool HeaderGenerator::finishGeneration() +HeaderGenerator::IndexValues + HeaderGenerator::collectTypeIndexes(const AbstractMetaClassCList &classList) { - // Generate the main header for this module. This header should be included - // by binding modules extending on top of this one. - ModuleHeaderParameters parameters; - ModuleHeaderParameters privateParameters; - StringStream macrosStream(TextStream::Language::Cpp); + IndexValues result; - const auto snips = TypeDatabase::instance()->defaultTypeSystemType()->codeSnips(); - if (!snips.isEmpty()) { - writeCodeSnips(macrosStream, snips, TypeSystem::CodeSnipPositionDeclaration, - TypeSystem::TargetLangCode); - } - - macrosStream << "// Type indices\nenum : int {\n"; - auto classList = api().classes(); - - std::sort(classList.begin(), classList.end(), - [](const AbstractMetaClass *a, const AbstractMetaClass *b) { - return a->typeEntry()->sbkIndex() < b->typeEntry()->sbkIndex(); - }); - - for (const AbstractMetaClass *metaClass : classList) - writeTypeIndexValueLines(macrosStream, api(), metaClass); + for (const auto &metaClass : classList) + collectClassTypeIndexes(api(), metaClass, &result); for (const AbstractMetaEnum &metaEnum : api().globalEnums()) - writeTypeIndexValueLine(macrosStream, api(), metaEnum.typeEntry()); + collectTypeEntryTypeIndexes(api(), metaEnum.typeEntry(), &result); // Write the smart pointer define indexes. int smartPointerCountIndex = getMaxTypeIndex(); int smartPointerCount = 0; for (const auto &smp : api().instantiatedSmartPointers()) { QString indexName = getTypeIndexVariableName(smp.type); - _writeTypeIndexValue(macrosStream, indexName, smartPointerCountIndex); - macrosStream << ", // " << smp.type.cppSignature() << '\n'; + result.append({indexName, smartPointerCountIndex, smp.type.cppSignature()}); // Add a the same value for const pointees (shared_ptr<const Foo>). const auto ptrName = smp.type.typeEntry()->entryName(); - int pos = indexName.indexOf(ptrName, 0, Qt::CaseInsensitive); + const auto pos = indexName.indexOf(ptrName, 0, Qt::CaseInsensitive); if (pos >= 0) { - indexName.insert(pos + ptrName.size() + 1, u"CONST"_s); - _writeTypeIndexValue(macrosStream, indexName, smartPointerCountIndex); - macrosStream << ", // (const)\n"; + indexName.insert(pos + ptrName.size() + 1, u"const"_s); + result.append({indexName, smartPointerCountIndex, "(const)"_L1}); } ++smartPointerCountIndex; ++smartPointerCount; } + result.append({"SBK_"_L1 + moduleName() + "_IDX_COUNT"_L1, + getMaxTypeIndex() + smartPointerCount, {}}); + return result; +} - _writeTypeIndexValue(macrosStream, - u"SBK_"_s + moduleName() + u"_IDX_COUNT"_s, - getMaxTypeIndex() + smartPointerCount); - macrosStream << "\n};\n"; - - macrosStream << "// This variable stores all Python types exported by this module.\n"; - macrosStream << "extern PyTypeObject **" << cppApiVariableName() << ";\n\n"; - macrosStream << "// This variable stores the Python module object exported by this module.\n"; - macrosStream << "extern PyObject *" << pythonModuleObjectName() << ";\n\n"; - macrosStream << "// This variable stores all type converters exported by this module.\n"; - macrosStream << "extern SbkConverter **" << convertersVariableName() << ";\n\n"; - - // TODO-CONVERTER ------------------------------------------------------------------------------ - // Using a counter would not do, a fix must be made to APIExtractor's getTypeIndex(). - macrosStream << "// Converter indices\nenum : int {\n"; +HeaderGenerator::IndexValues HeaderGenerator::collectConverterIndexes() const +{ + IndexValues result; const auto &primitives = primitiveTypes(); int pCount = 0; for (const auto &ptype : primitives) { - /* Note: do not generate indices for typedef'd primitive types - * as they'll use the primitive type converters instead, so we - * don't need to create any other. - */ - if (!ptype->generateCode() || !ptype->customConversion()) - continue; - - _writeTypeIndexValueLine(macrosStream, getTypeIndexVariableName(ptype), pCount++); + // Note: do not generate indices for typedef'd primitive types as + // they'll use the primitive type converters instead, so we + // don't need to create any other. + if (ptype->generateCode() && ptype->customConversion() != nullptr) + result.append({getTypeIndexVariableName(ptype), pCount++, {}}); } for (const AbstractMetaType &container : api().instantiatedContainers()) { - _writeTypeIndexValue(macrosStream, getTypeIndexVariableName(container), pCount); - macrosStream << ", // " << container.cppSignature() << '\n'; - pCount++; + result.append({getTypeIndexVariableName(container), + pCount++, container.cppSignature()}); } // Because on win32 the compiler will not accept a zero length array. if (pCount == 0) pCount++; - _writeTypeIndexValue(macrosStream, QStringLiteral("SBK_%1_CONVERTERS_IDX_COUNT") - .arg(moduleName()), pCount); - macrosStream << "\n};\n"; + result.append({"SBK_"_L1 + moduleName() + "_CONVERTERS_IDX_COUNT"_L1, + pCount, {}}); + return result; +} + +// PYSIDE-2404: Write the enums in unchanged case for reuse in type imports. +// For conpatibility, we create them in uppercase, too and with +// doubled index for emulating the former type-only case. +// +// FIXME: Remove in PySide 7. (See the note in `parser.py`) +// +static IndexValue typeIndexUpper(struct IndexValue const &ti) +{ + QString modi = ti.name.toUpper(); + if (modi == ti.name) + modi = u"// "_s + modi; + return {modi, ti.value * 2, ti.comment}; +} + +bool HeaderGenerator::finishGeneration() +{ + // Generate the main header for this module. This header should be included + // by binding modules extending on top of this one. + ModuleHeaderParameters parameters; + ModuleHeaderParameters privateParameters; + StringStream macrosStream(TextStream::Language::Cpp); + + const auto snips = TypeDatabase::instance()->defaultTypeSystemType()->codeSnips(); + writeModuleCodeSnips(macrosStream, snips, TypeSystem::CodeSnipPositionDeclaration, + TypeSystem::TargetLangCode); + + auto classList = api().classes(); + + std::sort(classList.begin(), classList.end(), + [](const AbstractMetaClassCPtr &a, const AbstractMetaClassCPtr &b) { + return a->typeEntry()->sbkIndex() < b->typeEntry()->sbkIndex(); + }); + + const auto typeIndexes = collectTypeIndexes(classList); + + macrosStream << "\n// Type indices\nenum [[deprecated]] : int {\n"; + for (const auto &ti : typeIndexes) + macrosStream << typeIndexUpper(ti); + macrosStream << "};\n"; + + macrosStream << "\n// Type indices\nenum : int {\n"; + for (const auto &ti : typeIndexes) + macrosStream << ti; + macrosStream << "};\n\n"; + + // FIXME: Remove backwards compatible variable in PySide 7. + macrosStream << "// This variable stores all Python types exported by this module.\n"; + macrosStream << "extern Shiboken::Module::TypeInitStruct *" << cppApiVariableName() << ";\n\n"; + macrosStream << "// This variable stores all Python types exported by this module "; + macrosStream << "in a backwards compatible way with identical indexing.\n"; + macrosStream << "[[deprecated]] extern PyTypeObject **" << cppApiVariableNameOld() << ";\n\n"; + macrosStream << "// This variable stores the Python module object exported by this module.\n"; + macrosStream << "extern PyObject *" << pythonModuleObjectName() << ";\n\n"; + macrosStream << "// This variable stores all type converters exported by this module.\n"; + macrosStream << "extern SbkConverter **" << convertersVariableName() << ";\n\n"; + + // TODO-CONVERTER ------------------------------------------------------------------------------ + // Using a counter would not do, a fix must be made to APIExtractor's getTypeIndex(). + const auto converterIndexes = collectConverterIndexes(); + macrosStream << "// Converter indices\nenum [[deprecated]] : int {\n"; + for (const auto &ci : converterIndexes) + macrosStream << typeIndexUpper(ci); + macrosStream << "};\n\n"; + + macrosStream << "// Converter indices\nenum : int {\n"; + for (const auto &ci : converterIndexes) + macrosStream << ci; + macrosStream << "};\n"; formatTypeDefEntries(macrosStream); @@ -627,28 +738,36 @@ bool HeaderGenerator::finishGeneration() for (const AbstractMetaEnum &cppEnum : api().globalEnums()) { if (!cppEnum.isAnonymous()) { - parameters.includes.insert(cppEnum.typeEntry()->include()); + const auto te = cppEnum.typeEntry(); + if (te->hasConfigCondition()) + parameters.conditionalIncludes[te->configCondition()].append(te->include()); + else + parameters.includes.insert(cppEnum.typeEntry()->include()); writeSbkTypeFunction(typeFunctions, cppEnum); } } StringStream protEnumsSurrogates(TextStream::Language::Cpp); - for (auto metaClass : classList) { + for (const auto &metaClass : classList) { const auto classType = metaClass->typeEntry(); if (!shouldGenerate(classType)) continue; - //Includes + // Includes const bool isPrivate = classType->isPrivate(); auto &par = isPrivate ? privateParameters : parameters; const auto classInclude = classType->include(); + const bool hasConfigCondition = classType->hasConfigCondition(); if (leanHeaders() && canForwardDeclare(metaClass)) par.forwardDeclarations.append(metaClass); + else if (hasConfigCondition) + par.conditionalIncludes[classType->configCondition()].append(classInclude); else par.includes.insert(classInclude); auto &typeFunctionsStr = isPrivate ? privateTypeFunctions : typeFunctions; + ConfigurableScope configScope(typeFunctionsStr, classType); for (const AbstractMetaEnum &cppEnum : metaClass->enums()) { if (cppEnum.isAnonymous() || cppEnum.isPrivate()) continue; @@ -688,6 +807,7 @@ bool HeaderGenerator::finishGeneration() } s << "#include <sbkpython.h>\n"; + s << "#include <sbkmodule.h>\n"; s << "#include <sbkconverter.h>\n"; QStringList requiredTargetImports = TypeDatabase::instance()->requiredTargetImports(); @@ -701,6 +821,7 @@ bool HeaderGenerator::finishGeneration() s << "// Bound library includes\n"; for (const Include &include : parameters.includes) s << include; + s << parameters.conditionalIncludes; if (leanHeaders()) { writeForwardDeclarations(s, parameters.forwardDeclarations); @@ -751,8 +872,7 @@ void HeaderGenerator::writePrivateHeader(const QString &moduleHeaderDir, TextStream &ps = privateFile.stream; ps.setLanguage(TextStream::Language::Cpp); QString privateIncludeShield = - publicIncludeShield.left(publicIncludeShield.size() - 2) - + QStringLiteral("_P_H"); + publicIncludeShield.left(publicIncludeShield.size() - 2) + "_P_H"_L1; ps << licenseComment()<< "\n\n"; @@ -761,6 +881,7 @@ void HeaderGenerator::writePrivateHeader(const QString &moduleHeaderDir, for (const Include &include : parameters.includes) ps << include; + ps << parameters.conditionalIncludes; ps << '\n'; if (leanHeaders()) @@ -789,36 +910,52 @@ void HeaderGenerator::writeTypeFunctions(TextStream &s, const QString &typeFunct s << "QT_WARNING_POP\n"; } -void HeaderGenerator::writeProtectedEnumSurrogate(TextStream &s, const AbstractMetaEnum &cppEnum) const +void HeaderGenerator::writeProtectedEnumSurrogate(TextStream &s, const AbstractMetaEnum &cppEnum) { if (avoidProtectedHack() && cppEnum.isProtected()) s << "enum " << protectedEnumSurrogateName(cppEnum) << " {};\n"; } -void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaEnum &cppEnum) const +void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaEnum &cppEnum) { const QString enumName = avoidProtectedHack() && cppEnum.isProtected() ? protectedEnumSurrogateName(cppEnum) : cppEnum.qualifiedCppName(); - - s << "template<> inline PyTypeObject *SbkType< ::" << enumName << " >() "; - s << "{ return " << cpythonTypeNameExt(cppEnum.typeEntry()) << "; }\n"; + const auto te = cppEnum.typeEntry(); + ConfigurableScope configScope(s, te); + s << "template<> inline PyTypeObject *SbkType< " << m_gsp << enumName << " >() "; + s << "{ return " << cpythonTypeNameExt(te) << "; }\n"; const auto flag = cppEnum.typeEntry()->flags(); - if (!flag.isNull()) { - s << "template<> inline PyTypeObject *SbkType< ::" << flag->name() << " >() " + if (flag) { + s << "template<> inline PyTypeObject *SbkType< " << m_gsp << flag->name() << " >() " << "{ return " << cpythonTypeNameExt(flag) << "; }\n"; } } -void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaClass *cppClass) +void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaClassCPtr &cppClass) { - s << "template<> inline PyTypeObject *SbkType< ::" << cppClass->qualifiedCppName() << " >() " - << "{ return reinterpret_cast<PyTypeObject *>(" << cpythonTypeNameExt(cppClass->typeEntry()) << "); }\n"; + s << "template<> inline PyTypeObject *SbkType< " + << getFullTypeName(cppClass) << " >() " + << "{ return " << cpythonTypeNameExt(cppClass->typeEntry()) << "; }\n"; } void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaType &metaType) { - s << "template<> inline PyTypeObject *SbkType< ::" << metaType.cppSignature() << " >() " + s << "template<> inline PyTypeObject *SbkType< " + << m_gsp << metaType.cppSignature() << " >() " << "{ return " << cpythonTypeNameExt(metaType) << "; }\n"; } + +void HeaderGenerator::writeModuleCodeSnips(TextStream &s, const CodeSnipList &codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language) const +{ + if (!codeSnips.isEmpty()) { + try { + writeCodeSnips(s, codeSnips, position, language); + } catch (const std::exception &e) { + throw Exception(msgSnippetError("module header of "_L1 + moduleName(), e.what())); + } + } +} diff --git a/sources/shiboken6/generator/shiboken/headergenerator.h b/sources/shiboken6/generator/shiboken/headergenerator.h index f1735cd9f..03b98e743 100644 --- a/sources/shiboken6/generator/shiboken/headergenerator.h +++ b/sources/shiboken6/generator/shiboken/headergenerator.h @@ -6,9 +6,12 @@ #include "shibokengenerator.h" #include "include.h" +#include "modifications_typedefs.h" +#include <QtCore/QList> #include <QtCore/QSet> +struct IndexValue; class AbstractMetaFunction; struct ModuleHeaderParameters; @@ -18,11 +21,9 @@ struct ModuleHeaderParameters; class HeaderGenerator : public ShibokenGenerator { public: - OptionDescriptions options() const override { return OptionDescriptions(); } - const char *name() const override { return "Header generator"; } - static QString headerFileNameForContext(const GeneratorContext &context); + static const char *protectedHackDefine; protected: QString fileNameForContext(const GeneratorContext &context) const override; @@ -30,26 +31,44 @@ protected: bool finishGeneration() override; private: - void writeCopyCtor(TextStream &s, const AbstractMetaClass *metaClass) const; - void writeFunction(TextStream &s, const AbstractMetaFunctionCPtr &func, - FunctionGeneration generation); - void writeSbkTypeFunction(TextStream &s, const AbstractMetaEnum &cppEnum) const; - static void writeSbkTypeFunction(TextStream &s, const AbstractMetaClass *cppClass) ; - static void writeSbkTypeFunction(TextStream &s, const AbstractMetaType &metaType) ; - void writeTypeIndexValueLine(TextStream &s, const ApiExtractorResult &api, - const TypeEntryCPtr &typeEntry); - void writeTypeIndexValueLines(TextStream &s, const ApiExtractorResult &api, - const AbstractMetaClass *metaClass); - void writeProtectedEnumSurrogate(TextStream &s, const AbstractMetaEnum &cppEnum) const; + using InheritedOverloadSet = QSet<AbstractMetaFunctionCPtr>; + using IndexValues = QList<IndexValue>; + + IndexValues collectTypeIndexes(const AbstractMetaClassCList &classList); + IndexValues collectConverterIndexes() const; + + static void writeCopyCtor(TextStream &s, const AbstractMetaClassCPtr &metaClass); + void writeFunction(TextStream &s, + const AbstractMetaFunctionCPtr &func, + InheritedOverloadSet *inheritedOverloads, + FunctionGeneration generation) const; + static void writeSbkTypeFunction(TextStream &s, const AbstractMetaEnum &cppEnum); + static void writeSbkTypeFunction(TextStream &s, const AbstractMetaClassCPtr &cppClass); + static void writeSbkTypeFunction(TextStream &s, const AbstractMetaType &metaType); + void collectTypeEntryTypeIndexes(const ApiExtractorResult &api, + const TypeEntryCPtr &typeEntry, + IndexValues *indexValues); + void collectClassTypeIndexes(const ApiExtractorResult &api, + const AbstractMetaClassCPtr &metaClass, + IndexValues *indexValues); + static void writeProtectedEnumSurrogate(TextStream &s, const AbstractMetaEnum &cppEnum); void writeMemberFunctionWrapper(TextStream &s, const AbstractMetaFunctionCPtr &func, const QString &postfix = {}) const; void writePrivateHeader(const QString &moduleHeaderDir, const QString &publicIncludeShield, const ModuleHeaderParameters ¶meters); - void writeTypeFunctions(TextStream &s, const QString &typeFunctions); + static void writeTypeFunctions(TextStream &s, const QString &typeFunctions); + void writeWrapperClassDeclaration(TextStream &s, + const QString &wrapperName, + const GeneratorContext &classContext) const; + void writeWrapperClass(TextStream &s, const QString &wrapperName, const GeneratorContext &classContext) const; + void writeInheritedWrapperClassDeclaration(TextStream &s, + const GeneratorContext &classContext) const; + void writeModuleCodeSnips(TextStream &s, const CodeSnipList &codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language) const; - QSet<AbstractMetaFunctionCPtr> m_inheritedOverloads; AbstractMetaClassCList m_alternateTemplateIndexes; }; diff --git a/sources/shiboken6/generator/shiboken/overloaddata.cpp b/sources/shiboken6/generator/shiboken/overloaddata.cpp index a5594791e..c28fcdc1a 100644 --- a/sources/shiboken6/generator/shiboken/overloaddata.cpp +++ b/sources/shiboken6/generator/shiboken/overloaddata.cpp @@ -16,7 +16,6 @@ #include "pytypenames.h" #include "textstream.h" #include "exception.h" -#include "messages.h" #include "qtcompat.h" @@ -126,6 +125,7 @@ using OverloadGraph = Graph<QString>; void OverloadDataRootNode::sortNextOverloads(const ApiExtractorResult &api) { QHash<QString, OverloadDataList> typeToOverloads; + using Edge = std::pair<QString, QString>; bool checkPyObject = false; bool checkPySequence = false; @@ -135,10 +135,10 @@ void OverloadDataRootNode::sortNextOverloads(const ApiExtractorResult &api) // Primitive types that are not int, long, short, // char and their respective unsigned counterparts. - static const QStringList nonIntegerPrimitives{floatT(), doubleT(), boolT()}; + static const QStringList nonIntegerPrimitives{floatT, doubleT, boolT}; // Signed integer primitive types. - static const QStringList signedIntegerPrimitives{intT(), shortT(), longT(), longLongT()}; + static const QStringList signedIntegerPrimitives{intT, shortT, longT, longLongT}; // sort the children overloads for (const auto &ov : std::as_const(m_children)) @@ -162,15 +162,15 @@ void OverloadDataRootNode::sortNextOverloads(const ApiExtractorResult &api) it.value().append(ov); } - if (!checkPyObject && typeName == cPyObjectT()) + if (!checkPyObject && typeName == cPyObjectT) checkPyObject = true; - else if (!checkPySequence && typeName == cPySequenceT()) + else if (!checkPySequence && typeName == cPySequenceT) checkPySequence = true; - else if (!checkPyBuffer && typeName == cPyBufferT()) + else if (!checkPyBuffer && typeName == cPyBufferT) checkPyBuffer = true; - else if (!checkQVariant && typeName == qVariantT()) + else if (!checkQVariant && typeName == qVariantT) checkQVariant = true; - else if (!checkQString && typeName == qStringT()) + else if (!checkQString && typeName == qStringT) checkQString = true; for (const auto &instantiation : ov->argType().instantiations()) { @@ -196,9 +196,9 @@ void OverloadDataRootNode::sortNextOverloads(const ApiExtractorResult &api) // Create the graph of type dependencies based on implicit conversions. // All C++ primitive types, add any forgotten type AT THE END OF THIS LIST! - static const QStringList primitiveTypes{intT(), unsignedIntT(), longT(), unsignedLongT(), - shortT(), unsignedShortT(), boolT(), unsignedCharT(), charT(), floatT(), - doubleT(), constCharPtrT()}; + static const QStringList primitiveTypes{intT, unsignedIntT, longT, unsignedLongT, + shortT, unsignedShortT, boolT, unsignedCharT, charT, floatT, + doubleT, constCharPtrT}; QStringList foundPrimitiveTypeIds; for (const auto &p : primitiveTypes) { @@ -207,7 +207,7 @@ void OverloadDataRootNode::sortNextOverloads(const ApiExtractorResult &api) } if (checkPySequence && checkPyObject) - graph.addEdge(cPySequenceT(), cPyObjectT()); + graph.addEdge(cPySequenceT, cPyObjectT); QStringList classesWithIntegerImplicitConversion; @@ -226,7 +226,7 @@ void OverloadDataRootNode::sortNextOverloads(const ApiExtractorResult &api) else convertibleType = getTypeName(function->arguments().constFirst().type()); - if (convertibleType == intT() || convertibleType == unsignedIntT()) + if (convertibleType == intT || convertibleType == unsignedIntT) classesWithIntegerImplicitConversion << targetTypeEntryName; if (!graph.hasNode(convertibleType)) @@ -246,7 +246,7 @@ void OverloadDataRootNode::sortNextOverloads(const ApiExtractorResult &api) if (!metaClass) throw Exception(msgArgumentClassNotFound(m_overloads.constFirst(), te)); const auto &ancestors = metaClass->allTypeSystemAncestors(); - for (const AbstractMetaClass *ancestor : ancestors) { + for (const auto &ancestor : ancestors) { QString ancestorTypeName = ancestor->typeEntry()->name(); if (!graph.hasNode(ancestorTypeName)) continue; @@ -288,28 +288,28 @@ void OverloadDataRootNode::sortNextOverloads(const ApiExtractorResult &api) if ((checkPySequence || checkPyObject || checkPyBuffer) - && !targetTypeEntryName.contains(cPyObjectT()) - && !targetTypeEntryName.contains(cPyBufferT()) - && !targetTypeEntryName.contains(cPySequenceT())) { + && !targetTypeEntryName.contains(cPyObjectT) + && !targetTypeEntryName.contains(cPyBufferT) + && !targetTypeEntryName.contains(cPySequenceT)) { if (checkPySequence) { // PySequence will be checked after all more specific types, but before PyObject. - graph.addEdge(targetTypeEntryName, cPySequenceT()); + graph.addEdge(targetTypeEntryName, cPySequenceT); } else if (checkPyBuffer) { // PySequence will be checked after all more specific types, but before PyObject. - graph.addEdge(targetTypeEntryName, cPyBufferT()); + graph.addEdge(targetTypeEntryName, cPyBufferT); } else { // Add dependency on PyObject, so its check is the last one (too generic). - graph.addEdge(targetTypeEntryName, cPyObjectT()); + graph.addEdge(targetTypeEntryName, cPyObjectT); } - } else if (checkQVariant && targetTypeEntryName != qVariantT()) { - if (!graph.containsEdge(qVariantT(), targetTypeEntryName)) // Avoid cyclic dependency. - graph.addEdge(targetTypeEntryName, qVariantT()); + } else if (checkQVariant && targetTypeEntryName != qVariantT) { + if (!graph.containsEdge(qVariantT, targetTypeEntryName)) // Avoid cyclic dependency. + graph.addEdge(targetTypeEntryName, qVariantT); } else if (checkQString && ov->argType().isPointer() - && targetTypeEntryName != qStringT() - && targetTypeEntryName != qByteArrayT() - && (!checkPyObject || targetTypeEntryName != cPyObjectT())) { - if (!graph.containsEdge(qStringT(), targetTypeEntryName)) // Avoid cyclic dependency. - graph.addEdge(targetTypeEntryName, qStringT()); + && targetTypeEntryName != qStringT + && targetTypeEntryName != qByteArrayT + && (!checkPyObject || targetTypeEntryName != cPyObjectT)) { + if (!graph.containsEdge(qStringT, targetTypeEntryName)) // Avoid cyclic dependency. + graph.addEdge(targetTypeEntryName, qStringT); } if (targetType.isEnum()) { @@ -320,8 +320,19 @@ void OverloadDataRootNode::sortNextOverloads(const ApiExtractorResult &api) } // QByteArray args need to be checked after QString args - if (graph.hasNode(qStringT()) && graph.hasNode(qByteArrayT())) - graph.addEdge(qStringT(), qByteArrayT()); + if (graph.hasNode(qStringT) && graph.hasNode(qByteArrayT)) + graph.addEdge(qStringT, qByteArrayT); + + static const Edge rangeOrder[] = + {{doubleT, floatT}, + {longLongT, longT}, {longLongT, intT}, {intT, shortT}, + {unsignedLongLongT, unsignedLongT}, {unsignedLongLongT, unsignedT}, + {unsignedLongLongT, unsignedIntT}, {unsignedT, unsignedShortT} + }; + for (const auto &r : rangeOrder) { + if (graph.hasNode(r.first) && graph.hasNode(r.second)) + graph.addEdge(r.first, r.second); + } for (const auto &ov : std::as_const(m_children)) { const AbstractMetaType &targetType = ov->argType(); @@ -381,8 +392,7 @@ static std::pair<int, int> getMinMaxArgs(const AbstractMetaFunctionCPtr &func) int defaultValueIndex = -1; const auto &arguments = func->arguments(); int argIndex = 0; - for (qsizetype i = 0, size = arguments.size(); i < size; ++i) { - const auto &arg = arguments.at(i); + for (const auto &arg : arguments) { if (!arg.isModifiedRemoved()) { if (defaultValueIndex < 0 && arg.hasDefaultValueExpression()) defaultValueIndex = argIndex; @@ -475,13 +485,13 @@ OverloadDataNode *OverloadDataRootNode::addOverloadDataNode(const AbstractMetaFu } } - if (overloadData.isNull()) { + if (!overloadData) { const int argpos = argPos() + 1; overloadData.reset(new OverloadDataNode(func, this, arg, argpos)); m_children.append(overloadData); } - return overloadData.data(); + return overloadData.get(); } bool OverloadData::hasNonVoidReturnType() const @@ -604,7 +614,7 @@ const AbstractMetaArgument *OverloadDataNode::overloadArgument(const AbstractMet bool OverloadDataRootNode::nextArgumentHasDefaultValue() const { for (const auto &overloadData : m_children) { - if (!overloadData->getFunctionWithDefaultValue().isNull()) + if (overloadData->getFunctionWithDefaultValue()) return true; } return false; @@ -612,20 +622,20 @@ bool OverloadDataRootNode::nextArgumentHasDefaultValue() const static const OverloadDataRootNode *_findNextArgWithDefault(const OverloadDataRootNode *overloadData) { - if (!overloadData->getFunctionWithDefaultValue().isNull()) + if (overloadData->getFunctionWithDefaultValue()) return overloadData; const OverloadDataRootNode *result = nullptr; const OverloadDataList &data = overloadData->children(); for (const auto &odata : data) { - const auto *tmp = _findNextArgWithDefault(odata.data()); + const auto *tmp = _findNextArgWithDefault(odata.get()); if (!result || (tmp && result->argPos() > tmp->argPos())) result = tmp; } return result; } -const OverloadDataRootNode *OverloadDataRootNode::findNextArgWithDefault() +const OverloadDataRootNode *OverloadDataRootNode::findNextArgWithDefault() const { return _findNextArgWithDefault(this); } diff --git a/sources/shiboken6/generator/shiboken/overloaddata.h b/sources/shiboken6/generator/shiboken/overloaddata.h index 3fc9eef50..875a5a8b5 100644 --- a/sources/shiboken6/generator/shiboken/overloaddata.h +++ b/sources/shiboken6/generator/shiboken/overloaddata.h @@ -9,13 +9,14 @@ #include <QtCore/QBitArray> #include <QtCore/QList> -#include <QtCore/QSharedPointer> + +#include <memory> QT_FORWARD_DECLARE_CLASS(QDebug) QT_FORWARD_DECLARE_CLASS(QTextStream) class OverloadDataNode; -using OverloadDataNodePtr = QSharedPointer<OverloadDataNode>; +using OverloadDataNodePtr = std::shared_ptr<OverloadDataNode>; using OverloadDataList = QList<OverloadDataNodePtr>; /// The root node of OverloadData. It contains all functions @@ -45,7 +46,7 @@ public: AbstractMetaFunctionCPtr getFunctionWithDefaultValue() const; /// Returns the nearest occurrence, including this instance, of an argument with a default value. - const OverloadDataRootNode *findNextArgWithDefault(); + const OverloadDataRootNode *findNextArgWithDefault() const; bool isFinalOccurrence(const AbstractMetaFunctionCPtr &func) const; int functionNumber(const AbstractMetaFunctionCPtr &func) const; diff --git a/sources/shiboken6/generator/shiboken/pytypenames.h b/sources/shiboken6/generator/shiboken/pytypenames.h index 696e0d88d..6c7658ff6 100644 --- a/sources/shiboken6/generator/shiboken/pytypenames.h +++ b/sources/shiboken6/generator/shiboken/pytypenames.h @@ -6,24 +6,24 @@ #include <QtCore/QString> -static inline QString pyBoolT() { return QStringLiteral("PyBool"); } -static inline QString pyFloatT() { return QStringLiteral("PyFloat"); } -static inline QString pyLongT() { return QStringLiteral("PyLong"); } -static inline QString pyObjectT() { return QStringLiteral("object"); } -static inline QString pyStrT() { return QStringLiteral("str"); } +constexpr auto pyBoolT = QLatin1StringView ("PyBool"); +constexpr auto pyFloatT = QLatin1StringView ("PyFloat"); +constexpr auto pyLongT = QLatin1StringView ("PyLong"); +constexpr auto pyObjectT = QLatin1StringView ("object"); +constexpr auto pyStrT = QLatin1StringView ("str"); // PYSIDE-1499: A custom type determined by existence of an `__fspath__` attribute. -static inline QString pyPathLikeT() { return QStringLiteral("PyPathLike"); } +constexpr auto pyPathLikeT = QLatin1StringView ("PyPathLike"); -static inline QString cPyBufferT() { return QStringLiteral("PyBuffer"); } -static inline QString cPyListT() { return QStringLiteral("PyList"); } -static inline QString cPyObjectT() { return QStringLiteral("PyObject"); } -static inline QString cPySequenceT() { return QStringLiteral("PySequence"); } -static inline QString cPyTypeObjectT() { return QStringLiteral("PyTypeObject"); } +constexpr auto cPyBufferT = QLatin1StringView ("PyBuffer"); +constexpr auto cPyListT = QLatin1StringView ("PyList"); +constexpr auto cPyObjectT = QLatin1StringView ("PyObject"); +constexpr auto cPySequenceT = QLatin1StringView ("PySequence"); +constexpr auto cPyTypeObjectT = QLatin1StringView ("PyTypeObject"); // numpy -static inline QString cPyArrayObjectT() { return QStringLiteral("PyArrayObject"); } +constexpr auto cPyArrayObjectT = QLatin1StringView ("PyArrayObject"); -static inline QString sbkCharT() { return QStringLiteral("SbkChar"); } +constexpr auto sbkCharT = QLatin1StringView ("SbkChar"); #endif // PYTYPENAMES_H diff --git a/sources/shiboken6/generator/shiboken/shibokengenerator.cpp b/sources/shiboken6/generator/shiboken/shibokengenerator.cpp index 1810235b7..67fd9c994 100644 --- a/sources/shiboken6/generator/shiboken/shibokengenerator.cpp +++ b/sources/shiboken6/generator/shiboken/shibokengenerator.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "shibokengenerator.h" +#include "generatorstrings.h" #include "generatorargument.h" #include "defaultvalue.h" #include "generatorcontext.h" @@ -20,12 +21,12 @@ #include <messages.h> #include <modifications.h> #include "overloaddata.h" +#include <optionsparser.h> #include "propertyspec.h" #include "pytypenames.h" #include <reporthandler.h> #include <textstream.h> #include <typedatabase.h> -#include <abstractmetabuilder.h> #include <containertypeentry.h> #include <customtypenentry.h> #include <enumtypeentry.h> @@ -33,6 +34,7 @@ #include <namespacetypeentry.h> #include <primitivetypeentry.h> #include <pythontypeentry.h> +#include <smartpointertypeentry.h> #include <valuetypeentry.h> #include <iostream> @@ -42,44 +44,73 @@ #include <QtCore/QDir> #include <QtCore/QDebug> #include <QtCore/QRegularExpression> + +#include <algorithm> #include <limits> #include <memory> +#include <utility> using namespace Qt::StringLiterals; -static const char PARENT_CTOR_HEURISTIC[] = "enable-parent-ctor-heuristic"; -static const char RETURN_VALUE_HEURISTIC[] = "enable-return-value-heuristic"; -static const char DISABLE_VERBOSE_ERROR_MESSAGES[] = "disable-verbose-error-messages"; -static const char USE_ISNULL_AS_NB_NONZERO[] = "use-isnull-as-nb_nonzero"; -static const char USE_OPERATOR_BOOL_AS_NB_NONZERO[] = "use-operator-bool-as-nb_nonzero"; -static const char WRAPPER_DIAGNOSTICS[] = "wrapper-diagnostics"; -static const char NO_IMPLICIT_CONVERSIONS[] = "no-implicit-conversions"; -static const char LEAN_HEADERS[] = "lean-headers"; - -const QString CPP_ARG = u"cppArg"_s; -const QString CPP_ARG_REMOVED = u"removed_cppArg"_s; -const QString CPP_RETURN_VAR = u"cppResult"_s; -const QString CPP_SELF_VAR = u"cppSelf"_s; -const QString NULL_PTR = u"nullptr"_s; -const QString PYTHON_ARG = u"pyArg"_s; -const QString PYTHON_ARGS = u"pyArgs"_s; -const QString PYTHON_OVERRIDE_VAR = u"pyOverride"_s; -const QString PYTHON_RETURN_VAR = u"pyResult"_s; -const QString PYTHON_TO_CPP_VAR = u"pythonToCpp"_s; -const QString SMART_POINTER_GETTER = u"kSmartPointerGetter"_s; - -const QString CONV_RULE_OUT_VAR_SUFFIX = u"_out"_s; -const QString BEGIN_ALLOW_THREADS = - u"PyThreadState *_save = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS"_s; -const QString END_ALLOW_THREADS = u"PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS"_s; +static constexpr auto PARENT_CTOR_HEURISTIC = "enable-parent-ctor-heuristic"_L1; +static constexpr auto RETURN_VALUE_HEURISTIC = "enable-return-value-heuristic"_L1; +static constexpr auto DISABLE_VERBOSE_ERROR_MESSAGES = "disable-verbose-error-messages"_L1; +static constexpr auto USE_ISNULL_AS_NB_BOOL = "use-isnull-as-nb-bool"_L1; +// FIXME PYSIDE 7: Remove USE_ISNULL_AS_NB_NONZERO/USE_OPERATOR_BOOL_AS_NB_NONZERO +static constexpr auto USE_ISNULL_AS_NB_NONZERO = "use-isnull-as-nb_nonzero"_L1; +static constexpr auto USE_OPERATOR_BOOL_AS_NB_BOOL = "use-operator-bool-as-nb-bool"_L1; +static constexpr auto USE_OPERATOR_BOOL_AS_NB_NONZERO = "use-operator-bool-as-nb-nonzero"_L1; +static constexpr auto WRAPPER_DIAGNOSTICS = "wrapper-diagnostics"_L1; +static constexpr auto NO_IMPLICIT_CONVERSIONS = "no-implicit-conversions"_L1; +static constexpr auto LEAN_HEADERS = "lean-headers"_L1; + +QString CPP_ARG_N(int i) +{ + return CPP_ARG + QString::number(i); +} + +constexpr auto CPP_ARG_REMOVED_PREFIX = "removed_cppArg"_L1; + +QString CPP_ARG_REMOVED(int i) +{ + return CPP_ARG_REMOVED_PREFIX + QString::number(i); +} + +const char *const METHOD_DEF_SENTINEL = "{nullptr, nullptr, 0, nullptr} // Sentinel\n"; +const char *const PYTHON_TO_CPPCONVERSION_STRUCT = "Shiboken::Conversions::PythonToCppConversion"; + +const char *const openTargetExternC = R"( +// Target --------------------------------------------------------- + +extern "C" { +)"; +const char *const closeExternC = "} // extern \"C\"\n\n"; +const char *const richCompareComment = + "// PYSIDE-74: By default, we redirect to object's tp_richcompare (which is `==`, `!=`).\n"; + +struct ShibokenGeneratorOptions +{ + bool useCtorHeuristic = false; + bool userReturnValueHeuristic = false; + bool verboseErrorMessagesDisabled = false; + bool useIsNullAsNbBool = false; + // FIXME PYSIDE 7 Flip m_leanHeaders default or remove? + bool leanHeaders = false; + bool useOperatorBoolAsNbBool = false; + // FIXME PYSIDE 7 Flip generateImplicitConversions default or remove? + bool generateImplicitConversions = true; + bool wrapperDiagnostics = false; +}; struct GeneratorClassInfoCacheEntry { ShibokenGenerator::FunctionGroups functionGroups; + QList<AbstractMetaFunctionCList> numberProtocolOperators; + BoolCastFunctionOptional boolCastFunctionO; bool needsGetattroFunction = false; }; -using GeneratorClassInfoCache = QHash<const AbstractMetaClass *, GeneratorClassInfoCacheEntry>; +using GeneratorClassInfoCache = QHash<AbstractMetaClassCPtr, GeneratorClassInfoCacheEntry>; Q_GLOBAL_STATIC(GeneratorClassInfoCache, generatorClassInfoCache) @@ -103,6 +134,10 @@ const ShibokenGenerator::TypeSystemConverterRegExps & return result; } +// Options are static to avoid duplicated handling since ShibokenGenerator +// is instantiated for HeaderGenerator and CppGenerator. +ShibokenGeneratorOptions ShibokenGenerator::m_options; + ShibokenGenerator::ShibokenGenerator() = default; ShibokenGenerator::~ShibokenGenerator() = default; @@ -111,32 +146,32 @@ ShibokenGenerator::~ShibokenGenerator() = default; static const QHash<QString, QString> &primitiveTypesCorrespondences() { static const QHash<QString, QString> result = { - {u"bool"_s, pyBoolT()}, - {u"char"_s, sbkCharT()}, - {u"signed char"_s, sbkCharT()}, - {u"unsigned char"_s, sbkCharT()}, - {intT(), pyLongT()}, - {u"signed int"_s, pyLongT()}, - {u"uint"_s, pyLongT()}, - {u"unsigned int"_s, pyLongT()}, - {shortT(), pyLongT()}, - {u"ushort"_s, pyLongT()}, - {u"signed short"_s, pyLongT()}, - {u"signed short int"_s, pyLongT()}, - {unsignedShortT(), pyLongT()}, - {u"unsigned short int"_s, pyLongT()}, - {longT(), pyLongT()}, - {doubleT(), pyFloatT()}, - {floatT(), pyFloatT()}, - {u"unsigned long"_s, pyLongT()}, - {u"signed long"_s, pyLongT()}, - {u"ulong"_s, pyLongT()}, - {u"unsigned long int"_s, pyLongT()}, - {u"long long"_s, pyLongT()}, - {u"__int64"_s, pyLongT()}, - {u"unsigned long long"_s, pyLongT()}, - {u"unsigned __int64"_s, pyLongT()}, - {u"size_t"_s, pyLongT()} + {u"bool"_s, pyBoolT}, + {u"char"_s, sbkCharT}, + {u"signed char"_s, sbkCharT}, + {u"unsigned char"_s, sbkCharT}, + {intT, pyLongT}, + {u"signed int"_s, pyLongT}, + {u"uint"_s, pyLongT}, + {u"unsigned int"_s, pyLongT}, + {shortT, pyLongT}, + {u"ushort"_s, pyLongT}, + {u"signed short"_s, pyLongT}, + {u"signed short int"_s, pyLongT}, + {unsignedShortT, pyLongT}, + {u"unsigned short int"_s, pyLongT}, + {longT, pyLongT}, + {doubleT, pyFloatT}, + {floatT, pyFloatT}, + {u"unsigned long"_s, pyLongT}, + {u"signed long"_s, pyLongT}, + {u"ulong"_s, pyLongT}, + {u"unsigned long int"_s, pyLongT}, + {u"long long"_s, pyLongT}, + {u"__int64"_s, pyLongT}, + {u"unsigned long long"_s, pyLongT}, + {u"unsigned __int64"_s, pyLongT}, + {u"size_t"_s, pyLongT} }; return result; } @@ -146,24 +181,24 @@ const QHash<QString, QChar> &ShibokenGenerator::formatUnits() static const QHash<QString, QChar> result = { {u"char"_s, u'b'}, {u"unsigned char"_s, u'B'}, - {intT(), u'i'}, + {intT, u'i'}, {u"unsigned int"_s, u'I'}, - {shortT(), u'h'}, - {unsignedShortT(), u'H'}, - {longT(), u'l'}, - {unsignedLongLongT(), u'k'}, - {longLongT(), u'L'}, + {shortT, u'h'}, + {unsignedShortT, u'H'}, + {longT, u'l'}, + {unsignedLongLongT, u'k'}, + {longLongT, u'L'}, {u"__int64"_s, u'L'}, - {unsignedLongLongT(), u'K'}, + {unsignedLongLongT, u'K'}, {u"unsigned __int64"_s, u'K'}, - {doubleT(), u'd'}, - {floatT(), u'f'}, + {doubleT, u'd'}, + {floatT, u'f'}, }; return result; } QString ShibokenGenerator::translateTypeForWrapperMethod(const AbstractMetaType &cType, - const AbstractMetaClass *context, + const AbstractMetaClassCPtr &context, Options options) const { if (cType.isArray()) { @@ -180,7 +215,7 @@ QString ShibokenGenerator::translateTypeForWrapperMethod(const AbstractMetaType return translateType(cType, context, options); } -bool ShibokenGenerator::shouldGenerateCppWrapper(const AbstractMetaClass *metaClass) const +bool ShibokenGenerator::shouldGenerateCppWrapper(const AbstractMetaClassCPtr &metaClass) { const auto wrapper = metaClass->cppWrapper(); return wrapper.testFlag(AbstractMetaClass::CppVirtualMethodWrapper) @@ -188,8 +223,17 @@ bool ShibokenGenerator::shouldGenerateCppWrapper(const AbstractMetaClass *metaCl && wrapper.testFlag(AbstractMetaClass::CppProtectedHackWrapper)); } -ShibokenGenerator::FunctionGeneration - ShibokenGenerator::functionGeneration(const AbstractMetaFunctionCPtr &func) const +bool ShibokenGenerator::shouldGenerateMetaObjectFunctions(const AbstractMetaClassCPtr &metaClass) +{ + return usePySideExtensions() + && (!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) + && !metaClass->typeEntry()->typeFlags() + .testFlag(ComplexTypeEntry::DisableQtMetaObjectFunctions) + && isQObject(metaClass); +} + +ShibokenGenerator::FunctionGeneration ShibokenGenerator::functionGeneration( + const AbstractMetaFunctionCPtr &func) { FunctionGeneration result; @@ -232,7 +276,7 @@ ShibokenGenerator::FunctionGeneration // Check on virtuals (including operators). const bool isAbstract = func->isAbstract(); if (!(isAbstract || func->isVirtual()) - || func->attributes().testFlag(AbstractMetaFunction::FinalCppMethod) + || func->cppAttributes().testFlag(FunctionAttribute::Final) || func->isModifiedFinal()) { return result; } @@ -240,7 +284,7 @@ ShibokenGenerator::FunctionGeneration // MetaObject virtuals only need to be declared; CppGenerator creates a // special implementation. if (functionType == AbstractMetaFunction::NormalFunction - && usePySideExtensions() && func->ownerClass()->isQObject()) { + && usePySideExtensions() && isQObject(func->ownerClass())) { const QString &name = func->name(); if (name == u"metaObject"_s || name == u"qt_metacall") { result.setFlag(FunctionGenerationFlag::QMetaObjectMethod); @@ -259,9 +303,9 @@ AbstractMetaFunctionCList ShibokenGenerator::implicitConversions(const TypeEntry { if (!generateImplicitConversions() || !t->isValue()) return {}; - auto vte = qSharedPointerCast<const ValueTypeEntry>(t); + auto vte = std::static_pointer_cast<const ValueTypeEntry>(t); auto customConversion = vte->customConversion(); - if (!customConversion.isNull() && customConversion->replaceOriginalTargetToNativeConversions()) + if (customConversion && customConversion->replaceOriginalTargetToNativeConversions()) return {}; auto result = api().implicitConversions(t); @@ -273,7 +317,7 @@ AbstractMetaFunctionCList ShibokenGenerator::implicitConversions(const TypeEntry return result; } -QString ShibokenGenerator::wrapperName(const AbstractMetaClass *metaClass) const +QString ShibokenGenerator::wrapperName(const AbstractMetaClassCPtr &metaClass) { Q_ASSERT(shouldGenerateCppWrapper(metaClass)); QString result = metaClass->name(); @@ -282,19 +326,55 @@ QString ShibokenGenerator::wrapperName(const AbstractMetaClass *metaClass) const return result + u"Wrapper"_s; } -QString ShibokenGenerator::fullPythonClassName(const AbstractMetaClass *metaClass) +QString ShibokenGenerator::fullPythonClassName(const AbstractMetaClassCPtr &metaClass) { QString fullClassName = metaClass->name(); - const AbstractMetaClass *enclosing = metaClass->enclosingClass(); + auto enclosing = metaClass->enclosingClass(); while (enclosing) { if (NamespaceTypeEntry::isVisibleScope(enclosing->typeEntry())) fullClassName.prepend(enclosing->name() + u'.'); enclosing = enclosing->enclosingClass(); } - fullClassName.prepend(packageName() + u'.'); + fullClassName.prepend(metaClass->typeEntry()->targetLangPackage() + u'.'); return fullClassName; } +QString ShibokenGenerator::headerFileNameForContext(const GeneratorContext &context) +{ + return fileNameForContextHelper(context, u"_wrapper.h"_s); +} + +// PYSIDE-500: When avoiding the protected hack, also include the inherited +// wrapper classes of the *current* module, because without the protected hack, +// we sometimes need to cast inherited wrappers. Inherited classes +// of *other* modules are completely regenerated by the header generator +// since the wrapper headers are not installed. + +IncludeGroup ShibokenGenerator::baseWrapperIncludes(const GeneratorContext &classContext) const +{ + IncludeGroup result{u"Wrappers"_s, {}}; + if (!classContext.useWrapper() || !avoidProtectedHack() + || classContext.forSmartPointer()) { + return result; + } + + const auto moduleEntry = TypeDatabase::instance()->defaultTypeSystemType(); + const auto &baseClasses = allBaseClasses(classContext.metaClass()); + for (const auto &base : baseClasses) { + const auto te = base->typeEntry(); + if (te->codeGeneration() == TypeEntry::GenerateCode) { // current module + const auto context = contextForClass(base); + if (context.useWrapper()) { + const QString header = headerFileNameForContext(context); + const auto type = typeSystemTypeEntry(te) == moduleEntry + ? Include::LocalPath : Include::IncludePath; + result.append(Include(type, header)); + } + } + } + return result; +} + QString ShibokenGenerator::fullPythonFunctionName(const AbstractMetaFunctionCPtr &func, bool forceFunc) { QString funcName; @@ -319,6 +399,11 @@ QString ShibokenGenerator::fullPythonFunctionName(const AbstractMetaFunctionCPtr return funcName; } +bool ShibokenGenerator::wrapperDiagnostics() +{ + return m_options.wrapperDiagnostics; +} + QString ShibokenGenerator::protectedEnumSurrogateName(const AbstractMetaEnum &metaEnum) { QString result = metaEnum.fullName(); @@ -354,37 +439,37 @@ QString ShibokenGenerator::cpythonFunctionName(const AbstractMetaFunctionCPtr &f QString ShibokenGenerator::cpythonMethodDefinitionName(const AbstractMetaFunctionCPtr &func) { if (!func->ownerClass()) - return QString(); + return {}; return cpythonBaseName(func->ownerClass()->typeEntry()) + u"Method_"_s + func->name(); } -QString ShibokenGenerator::cpythonGettersSettersDefinitionName(const AbstractMetaClass *metaClass) +QString ShibokenGenerator::cpythonGettersSettersDefinitionName(const AbstractMetaClassCPtr &metaClass) { return cpythonBaseName(metaClass) + u"_getsetlist"_s; } -QString ShibokenGenerator::cpythonSetattroFunctionName(const AbstractMetaClass *metaClass) +QString ShibokenGenerator::cpythonSetattroFunctionName(const AbstractMetaClassCPtr &metaClass) { return cpythonBaseName(metaClass) + u"_setattro"_s; } -QString ShibokenGenerator::cpythonGetattroFunctionName(const AbstractMetaClass *metaClass) +QString ShibokenGenerator::cpythonGetattroFunctionName(const AbstractMetaClassCPtr &metaClass) { return cpythonBaseName(metaClass) + u"_getattro"_s; } QString ShibokenGenerator::cpythonGetterFunctionName(const QString &name, - const AbstractMetaClass *enclosingClass) + const AbstractMetaClassCPtr &enclosingClass) { - return cpythonBaseName(enclosingClass) + QStringLiteral("_get_") + name; + return cpythonBaseName(enclosingClass) + "_get_"_L1 + name; } QString ShibokenGenerator::cpythonSetterFunctionName(const QString &name, - const AbstractMetaClass *enclosingClass) + const AbstractMetaClassCPtr &enclosingClass) { - return cpythonBaseName(enclosingClass) + QStringLiteral("_set_") + name; + return cpythonBaseName(enclosingClass) + "_set_"_L1 + name; } QString ShibokenGenerator::cpythonGetterFunctionName(const AbstractMetaField &metaField) @@ -398,13 +483,13 @@ QString ShibokenGenerator::cpythonSetterFunctionName(const AbstractMetaField &me } QString ShibokenGenerator::cpythonGetterFunctionName(const QPropertySpec &property, - const AbstractMetaClass *metaClass) + const AbstractMetaClassCPtr &metaClass) { return cpythonGetterFunctionName(property.name(), metaClass); } QString ShibokenGenerator::cpythonSetterFunctionName(const QPropertySpec &property, - const AbstractMetaClass *metaClass) + const AbstractMetaClassCPtr &metaClass) { return cpythonSetterFunctionName(property.name(), metaClass); } @@ -439,15 +524,15 @@ QString ShibokenGenerator::cpythonFlagsName(const FlagsTypeEntryCPtr &flagsEntry QString ShibokenGenerator::cpythonFlagsName(const AbstractMetaEnum *metaEnum) { const auto flags = metaEnum->typeEntry()->flags(); - return flags.isNull() ? QString{} : cpythonFlagsName(flags); + return flags ? cpythonFlagsName(flags) : QString{}; } -QString ShibokenGenerator::cpythonSpecialCastFunctionName(const AbstractMetaClass *metaClass) +QString ShibokenGenerator::cpythonSpecialCastFunctionName(const AbstractMetaClassCPtr &metaClass) { return cpythonBaseName(metaClass->typeEntry()) + u"SpecialCastFunction"_s; } -QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaClass *metaClass, +QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaClassCPtr &metaClass, const QString &argName) { return cpythonWrapperCPtr(metaClass->typeEntry(), argName); @@ -457,7 +542,7 @@ QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaType &metaType, const QString &argName) { if (!metaType.isWrapperType()) - return QString(); + return {}; return u"reinterpret_cast< ::"_s + metaType.cppSignature() + u" *>(Shiboken::Conversions::cppPointer("_s + cpythonTypeNameExt(metaType) + u", reinterpret_cast<SbkObject *>("_s + argName + u")))"_s; @@ -468,29 +553,30 @@ QString ShibokenGenerator::cpythonWrapperCPtr(const TypeEntryCPtr &type, { if (!type->isWrapperType()) return QString(); - return u"reinterpret_cast< ::"_s + type->qualifiedCppName() + return u"reinterpret_cast< "_s + getFullTypeName(type) + u" *>(Shiboken::Conversions::cppPointer("_s + cpythonTypeNameExt(type) + u", reinterpret_cast<SbkObject *>("_s + argName + u")))"_s; } void ShibokenGenerator::writeToPythonConversion(TextStream & s, const AbstractMetaType &type, - const AbstractMetaClass * /* context */, + const AbstractMetaClassCPtr & /* context */, const QString &argumentName) { s << cpythonToPythonConversionFunction(type) << argumentName << ')'; } -void ShibokenGenerator::writeToCppConversion(TextStream &s, const AbstractMetaClass *metaClass, +void ShibokenGenerator::writeToCppConversion(TextStream &s, + const AbstractMetaClassCPtr &metaClass, const QString &inArgName, const QString &outArgName) { s << cpythonToCppConversionFunction(metaClass) << inArgName << ", &" << outArgName << ')'; } void ShibokenGenerator::writeToCppConversion(TextStream &s, const AbstractMetaType &type, - const AbstractMetaClass *context, const QString &inArgName, + const QString &inArgName, const QString &outArgName) { - s << cpythonToCppConversionFunction(type, context) << inArgName << ", &" << outArgName << ')'; + s << cpythonToCppConversionFunction(type) << inArgName << ", &" << outArgName << ')'; } bool ShibokenGenerator::shouldRejectNullPointerArgument(const AbstractMetaFunctionCPtr &func, @@ -525,11 +611,29 @@ QString ShibokenGenerator::cpythonBaseName(const AbstractMetaType &type) return cpythonBaseName(type.typeEntry()); } -QString ShibokenGenerator::cpythonBaseName(const AbstractMetaClass *metaClass) +QString ShibokenGenerator::cpythonBaseName(const AbstractMetaClassCPtr &metaClass) { return cpythonBaseName(metaClass->typeEntry()); } +QString ShibokenGenerator::containerCpythonBaseName(const ContainerTypeEntryCPtr &ctype) +{ + switch (ctype->containerKind()) { + case ContainerTypeEntry::SetContainer: + return u"PySet"_s; + case ContainerTypeEntry::MapContainer: + case ContainerTypeEntry::MultiMapContainer: + return u"PyDict"_s; + case ContainerTypeEntry::ListContainer: + case ContainerTypeEntry::PairContainer: + case ContainerTypeEntry::SpanContainer: + break; + default: + Q_ASSERT(false); + } + return cPySequenceT; +} + QString ShibokenGenerator::cpythonBaseName(const TypeEntryCPtr &type) { QString baseName; @@ -540,36 +644,19 @@ QString ShibokenGenerator::cpythonBaseName(const TypeEntryCPtr &type) baseName = ptype->hasTargetLangApiType() ? ptype->targetLangApiName() : pythonPrimitiveTypeName(ptype->name()); } else if (type->isEnum()) { - baseName = cpythonEnumName(qSharedPointerCast<const EnumTypeEntry>(type)); + baseName = cpythonEnumName(std::static_pointer_cast<const EnumTypeEntry>(type)); } else if (type->isFlags()) { - baseName = cpythonFlagsName(qSharedPointerCast<const FlagsTypeEntry>(type)); + baseName = cpythonFlagsName(std::static_pointer_cast<const FlagsTypeEntry>(type)); } else if (type->isContainer()) { - const auto ctype = qSharedPointerCast<const ContainerTypeEntry>(type); - switch (ctype->containerKind()) { - case ContainerTypeEntry::ListContainer: - //baseName = "PyList"; - //break; - case ContainerTypeEntry::PairContainer: - //baseName = "PyTuple"; - baseName = cPySequenceT(); - break; - case ContainerTypeEntry::SetContainer: - baseName = u"PySet"_s; - break; - case ContainerTypeEntry::MapContainer: - case ContainerTypeEntry::MultiMapContainer: - baseName = u"PyDict"_s; - break; - default: - Q_ASSERT(false); - } + const auto ctype = std::static_pointer_cast<const ContainerTypeEntry>(type); + baseName = containerCpythonBaseName(ctype); } else { - baseName = cPyObjectT(); + baseName = cPyObjectT; } return baseName.replace(u"::"_s, u"_"_s); } -QString ShibokenGenerator::cpythonTypeName(const AbstractMetaClass *metaClass) +QString ShibokenGenerator::cpythonTypeName(const AbstractMetaClassCPtr &metaClass) { return cpythonTypeName(metaClass->typeEntry()); } @@ -579,12 +666,6 @@ QString ShibokenGenerator::cpythonTypeName(const TypeEntryCPtr &type) return cpythonBaseName(type) + u"_TypeF()"_s; } -QString ShibokenGenerator::cpythonTypeNameExt(const TypeEntryCPtr &type) -{ - return cppApiVariableName(type->targetLangPackage()) + u'[' - + getTypeIndexVariableName(type) + u']'; -} - QString ShibokenGenerator::converterObject(const AbstractMetaType &type) { if (type.isCString()) @@ -593,7 +674,7 @@ QString ShibokenGenerator::converterObject(const AbstractMetaType &type) return u"Shiboken::Conversions::PrimitiveTypeConverter<void *>()"_s; const AbstractMetaTypeList nestedArrayTypes = type.nestedArrayTypes(); if (!nestedArrayTypes.isEmpty() && nestedArrayTypes.constLast().isCppPrimitive()) { - return QStringLiteral("Shiboken::Conversions::ArrayTypeConverter<") + return "Shiboken::Conversions::ArrayTypeConverter<"_L1 + nestedArrayTypes.constLast().minimalSignature() + u">("_s + QString::number(nestedArrayTypes.size()) + u')'; @@ -615,23 +696,20 @@ QString ShibokenGenerator::converterObject(const TypeEntryCPtr &type) if (type->isWrapperType()) return QString::fromLatin1("PepType_SOTP(reinterpret_cast<PyTypeObject *>(%1))->converter") .arg(cpythonTypeNameExt(type)); - if (type->isEnum()) + if (type->isEnum() || type->isFlags()) return QString::fromLatin1("PepType_SETP(reinterpret_cast<SbkEnumType *>(%1))->converter") .arg(cpythonTypeNameExt(type)); - if (type->isFlags()) - return QString::fromLatin1("PepType_PFTP(reinterpret_cast<PySideQFlagsType *>(%1))->converter") - .arg(cpythonTypeNameExt(type)); if (type->isArray()) { qDebug() << "Warning: no idea how to handle the Qt5 type " << type->qualifiedCppName(); - return QString(); + return {}; } /* the typedef'd primitive types case */ - auto pte = qSharedPointerDynamicCast<const PrimitiveTypeEntry>(type); - if (pte.isNull()) { + auto pte = std::dynamic_pointer_cast<const PrimitiveTypeEntry>(type); + if (!pte) { qDebug() << "Warning: the Qt5 primitive type is unknown" << type->qualifiedCppName(); - return QString(); + return {}; } pte = basicReferencedTypeEntry(pte); if (pte->isPrimitive() && !isCppPrimitive(pte) && !pte->customConversion()) { @@ -643,13 +721,29 @@ QString ShibokenGenerator::converterObject(const TypeEntryCPtr &type) + u'[' + getTypeIndexVariableName(type) + u']'; } -QString ShibokenGenerator::cpythonTypeNameExt(const AbstractMetaType &type) +QString ShibokenGenerator::cpythonTypeNameExtSet(const TypeEntryCPtr &type) +{ + return cppApiVariableName(type->targetLangPackage()) + u'[' + + getTypeIndexVariableName(type) + "].type"_L1; +} + +QString ShibokenGenerator::cpythonTypeNameExtSet(const AbstractMetaType &type) { return cppApiVariableName(type.typeEntry()->targetLangPackage()) + u'[' - + getTypeIndexVariableName(type) + u']'; + + getTypeIndexVariableName(type) + "].type"_L1; +} + +QString ShibokenGenerator::cpythonTypeNameExt(const TypeEntryCPtr &type) +{ + return "Shiboken::Module::get("_L1 + cppApiVariableName(type->targetLangPackage()) + + u'[' + getTypeIndexVariableName(type) + "])"_L1; } -static inline QString unknownOperator() { return QStringLiteral("__UNKNOWN_OPERATOR__"); } +QString ShibokenGenerator::cpythonTypeNameExt(const AbstractMetaType &type) +{ + return u"Shiboken::Module::get("_s + cppApiVariableName(type.typeEntry()->targetLangPackage()) + + u'[' + getTypeIndexVariableName(type) + "])"_L1; +} QString ShibokenGenerator::fixedCppTypeName(const TargetToNativeConversion &toNative) { @@ -698,8 +792,8 @@ QString ShibokenGenerator::pythonOperatorFunctionName(const AbstractMetaFunction { QString op = Generator::pythonOperatorFunctionName(func->originalName()); if (op.isEmpty()) { - qCWarning(lcShiboken).noquote().nospace() << msgUnknownOperator(func.data()); - return unknownOperator(); + qCWarning(lcShiboken).noquote().nospace() << msgUnknownOperator(func.get()); + return "__UNKNOWN_OPERATOR__"_L1; } if (func->arguments().isEmpty()) { if (op == u"__sub__") @@ -716,8 +810,8 @@ QString ShibokenGenerator::pythonOperatorFunctionName(const AbstractMetaFunction bool ShibokenGenerator::isNumber(const QString &cpythonApiName) { - return cpythonApiName == pyFloatT() || cpythonApiName == pyLongT() - || cpythonApiName == pyBoolT(); + return cpythonApiName == pyFloatT || cpythonApiName == pyLongT + || cpythonApiName == pyBoolT; } static std::optional<TypeSystem::CPythonType> @@ -728,7 +822,7 @@ static std::optional<TypeSystem::CPythonType> const auto cte = t->targetLangApiType(); if (cte->type() != TypeEntry::PythonType) return {}; - return qSharedPointerCast<const PythonTypeEntry>(cte)->cPythonType(); + return std::static_pointer_cast<const PythonTypeEntry>(cte)->cPythonType(); } bool ShibokenGenerator::isNumber(const TypeEntryCPtr &type) @@ -764,7 +858,7 @@ bool ShibokenGenerator::isPyInt(const TypeEntryCPtr &type) if (!cPythonTypeOpt.has_value()) { const auto &mapping = primitiveTypesCorrespondences(); const auto it = mapping.constFind(pte->name()); - return it != mapping.cend() && it.value() == pyLongT(); + return it != mapping.cend() && it.value() == pyLongT; } return cPythonTypeOpt.value() == TypeSystem::CPythonType::Integer; } @@ -784,7 +878,7 @@ QString ShibokenGenerator::cpythonCheckFunction(AbstractMetaType metaType) { const auto typeEntry = metaType.typeEntry(); if (typeEntry->isCustom()) { - const auto cte = qSharedPointerCast<const CustomTypeEntry>(typeEntry); + const auto cte = std::static_pointer_cast<const CustomTypeEntry>(typeEntry); if (cte->hasCheckFunction()) return cte->checkFunction(); throw Exception(msgUnknownCheckFunction(typeEntry)); @@ -801,7 +895,7 @@ QString ShibokenGenerator::cpythonCheckFunction(AbstractMetaType metaType) if (typeEntry->isContainer()) { QString typeCheck = u"Shiboken::Conversions::"_s; ContainerTypeEntry::ContainerKind type = - qSharedPointerCast<const ContainerTypeEntry>(typeEntry)->containerKind(); + std::static_pointer_cast<const ContainerTypeEntry>(typeEntry)->containerKind(); if (type == ContainerTypeEntry::ListContainer || type == ContainerTypeEntry::SetContainer) { const QString containerType = type == ContainerTypeEntry::SetContainer @@ -852,7 +946,7 @@ QString ShibokenGenerator::cpythonCheckFunction(AbstractMetaType metaType) QString ShibokenGenerator::cpythonCheckFunction(TypeEntryCPtr type) { if (type->isCustom()) { - const auto cte = qSharedPointerCast<const CustomTypeEntry>(type); + const auto cte = std::static_pointer_cast<const CustomTypeEntry>(type); if (cte->hasCheckFunction()) return cte->checkFunction(); throw Exception(msgUnknownCheckFunction(type)); @@ -881,7 +975,7 @@ QString ShibokenGenerator::cpythonIsConvertibleFunction(const TypeEntryCPtr &typ QString result = u"Shiboken::Conversions::"_s; bool isValue = false; if (type->isValue()) { - const auto cte = qSharedPointerCast<const ComplexTypeEntry>(type); + const auto cte = std::static_pointer_cast<const ComplexTypeEntry>(type); isValue = !cte->isValueTypeWithCopyConstructorOnly(); } result += isValue ? u"isPythonToCppValueConvertible"_s @@ -893,11 +987,11 @@ QString ShibokenGenerator::cpythonIsConvertibleFunction(const TypeEntryCPtr &typ .arg(converterObject(type)); } -QString ShibokenGenerator::cpythonIsConvertibleFunction(AbstractMetaType metaType) +QString ShibokenGenerator::cpythonIsConvertibleFunction(const AbstractMetaType &metaType) { const auto typeEntry = metaType.typeEntry(); if (typeEntry->isCustom()) { - const auto cte = qSharedPointerCast<const CustomTypeEntry>(typeEntry); + const auto cte = std::static_pointer_cast<const CustomTypeEntry>(typeEntry); if (cte->hasCheckFunction()) return cte->checkFunction(); throw Exception(msgUnknownCheckFunction(typeEntry)); @@ -910,12 +1004,14 @@ QString ShibokenGenerator::cpythonIsConvertibleFunction(AbstractMetaType metaTyp return result; } if (metaType.isWrapperType()) { - if (metaType.isPointer() || metaType.isValueTypeWithCopyConstructorOnly()) + if (metaType.isPointer() || metaType.isValueTypeWithCopyConstructorOnly()) { result += u"pythonToCppPointerConversion"_s; - else if (metaType.referenceType() == LValueReference) + } else if (metaType.referenceType() == LValueReference + || (metaType.referenceType() == RValueReference && typeEntry->isObject())) { result += u"pythonToCppReferenceConversion"_s; - else + } else { result += u"pythonToCppValueConversion"_s; + } result += u'(' + cpythonTypeNameExt(metaType) + u", "_s; return result; } @@ -938,26 +1034,24 @@ QString ShibokenGenerator::cpythonIsConvertibleFunction(const AbstractMetaArgume return cpythonIsConvertibleFunction(metaArg.type()); } -QString ShibokenGenerator::cpythonToCppConversionFunction(const AbstractMetaClass *metaClass) +QString ShibokenGenerator::cpythonToCppConversionFunction(const AbstractMetaClassCPtr &metaClass) { return u"Shiboken::Conversions::pythonToCppPointer("_s + cpythonTypeNameExt(metaClass->typeEntry()) + u", "_s; } -QString ShibokenGenerator::cpythonToCppConversionFunction(const AbstractMetaType &type, - const AbstractMetaClass * /* context */) +QString ShibokenGenerator::cpythonToCppConversionFunction(const AbstractMetaType &type) { if (type.isWrapperType()) { return u"Shiboken::Conversions::pythonToCpp"_s + (type.isPointer() ? u"Pointer"_s : u"Copy"_s) + u'(' + cpythonTypeNameExt(type) + u", "_s; } - return QStringLiteral("Shiboken::Conversions::pythonToCppCopy(%1, ") - .arg(converterObject(type)); + return "Shiboken::Conversions::pythonToCppCopy("_L1 + + converterObject(type) + ", "_L1; } -QString ShibokenGenerator::cpythonToPythonConversionFunction(const AbstractMetaType &type, - const AbstractMetaClass * /* context */) +QString ShibokenGenerator::cpythonToPythonConversionFunction(const AbstractMetaType &type) { if (type.isWrapperType()) { QString conversion; @@ -976,12 +1070,13 @@ QString ShibokenGenerator::cpythonToPythonConversionFunction(const AbstractMetaT result += u'&'; return result; } - return QStringLiteral("Shiboken::Conversions::copyToPython(%1, %2") - .arg(converterObject(type), - (type.isCString() || type.isVoidPointer()) ? QString() : u"&"_s); + + const auto indirections = type.indirections() - 1; + return u"Shiboken::Conversions::copyToPython("_s + converterObject(type) + + u", "_s + AbstractMetaType::dereferencePrefix(indirections); } -QString ShibokenGenerator::cpythonToPythonConversionFunction(const AbstractMetaClass *metaClass) +QString ShibokenGenerator::cpythonToPythonConversionFunction(const AbstractMetaClassCPtr &metaClass) { return cpythonToPythonConversionFunction(metaClass->typeEntry()); } @@ -1009,13 +1104,14 @@ QString ShibokenGenerator::argumentString(const AbstractMetaFunctionCPtr &func, auto type = options.testFlag(OriginalTypeDescription) ? argument.type() : argument.modifiedType(); + QString arg = translateType(type, func->implementingClass(), options); if (argument.isTypeModified()) arg.replace(u'$', u'.'); // Haehh? // "int a", "int a[]" - const int arrayPos = arg.indexOf(u'['); + const auto arrayPos = arg.indexOf(u'['); if (arrayPos != -1) arg.insert(arrayPos, u' ' + argument.name()); else @@ -1051,6 +1147,10 @@ void ShibokenGenerator::writeFunctionArguments(TextStream &s, Options options) const { int argUsed = 0; + if (func->isUserAddedPythonOverride()) { + s << "Shiboken::GilState &gil, PyObject *" << PYTHON_OVERRIDE_VAR; + argUsed += 2; + } for (const auto &arg : func->arguments()) { if (options.testFlag(Generator::SkipRemovedArguments) && arg.isModifiedRemoved()) continue; @@ -1062,7 +1162,7 @@ void ShibokenGenerator::writeFunctionArguments(TextStream &s, } } -GeneratorContext ShibokenGenerator::contextForClass(const AbstractMetaClass *c) const +GeneratorContext ShibokenGenerator::contextForClass(const AbstractMetaClassCPtr &c) const { GeneratorContext result = Generator::contextForClass(c); if (shouldGenerateCppWrapper(c)) { @@ -1087,6 +1187,8 @@ QString ShibokenGenerator::functionSignature(const AbstractMetaFunctionCPtr &fun { StringStream s(TextStream::Language::Cpp); // The actual function + if (!options.testFlag(Option::SkipDefaultValues) && func->isStatic()) // Declaration + s << "static "; if (func->isEmptyFunction() || func->needsReturnType()) s << functionReturnType(func, options) << ' '; else @@ -1151,7 +1253,7 @@ void ShibokenGenerator::writeFunctionCall(TextStream &s, ShibokenGenerator::ExtendedConverterData ShibokenGenerator::getExtendedConverters() const { ExtendedConverterData extConvs; - for (auto metaClass : api().classes()) { + for (const auto &metaClass : api().classes()) { // Use only the classes for the current module. if (!shouldGenerate(metaClass->typeEntry())) continue; @@ -1188,7 +1290,7 @@ static QString getArgumentsFromMethodCall(const QString &str) // For more information check this: // http://perl.plover.com/yak/regex/samples/slide083.html static QLatin1String funcCall("%CPPSELF.%FUNCTION_NAME"); - int pos = str.indexOf(funcCall); + auto pos = str.indexOf(funcCall); if (pos == -1) return QString(); pos = pos + funcCall.size(); @@ -1232,7 +1334,7 @@ void ShibokenGenerator::processClassCodeSnip(QString &code, const GeneratorConte code.replace(u"%TYPE"_s, className); code.replace(u"%CPPTYPE"_s, metaClass->name()); - processCodeSnip(code); + processCodeSnip(code, context.effectiveClassName()); } void ShibokenGenerator::processCodeSnip(QString &code) const @@ -1250,6 +1352,15 @@ void ShibokenGenerator::processCodeSnip(QString &code) const replaceTypeCheckTypeSystemVariable(code); } +void ShibokenGenerator::processCodeSnip(QString &code, const QString &context) const +{ + try { + processCodeSnip(code); + } catch (const std::exception &e) { + throw Exception(msgSnippetError(context, e.what())); + } +} + ShibokenGenerator::ArgumentVarReplacementList ShibokenGenerator::getArgumentReplacement(const AbstractMetaFunctionCPtr &func, bool usePyArgs, TypeSystem::Language language, @@ -1270,7 +1381,7 @@ ShibokenGenerator::ArgumentVarReplacementList if (argRemoved && hasConversionRule) argValue = arg.name() + CONV_RULE_OUT_VAR_SUFFIX; else if (argRemoved || (lastArg && arg.argumentIndex() > lastArg->argumentIndex())) - argValue = CPP_ARG_REMOVED + QString::number(i); + argValue = CPP_ARG_REMOVED(i); if (!argRemoved && argValue.isEmpty()) { int argPos = i - removed; AbstractMetaType type = arg.modifiedType(); @@ -1280,7 +1391,7 @@ ShibokenGenerator::ArgumentVarReplacementList } else { argValue = hasConversionRule ? arg.name() + CONV_RULE_OUT_VAR_SUFFIX - : CPP_ARG + QString::number(argPos); + : CPP_ARG_N(argPos); const auto generatorArg = GeneratorArgument::fromMetaType(type); AbstractMetaType::applyDereference(&argValue, generatorArg.indirections); } @@ -1322,7 +1433,7 @@ void ShibokenGenerator::writeCodeSnips(TextStream &s, static void replacePyArg0(TypeSystem::Language language, QString *code) { - static const QString pyArg0 = u"%PYARG_0"_s; + static constexpr auto pyArg0 = "%PYARG_0"_L1; if (!code->contains(pyArg0)) return; @@ -1361,18 +1472,18 @@ void ShibokenGenerator::writeCodeSnips(TextStream &s, // Replace %PYARG_# variables. replacePyArg0(language, &code); - static const QRegularExpression pyArgsRegex(QStringLiteral("%PYARG_(\\d+)")); + static const QRegularExpression pyArgsRegex("%PYARG_(\\d+)"_L1); Q_ASSERT(pyArgsRegex.isValid()); if (language == TypeSystem::TargetLangCode) { if (usePyArgs) { code.replace(pyArgsRegex, PYTHON_ARGS + u"[\\1-1]"_s); } else { - static const QRegularExpression pyArgsRegexCheck(QStringLiteral("%PYARG_([2-9]+)")); + static const QRegularExpression pyArgsRegexCheck("%PYARG_([2-9]+)"_L1); Q_ASSERT(pyArgsRegexCheck.isValid()); const QRegularExpressionMatch match = pyArgsRegexCheck.match(code); if (match.hasMatch()) { qCWarning(lcShiboken).noquote().nospace() - << msgWrongIndex("%PYARG", match.captured(1), func.data()); + << msgWrongIndex("%PYARG", match.captured(1), func.get()); return; } code.replace(u"%PYARG_1"_s, PYTHON_ARG); @@ -1380,12 +1491,12 @@ void ShibokenGenerator::writeCodeSnips(TextStream &s, } else { // Replaces the simplest case of attribution to a // Python argument on the binding virtual method. - static const QRegularExpression pyArgsAttributionRegex(QStringLiteral("%PYARG_(\\d+)\\s*=[^=]\\s*([^;]+)")); + static const QRegularExpression pyArgsAttributionRegex("%PYARG_(\\d+)\\s*=[^=]\\s*([^;]+)"_L1); Q_ASSERT(pyArgsAttributionRegex.isValid()); code.replace(pyArgsAttributionRegex, u"PyTuple_SET_ITEM("_s - + PYTHON_ARGS + u", \\1-1, \\2)"_s); + + PYTHON_ARGS + u".object(), \\1-1, \\2)"_s); code.replace(pyArgsRegex, u"PyTuple_GET_ITEM("_s - + PYTHON_ARGS + u", \\1-1)"_s); + + PYTHON_ARGS + u".object(), \\1-1)"_s); } // Replace %ARG#_TYPE variables. @@ -1397,13 +1508,13 @@ void ShibokenGenerator::writeCodeSnips(TextStream &s, code.replace(argTypeVar, argTypeVal); } - static const QRegularExpression cppArgTypeRegexCheck(QStringLiteral("%ARG(\\d+)_TYPE")); + static const QRegularExpression cppArgTypeRegexCheck("%ARG(\\d+)_TYPE"_L1); Q_ASSERT(cppArgTypeRegexCheck.isValid()); QRegularExpressionMatchIterator rit = cppArgTypeRegexCheck.globalMatch(code); while (rit.hasNext()) { QRegularExpressionMatch match = rit.next(); qCWarning(lcShiboken).noquote().nospace() - << msgWrongIndex("%ARG#_TYPE", match.captured(1), func.data()); + << msgWrongIndex("%ARG#_TYPE", match.captured(1), func.get()); } // Replace template variable for return variable name. @@ -1489,7 +1600,7 @@ void ShibokenGenerator::writeCodeSnips(TextStream &s, QStringList args; for (const ArgumentVarReplacementPair &pair : argReplacements) { - if (pair.second.startsWith(CPP_ARG_REMOVED)) + if (pair.second.startsWith(CPP_ARG_REMOVED_PREFIX)) continue; args << pair.second; } @@ -1539,8 +1650,8 @@ void ShibokenGenerator::writeCodeSnips(TextStream &s, if (isProtected) { code.replace(u"%TYPE::%FUNCTION_NAME"_s, - QStringLiteral("%1::%2_protected") - .arg(wrapperName(func->ownerClass()), func->originalName())); + wrapperName(func->ownerClass()) + "::"_L1 + + func->originalName() + "_protected"_L1); code.replace(u"%FUNCTION_NAME"_s, func->originalName() + u"_protected"_s); } @@ -1554,7 +1665,7 @@ void ShibokenGenerator::writeCodeSnips(TextStream &s, replaceTemplateVariables(code, func); - processCodeSnip(code); + processCodeSnip(code, func->classQualifiedSignature()); s << "// Begin code injection\n" << code << "// End of code injection\n\n"; } @@ -1562,7 +1673,7 @@ void ShibokenGenerator::writeCodeSnips(TextStream &s, // and false if it is a variable. static bool isVariable(const QString &code) { - static const QRegularExpression expr(QStringLiteral("^\\s*\\*?\\s*[A-Za-z_][A-Za-z_0-9.]*\\s*(?:\\[[^\\[]+\\])*$")); + static const QRegularExpression expr("^\\s*\\*?\\s*[A-Za-z_][A-Za-z_0-9.]*\\s*(?:\\[[^\\[]+\\])*$"_L1); Q_ASSERT(expr.isValid()); return expr.match(code.trimmed()).hasMatch(); } @@ -1623,7 +1734,7 @@ const QHash<int, QString> &ShibokenGenerator::typeSystemConvName() return result; } -using StringPair = QPair<QString, QString>; +using StringPair = std::pair<QString, QString>; void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVariable converterVariable, QString &code) const @@ -1658,9 +1769,6 @@ void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVa varType = miniNormalizer(varType); QString varName = list.at(1).trimmed(); if (!varType.isEmpty()) { - const QString conversionSignature = conversionType.cppSignature(); - if (varType != u"auto" && varType != conversionSignature) - throw Exception(msgConversionTypesDiffer(varType, conversionSignature)); c << getFullTypeName(conversionType) << ' ' << varName << minimalConstructorExpression(api(), conversionType) << ";\n"; } @@ -1677,7 +1785,7 @@ void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVa case TypeSystemCheckFunction: conversion = cpythonCheckFunction(conversionType); if (conversionType.typeEntry()->isPrimitive() - && (conversionType.typeEntry()->name() == cPyObjectT() + && (conversionType.typeEntry()->name() == cPyObjectT || !conversion.endsWith(u' '))) { conversion += u'('; break; @@ -1708,7 +1816,7 @@ void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVa } } } - replacements.append(qMakePair(conversionString, conversion)); + replacements.append(std::make_pair(conversionString, conversion)); } for (const StringPair &rep : std::as_const(replacements)) code.replace(rep.first, rep.second); @@ -1735,12 +1843,13 @@ bool ShibokenGenerator::injectedCodeCallsCppFunction(const GeneratorContext &con return func->injectedCodeContains(wrappedCtorCall); } -bool ShibokenGenerator::useOverrideCaching(const AbstractMetaClass *metaClass) +bool ShibokenGenerator::useOverrideCaching(const AbstractMetaClassCPtr &metaClass) { return metaClass->isPolymorphic(); } -ShibokenGenerator::AttroCheck ShibokenGenerator::checkAttroFunctionNeeds(const AbstractMetaClass *metaClass) const +ShibokenGenerator::AttroCheck ShibokenGenerator::checkAttroFunctionNeeds( + const AbstractMetaClassCPtr &metaClass) { AttroCheck result; if (metaClass->typeEntry()->isSmartPointer()) { @@ -1752,7 +1861,7 @@ ShibokenGenerator::AttroCheck ShibokenGenerator::checkAttroFunctionNeeds(const A FunctionQueryOption::GetAttroFunction)) { result |= AttroCheckFlag::GetattroUser; } - if (usePySideExtensions() && metaClass->qualifiedCppName() == qObjectT()) + if (usePySideExtensions() && metaClass->qualifiedCppName() == qObjectT) result |= AttroCheckFlag::SetattroQObject; if (useOverrideCaching(metaClass)) result |= AttroCheckFlag::SetattroMethodOverride; @@ -1764,14 +1873,14 @@ ShibokenGenerator::AttroCheck ShibokenGenerator::checkAttroFunctionNeeds(const A // QObject, the property code needs to be generated, too. if ((result & AttroCheckFlag::SetattroMask) != 0 && !result.testFlag(AttroCheckFlag::SetattroQObject) - && metaClass->isQObject()) { + && isQObject(metaClass)) { result |= AttroCheckFlag::SetattroQObject; } } return result; } -bool ShibokenGenerator::classNeedsGetattroFunctionImpl(const AbstractMetaClass *metaClass) +bool ShibokenGenerator::classNeedsGetattroFunctionImpl(const AbstractMetaClassCPtr &metaClass) { if (!metaClass) return false; @@ -1797,7 +1906,7 @@ bool ShibokenGenerator::classNeedsGetattroFunctionImpl(const AbstractMetaClass * } AbstractMetaFunctionCList - ShibokenGenerator::getMethodsWithBothStaticAndNonStaticMethods(const AbstractMetaClass *metaClass) + ShibokenGenerator::getMethodsWithBothStaticAndNonStaticMethods(const AbstractMetaClassCPtr &metaClass) { AbstractMetaFunctionCList methods; if (metaClass) { @@ -1821,7 +1930,8 @@ AbstractMetaFunctionCList return methods; } -const AbstractMetaClass *ShibokenGenerator::getMultipleInheritingClass(const AbstractMetaClass *metaClass) +AbstractMetaClassCPtr + ShibokenGenerator::getMultipleInheritingClass(const AbstractMetaClassCPtr &metaClass) { if (!metaClass || metaClass->baseClassNames().isEmpty()) return nullptr; @@ -1832,20 +1942,20 @@ const AbstractMetaClass *ShibokenGenerator::getMultipleInheritingClass(const Abs QString ShibokenGenerator::getModuleHeaderFileBaseName(const QString &moduleName) { - return moduleCppPrefix(moduleName).toLower() + QStringLiteral("_python"); + return moduleCppPrefix(moduleName).toLower() + "_python"_L1; } QString ShibokenGenerator::getModuleHeaderFileName(const QString &moduleName) { - return getModuleHeaderFileBaseName(moduleName) + QStringLiteral(".h"); + return getModuleHeaderFileBaseName(moduleName) + ".h"_L1; } QString ShibokenGenerator::getPrivateModuleHeaderFileName(const QString &moduleName) { - return getModuleHeaderFileBaseName(moduleName) + QStringLiteral("_p.h"); + return getModuleHeaderFileBaseName(moduleName) + "_p.h"_L1; } -IncludeGroupList ShibokenGenerator::classIncludes(const AbstractMetaClass *metaClass) const +IncludeGroupList ShibokenGenerator::classIncludes(const AbstractMetaClassCPtr &metaClass) const { IncludeGroupList result; const auto typeEntry = metaClass->typeEntry(); @@ -1859,9 +1969,9 @@ IncludeGroupList ShibokenGenerator::classIncludes(const AbstractMetaClass *metaC result.append({u"Argument includes"_s, typeEntry->argumentIncludes()}); const auto implicitConvs = implicitConversions(typeEntry); - for (auto &f : implicitConvs) { + for (const auto &f : implicitConvs) { if (f->isConversionOperator()) { - auto *source = f->ownerClass(); + const auto source = f->ownerClass(); Q_ASSERT(source); result.back().append(source->typeEntry()->include()); } @@ -1917,29 +2027,47 @@ ShibokenGenerator::FunctionGroups ShibokenGenerator::getGlobalFunctionGroups() c { FunctionGroups results; insertIntoFunctionGroups(api().globalFunctions(), &results); - for (auto nsp : invisibleTopNamespaces()) + for (const auto &nsp : invisibleTopNamespaces()) insertIntoFunctionGroups(nsp->functions(), &results); return results; } -const GeneratorClassInfoCacheEntry &ShibokenGenerator::getGeneratorClassInfo(const AbstractMetaClass *scope) +const GeneratorClassInfoCacheEntry & + ShibokenGenerator::getGeneratorClassInfo(const AbstractMetaClassCPtr &scope) { auto cache = generatorClassInfoCache(); auto it = cache->find(scope); if (it == cache->end()) { it = cache->insert(scope, {}); - it.value().functionGroups = getFunctionGroupsImpl(scope); - it.value().needsGetattroFunction = classNeedsGetattroFunctionImpl(scope); + auto &entry = it.value(); + entry.functionGroups = getFunctionGroupsImpl(scope); + entry.needsGetattroFunction = classNeedsGetattroFunctionImpl(scope); + entry.numberProtocolOperators = getNumberProtocolOperators(scope); + entry.boolCastFunctionO = getBoolCast(scope); } return it.value(); } -ShibokenGenerator::FunctionGroups ShibokenGenerator::getFunctionGroups(const AbstractMetaClass *scope) +ShibokenGenerator::FunctionGroups + ShibokenGenerator::getFunctionGroups(const AbstractMetaClassCPtr &scope) { Q_ASSERT(scope); return getGeneratorClassInfo(scope).functionGroups; } +QList<AbstractMetaFunctionCList> + ShibokenGenerator::numberProtocolOperators(const AbstractMetaClassCPtr &scope) +{ + Q_ASSERT(scope); + return getGeneratorClassInfo(scope).numberProtocolOperators; +} + +BoolCastFunctionOptional ShibokenGenerator::boolCast(const AbstractMetaClassCPtr &scope) +{ + Q_ASSERT(scope); + return getGeneratorClassInfo(scope).boolCastFunctionO; +} + // Use non-const overloads only, for example, "foo()" and "foo()const" // the second is removed. static void removeConstOverloads(AbstractMetaFunctionCList *overloads) @@ -1948,7 +2076,7 @@ static void removeConstOverloads(AbstractMetaFunctionCList *overloads) const auto &f = overloads->at(i); if (f->isConstant()) { for (qsizetype c = 0, size = overloads->size(); c < size; ++c) { - if (f->isConstOverloadOf(overloads->at(c).data())) { + if (f->isConstOverloadOf(overloads->at(c).get())) { overloads->removeAt(i); break; } @@ -1957,7 +2085,8 @@ static void removeConstOverloads(AbstractMetaFunctionCList *overloads) } } -ShibokenGenerator::FunctionGroups ShibokenGenerator::getFunctionGroupsImpl(const AbstractMetaClass *scope) +ShibokenGenerator::FunctionGroups + ShibokenGenerator::getFunctionGroupsImpl(const AbstractMetaClassCPtr &scope) { AbstractMetaFunctionCList lst = scope->functions(); scope->getFunctionsFromInvisibleNamespacesToBeGenerated(&lst); @@ -1988,13 +2117,156 @@ ShibokenGenerator::FunctionGroups ShibokenGenerator::getFunctionGroupsImpl(const return results; } +static bool removeNumberProtocolOperator(const AbstractMetaFunctionCPtr &f) +{ + return !f->generateBinding() + || (f->ownerClass() != f->implementingClass() && !f->isAbstract()); +} + +QList<AbstractMetaFunctionCList> + ShibokenGenerator::getNumberProtocolOperators(const AbstractMetaClassCPtr &metaClass) +{ + QList<AbstractMetaFunctionCList> result; + if (metaClass->isNamespace()) + return result; + result = filterGroupedOperatorFunctions( + metaClass, + OperatorQueryOption::ArithmeticOp + | OperatorQueryOption::IncDecrementOp + | OperatorQueryOption::LogicalOp + | OperatorQueryOption::BitwiseOp + | OperatorQueryOption::ConversionOp); + + for (auto i = result.size() - 1; i >= 0; --i) { + AbstractMetaFunctionCList &l = result[i]; + auto rend = std::remove_if(l.begin(), l.end(), removeNumberProtocolOperator); + l.erase(rend, l.end()); + if (l.isEmpty()) + result.removeAt(i); + } + + return result; +} + +BoolCastFunctionOptional +ShibokenGenerator::getBoolCast(const AbstractMetaClassCPtr &metaClass) +{ + if (metaClass->isNamespace()) + return std::nullopt; + + const auto te = metaClass->typeEntry(); + if (te->isSmartPointer()) { + auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(te); + + auto valueCheckMethod = ste->valueCheckMethod(); + if (!valueCheckMethod.isEmpty()) { + const auto func = metaClass->findFunction(valueCheckMethod); + if (!func) + throw Exception(msgMethodNotFound(metaClass, valueCheckMethod)); + return BoolCastFunction{func, false}; + } + + auto nullCheckMethod = ste->nullCheckMethod(); + if (!nullCheckMethod.isEmpty()) { + const auto func = metaClass->findFunction(nullCheckMethod); + if (!func) + throw Exception(msgMethodNotFound(metaClass, nullCheckMethod)); + return BoolCastFunction{func, true}; + } + } + + auto mode = te->operatorBoolMode(); + if (useOperatorBoolAsNbBool() + ? mode != TypeSystem::BoolCast::Disabled : mode == TypeSystem::BoolCast::Enabled) { + const auto func = metaClass->findOperatorBool(); + if (func) + return BoolCastFunction{func, false}; + } + + mode = te->isNullMode(); + if (useIsNullAsNbBool() + ? mode != TypeSystem::BoolCast::Disabled : mode == TypeSystem::BoolCast::Enabled) { + const auto func = metaClass->findQtIsNullMethod(); + if (func) + return BoolCastFunction{func, true}; + } + return std::nullopt; +} + +static bool isInplaceAdd(const AbstractMetaFunctionCPtr &func) +{ + return func->name() == u"operator+="; +} + +static bool isIncrementOperator(const AbstractMetaFunctionCPtr &func) +{ + return func->functionType() == AbstractMetaFunction::IncrementOperator; +} + +static bool isDecrementOperator(const AbstractMetaFunctionCPtr &func) +{ + return func->functionType() == AbstractMetaFunction::DecrementOperator; +} + +// Filter predicate for operator functions +static bool skipOperatorFunc(const AbstractMetaFunctionCPtr &func) +{ + if (func->isModifiedRemoved() || func->usesRValueReferences()) + return true; + const auto &name = func->name(); + return name == u"operator[]" || name == u"operator->" || name == u"operator!" + || name == u"operator/="; // __idiv__ is not needed in Python3 +} + +QList<AbstractMetaFunctionCList> +ShibokenGenerator::filterGroupedOperatorFunctions(const AbstractMetaClassCPtr &metaClass, + OperatorQueryOptions query) +{ + // ( func_name, num_args ) => func_list + QMap<std::pair<QString, int>, AbstractMetaFunctionCList> results; + auto funcs = metaClass->operatorOverloads(query); + auto end = std::remove_if(funcs.begin(), funcs.end(), skipOperatorFunc); + funcs.erase(end, funcs.end()); + // If we have operator+=, we remove the operator++/-- which would + // otherwise be used for emulating __iadd__, __isub__. + if (std::any_of(funcs.cbegin(), funcs.cend(), isInplaceAdd)) { + end = std::remove_if(funcs.begin(), funcs.end(), + [] (const AbstractMetaFunctionCPtr &func) { + return func->isIncDecrementOperator(); + }); + funcs.erase(end, funcs.end()); + } else { + // If both prefix/postfix ++/-- are present, remove one + if (std::count_if(funcs.begin(), funcs.end(), isIncrementOperator) > 1) + funcs.erase(std::find_if(funcs.begin(), funcs.end(), isIncrementOperator)); + if (std::count_if(funcs.begin(), funcs.end(), isDecrementOperator) > 1) + funcs.erase(std::find_if(funcs.begin(), funcs.end(), isDecrementOperator)); + } + for (const auto &func : funcs) { + int args; + if (func->isComparisonOperator()) { + args = -1; + } else { + args = func->arguments().size(); + } + auto op = std::make_pair(func->name(), args); + results[op].append(func); + } + QList<AbstractMetaFunctionCList> result; + result.reserve(results.size()); + for (auto it = results.cbegin(), end = results.cend(); it != end; ++it) + result.append(it.value()); + return result; +} + static bool hidesBaseClassFunctions(const AbstractMetaFunctionCPtr &f) { - return 0 == (f->attributes() - & (AbstractMetaFunction::OverriddenCppMethod | AbstractMetaFunction::FinalCppMethod)); + auto attributes = f->cppAttributes(); + return !attributes.testFlag(FunctionAttribute::Override) + && !attributes.testFlag(FunctionAttribute::Final); } -void ShibokenGenerator::getInheritedOverloads(const AbstractMetaClass *scope, +void ShibokenGenerator::getInheritedOverloads(const AbstractMetaClassCPtr &scope, AbstractMetaFunctionCList *overloads) { if (overloads->isEmpty() || scope->isNamespace() || scope->baseClasses().isEmpty()) @@ -2020,7 +2292,8 @@ void ShibokenGenerator::getInheritedOverloads(const AbstractMetaClass *scope, AbstractMetaFunctionCList baseCandidates; - auto basePredicate = [&functionName, &seenSignatures, &baseCandidates](const AbstractMetaClass *b) { + auto basePredicate = [&functionName, &seenSignatures, &baseCandidates] + (const AbstractMetaClassCPtr &b) { for (const auto &f : b->functions()) { if (f->generateBinding() && f->name() == functionName) { const QString signature = f->minimalSignature(); @@ -2033,7 +2306,7 @@ void ShibokenGenerator::getInheritedOverloads(const AbstractMetaClass *scope, return false; // Keep going }; - for (const auto *baseClass : scope->baseClasses()) + for (const auto &baseClass : scope->baseClasses()) recurseClassHierarchy(baseClass, basePredicate); // Remove the ones that are not made visible with using declarations @@ -2063,92 +2336,108 @@ void ShibokenGenerator::getInheritedOverloads(const AbstractMetaClass *scope, } } -Generator::OptionDescriptions ShibokenGenerator::options() const +QList<OptionDescription> ShibokenGenerator::options() { - auto result = Generator::options(); - result.append({ - {QLatin1StringView(DISABLE_VERBOSE_ERROR_MESSAGES), + return { + {DISABLE_VERBOSE_ERROR_MESSAGES, u"Disable verbose error messages. Turn the python code hard to debug\n" "but safe few kB on the generated bindings."_s}, - {QLatin1StringView(PARENT_CTOR_HEURISTIC), + {PARENT_CTOR_HEURISTIC, u"Enable heuristics to detect parent relationship on constructors."_s}, - {QLatin1StringView(RETURN_VALUE_HEURISTIC), + {RETURN_VALUE_HEURISTIC, u"Enable heuristics to detect parent relationship on return values\n" "(USE WITH CAUTION!)"_s}, - {QLatin1StringView(USE_ISNULL_AS_NB_NONZERO), + {USE_ISNULL_AS_NB_BOOL, u"If a class have an isNull() const method, it will be used to compute\n" "the value of boolean casts"_s}, - {QLatin1StringView(LEAN_HEADERS), + {LEAN_HEADERS, u"Forward declare classes in module headers"_s}, - {QLatin1StringView(USE_OPERATOR_BOOL_AS_NB_NONZERO), + {USE_OPERATOR_BOOL_AS_NB_BOOL, u"If a class has an operator bool, it will be used to compute\n" "the value of boolean casts"_s}, - {QLatin1StringView(NO_IMPLICIT_CONVERSIONS), + {NO_IMPLICIT_CONVERSIONS, u"Do not generate implicit_conversions for function arguments."_s}, - {QLatin1StringView(WRAPPER_DIAGNOSTICS), + {WRAPPER_DIAGNOSTICS, u"Generate diagnostic code around wrappers"_s} - }); - return result; + }; } -bool ShibokenGenerator::handleOption(const QString &key, const QString &value) +class ShibokenGeneratorOptionsParser : public OptionsParser { - if (Generator::handleOption(key, value)) - return true; - if (key == QLatin1StringView(PARENT_CTOR_HEURISTIC)) - return (m_useCtorHeuristic = true); - if (key == QLatin1StringView(RETURN_VALUE_HEURISTIC)) - return (m_userReturnValueHeuristic = true); - if (key == QLatin1StringView(DISABLE_VERBOSE_ERROR_MESSAGES)) - return (m_verboseErrorMessagesDisabled = true); - if (key == QLatin1StringView(USE_ISNULL_AS_NB_NONZERO)) - return (m_useIsNullAsNbNonZero = true); - if (key == QLatin1StringView(LEAN_HEADERS)) - return (m_leanHeaders= true); - if (key == QLatin1StringView(USE_OPERATOR_BOOL_AS_NB_NONZERO)) - return (m_useOperatorBoolAsNbNonZero = true); - if (key == QLatin1StringView(NO_IMPLICIT_CONVERSIONS)) { - return m_generateImplicitConversions = false; +public: + explicit ShibokenGeneratorOptionsParser(ShibokenGeneratorOptions *o) : m_options(o) {} + + bool handleBoolOption(const QString & key, OptionSource source) override; + +private: + ShibokenGeneratorOptions *m_options; +}; + +bool ShibokenGeneratorOptionsParser::handleBoolOption(const QString &key, OptionSource source) +{ + if (source == OptionSource::CommandLineSingleDash) + return false; + if (key == PARENT_CTOR_HEURISTIC) + return (m_options->useCtorHeuristic = true); + if (key == RETURN_VALUE_HEURISTIC) + return (m_options->userReturnValueHeuristic = true); + if (key == DISABLE_VERBOSE_ERROR_MESSAGES) + return (m_options->verboseErrorMessagesDisabled = true); + if (key == USE_ISNULL_AS_NB_BOOL || key == USE_ISNULL_AS_NB_NONZERO) { + return (m_options->useIsNullAsNbBool = true); + } + if (key == LEAN_HEADERS) + return (m_options->leanHeaders= true); + if (key == USE_OPERATOR_BOOL_AS_NB_BOOL || key == USE_OPERATOR_BOOL_AS_NB_NONZERO) { + return (m_options->useOperatorBoolAsNbBool = true); + } + if (key == NO_IMPLICIT_CONVERSIONS) { + m_options->generateImplicitConversions = false; return true; } - if (key == QLatin1StringView(WRAPPER_DIAGNOSTICS)) - return (m_wrapperDiagnostics = true); + if (key == WRAPPER_DIAGNOSTICS) + return (m_options->wrapperDiagnostics = true); return false; } +std::shared_ptr<OptionsParser> ShibokenGenerator::createOptionsParser() +{ + return std::make_shared<ShibokenGeneratorOptionsParser>(&m_options); +} + bool ShibokenGenerator::doSetup() { return true; } -bool ShibokenGenerator::useCtorHeuristic() const +bool ShibokenGenerator::useCtorHeuristic() { - return m_useCtorHeuristic; + return m_options.useCtorHeuristic; } -bool ShibokenGenerator::useReturnValueHeuristic() const +bool ShibokenGenerator::useReturnValueHeuristic() { - return m_userReturnValueHeuristic; + return m_options.userReturnValueHeuristic; } -bool ShibokenGenerator::useIsNullAsNbNonZero() const +bool ShibokenGenerator::useIsNullAsNbBool() { - return m_useIsNullAsNbNonZero; + return m_options.useIsNullAsNbBool; } -bool ShibokenGenerator::leanHeaders() const +bool ShibokenGenerator::leanHeaders() { - return m_leanHeaders; + return m_options.leanHeaders; } -bool ShibokenGenerator::useOperatorBoolAsNbNonZero() const +bool ShibokenGenerator::useOperatorBoolAsNbBool() { - return m_useOperatorBoolAsNbNonZero; + return m_options.useOperatorBoolAsNbBool; } -bool ShibokenGenerator::generateImplicitConversions() const +bool ShibokenGenerator::generateImplicitConversions() { - return m_generateImplicitConversions; + return m_options.generateImplicitConversions; } QString ShibokenGenerator::moduleCppPrefix(const QString &moduleName) @@ -2158,19 +2447,24 @@ QString ShibokenGenerator::moduleCppPrefix(const QString &moduleName) return result; } +QString ShibokenGenerator::cppApiVariableNameOld(const QString &moduleName) +{ + return "Sbk"_L1 + moduleCppPrefix(moduleName) + "Types"_L1; +} + QString ShibokenGenerator::cppApiVariableName(const QString &moduleName) { - return u"Sbk"_s + moduleCppPrefix(moduleName) + u"Types"_s; + return "Sbk"_L1 + moduleCppPrefix(moduleName) + "TypeStructs"_L1; } QString ShibokenGenerator::pythonModuleObjectName(const QString &moduleName) { - return u"Sbk"_s + moduleCppPrefix(moduleName) + u"ModuleObject"_s; + return "Sbk"_L1 + moduleCppPrefix(moduleName) + "ModuleObject"_L1; } QString ShibokenGenerator::convertersVariableName(const QString &moduleName) { - QString result = cppApiVariableName(moduleName); + QString result = cppApiVariableNameOld(moduleName); result.chop(1); result.append(u"Converters"_s); return result; @@ -2178,11 +2472,11 @@ QString ShibokenGenerator::convertersVariableName(const QString &moduleName) static QString processInstantiationsVariableName(const AbstractMetaType &type) { - QString res = u'_' + _fixedCppTypeName(type.typeEntry()->qualifiedCppName()).toUpper(); + QString res = u'_' + _fixedCppTypeName(type.typeEntry()->qualifiedCppName()); for (const auto &instantiation : type.instantiations()) { res += instantiation.isContainer() ? processInstantiationsVariableName(instantiation) - : u'_' + _fixedCppTypeName(instantiation.cppSignature()).toUpper(); + : u'_' + _fixedCppTypeName(instantiation.cppSignature()); } return res; } @@ -2191,22 +2485,23 @@ static void appendIndexSuffix(QString *s) { if (!s->endsWith(u'_')) s->append(u'_'); - s->append(QStringLiteral("IDX")); + s->append("IDX"_L1); } -QString ShibokenGenerator::getTypeAlternateTemplateIndexVariableName(const AbstractMetaClass *metaClass) +QString + ShibokenGenerator::getTypeAlternateTemplateIndexVariableName(const AbstractMetaClassCPtr &metaClass) { - const AbstractMetaClass *templateBaseClass = metaClass->templateBaseClass(); + const auto templateBaseClass = metaClass->templateBaseClass(); Q_ASSERT(templateBaseClass); QString result = u"SBK_"_s - + _fixedCppTypeName(templateBaseClass->typeEntry()->qualifiedCppName()).toUpper(); + + _fixedCppTypeName(templateBaseClass->typeEntry()->qualifiedCppName()); for (const auto &instantiation : metaClass->templateBaseClassInstantiations()) result += processInstantiationsVariableName(instantiation); appendIndexSuffix(&result); return result; } -QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaClass *metaClass) +QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaClassCPtr &metaClass) { return getTypeIndexVariableName(metaClass->typeEntry()); } @@ -2221,7 +2516,7 @@ QString ShibokenGenerator::getTypeIndexVariableName(TypeEntryCPtr type) const int dot = package.lastIndexOf(u'.'); result += QStringView{package}.right(package.size() - (dot + 1)); } - result += _fixedCppTypeName(type->qualifiedCppName()).toUpper(); + result += _fixedCppTypeName(type->qualifiedCppName()); appendIndexSuffix(&result); return result; } @@ -2229,15 +2524,49 @@ QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaType &type { QString result = u"SBK"_s; if (type.typeEntry()->isContainer()) - result += u'_' + moduleName().toUpper(); + result += u'_' + moduleName(); result += processInstantiationsVariableName(type); appendIndexSuffix(&result); return result; } -bool ShibokenGenerator::verboseErrorMessagesDisabled() const +void collectfromTypeEntry(TypeEntryCPtr entry, QStringList &typeNames) +{ + if (entry->shouldGenerate()) { + typeNames[entry->sbkIndex()] = entry->qualifiedTargetLangName(); + if (entry->isEnum()) { + auto ete = std::static_pointer_cast<const EnumTypeEntry>(entry); + if (ete->flags()) { + auto entry = ete->flags(); + typeNames[entry->sbkIndex()] = entry->qualifiedTargetLangName(); + } + } + } +} + +void ShibokenGenerator::collectFullTypeNamesArray(QStringList &typeNames) +{ + for (const auto &metaClass : api().classes()) { + collectfromTypeEntry(metaClass->typeEntry(), typeNames); + + for (const AbstractMetaEnum &metaEnum : metaClass->enums()) + collectfromTypeEntry(metaEnum.typeEntry(), typeNames); + + int smartPointerCountIndex = getMaxTypeIndex(); + for (const auto &smp : api().instantiatedSmartPointers()) { + auto entry = smp.type.typeEntry(); + typeNames[smartPointerCountIndex] = + smp.specialized->typeEntry()->qualifiedTargetLangName(); + ++smartPointerCountIndex; + } + } + for (const AbstractMetaEnum &metaEnum : api().globalEnums()) + collectfromTypeEntry(metaEnum.typeEntry(), typeNames); +} + +bool ShibokenGenerator::verboseErrorMessagesDisabled() { - return m_verboseErrorMessagesDisabled; + return m_options.verboseErrorMessagesDisabled; } bool ShibokenGenerator::pythonFunctionWrapperUsesListOfArguments(const AbstractMetaFunctionCPtr &func) const @@ -2290,7 +2619,7 @@ QString ShibokenGenerator::pythonArgsAt(int i) void ShibokenGenerator::replaceTemplateVariables(QString &code, const AbstractMetaFunctionCPtr &func) const { - const AbstractMetaClass *cpp_class = func->ownerClass(); + const auto cpp_class = func->ownerClass(); if (cpp_class) code.replace(u"%TYPE"_s, cpp_class->name()); diff --git a/sources/shiboken6/generator/shiboken/shibokengenerator.h b/sources/shiboken6/generator/shiboken/shibokengenerator.h index d0b1158c6..22ee73fa2 100644 --- a/sources/shiboken6/generator/shiboken/shibokengenerator.h +++ b/sources/shiboken6/generator/shiboken/shibokengenerator.h @@ -7,12 +7,14 @@ #include <generator.h> #include "customconversion_typedefs.h" +#include "abstractmetalang_enums.h" #include "typesystem_typedefs.h" #include "typesystem_enums.h" #include <QtCore/QRegularExpression> #include <array> +#include <optional> class EnumTypeEntry; class FlagsTypeEntry; @@ -23,8 +25,18 @@ class OverloadData; class TargetToNativeConversion; struct GeneratorClassInfoCacheEntry; struct IncludeGroup; +struct ShibokenGeneratorOptions; -QT_FORWARD_DECLARE_CLASS(TextStream) +class TextStream; + +// Function to be used for implementing nb_bool +struct BoolCastFunction +{ + AbstractMetaFunctionCPtr function; + bool invert = false; // Function is "isNull()", (invert result). +}; + +using BoolCastFunctionOptional = std::optional<BoolCastFunction>; /** * Abstract generator that contains common methods used in CppGenerator and HeaderGenerator. @@ -32,6 +44,8 @@ QT_FORWARD_DECLARE_CLASS(TextStream) class ShibokenGenerator : public Generator { public: + Q_DISABLE_COPY_MOVE(ShibokenGenerator) + /// Besides the actual bindings (see AbstractMetaFunction::generateBinding(), /// some functions need to be generated into the wrapper class /// (virtual method/avoid protected hack expose). @@ -75,6 +89,9 @@ public: const char *name() const override { return "Shiboken"; } + static QList<OptionDescription> options(); + static std::shared_ptr<OptionsParser> createOptionsParser(); + static QString minimalConstructorExpression(const ApiExtractorResult &api, const AbstractMetaType &type); static QString minimalConstructorExpression(const ApiExtractorResult &api, @@ -83,7 +100,7 @@ public: protected: bool doSetup() override; - GeneratorContext contextForClass(const AbstractMetaClass *c) const override; + GeneratorContext contextForClass(const AbstractMetaClassCPtr &c) const override; /** * Returns a map with all functions grouped, the function name is used as key. @@ -91,7 +108,12 @@ protected: * \param scope Where to search for functions, null means all global functions. */ FunctionGroups getGlobalFunctionGroups() const; - static FunctionGroups getFunctionGroups(const AbstractMetaClass *scope); + static FunctionGroups getFunctionGroups(const AbstractMetaClassCPtr &scope); + + static QList<AbstractMetaFunctionCList> + numberProtocolOperators(const AbstractMetaClassCPtr &scope); + + static BoolCastFunctionOptional boolCast(const AbstractMetaClassCPtr &scope); /** * Returns all different inherited overloads of func, and includes func as well. @@ -123,6 +145,7 @@ protected: /// Replaces variables for the user's custom code at global or class level. void processCodeSnip(QString &code) const; + void processCodeSnip(QString &code, const QString &context) const; void processClassCodeSnip(QString &code, const GeneratorContext &context) const; /** @@ -148,53 +171,65 @@ protected: int arg_count = -1) const; /// Returns the top-most class that has multiple inheritance in the ancestry. - static const AbstractMetaClass *getMultipleInheritingClass(const AbstractMetaClass *metaClass); + static AbstractMetaClassCPtr + getMultipleInheritingClass(const AbstractMetaClassCPtr &metaClass); - static bool useOverrideCaching(const AbstractMetaClass *metaClass); - AttroCheck checkAttroFunctionNeeds(const AbstractMetaClass *metaClass) const; + static bool useOverrideCaching(const AbstractMetaClassCPtr &metaClass); + static AttroCheck checkAttroFunctionNeeds(const AbstractMetaClassCPtr &metaClass); - /// Returns a list of methods of the given class where each one is part of a different overload with both static and non-static method. + /// Returns a list of methods of the given class where each one is part of + /// a different overload with both static and non-static method. static AbstractMetaFunctionCList - getMethodsWithBothStaticAndNonStaticMethods(const AbstractMetaClass *metaClass); + getMethodsWithBothStaticAndNonStaticMethods(const AbstractMetaClassCPtr &metaClass); static void writeToPythonConversion(TextStream &s, const AbstractMetaType &type, - const AbstractMetaClass *context, + const AbstractMetaClassCPtr &context, const QString &argumentName); static void writeToCppConversion(TextStream &s, const AbstractMetaType &type, - const AbstractMetaClass *context, const QString &inArgName, const QString &outArgName); static void writeToCppConversion(TextStream &s, - const AbstractMetaClass *metaClass, const QString &inArgName, + const AbstractMetaClassCPtr &metaClass, + const QString &inArgName, const QString &outArgName); /// Returns true if the argument is a pointer that rejects nullptr values. static bool shouldRejectNullPointerArgument(const AbstractMetaFunctionCPtr &func, int argIndex); - /// Verifies if the class should have a C++ wrapper generated for it, instead of only a Python wrapper. - bool shouldGenerateCppWrapper(const AbstractMetaClass *metaClass) const; + /// Verifies if the class should have a C++ wrapper generated for it, + /// instead of only a Python wrapper. + static bool shouldGenerateCppWrapper(const AbstractMetaClassCPtr &metaClass); + + static bool shouldGenerateMetaObjectFunctions(const AbstractMetaClassCPtr &metaClass); /// Returns which functions need to be generated into the wrapper class - FunctionGeneration functionGeneration(const AbstractMetaFunctionCPtr &func) const; + static FunctionGeneration functionGeneration(const AbstractMetaFunctionCPtr &func); // Return a list of implicit conversions if generation is enabled. AbstractMetaFunctionCList implicitConversions(const TypeEntryCPtr &t) const; - QString wrapperName(const AbstractMetaClass *metaClass) const; + static QString wrapperName(const AbstractMetaClassCPtr &metaClass); + + static QString fullPythonClassName(const AbstractMetaClassCPtr &metaClass); + + static QString headerFileNameForContext(const GeneratorContext &context); + IncludeGroup baseWrapperIncludes(const GeneratorContext &classContext) const; - static QString fullPythonClassName(const AbstractMetaClass *metaClass); static QString fullPythonFunctionName(const AbstractMetaFunctionCPtr &func, bool forceFunc); - bool wrapperDiagnostics() const { return m_wrapperDiagnostics; } + static bool wrapperDiagnostics(); static QString protectedEnumSurrogateName(const AbstractMetaEnum &metaEnum); static QString pythonPrimitiveTypeName(const QString &cppTypeName); static QString pythonOperatorFunctionName(const AbstractMetaFunctionCPtr &func); + static QList<AbstractMetaFunctionCList> + filterGroupedOperatorFunctions(const AbstractMetaClassCPtr &metaClass, + OperatorQueryOptions query); static QString fixedCppTypeName(const TargetToNativeConversion &toNative); static QString fixedCppTypeName(const AbstractMetaType &type); @@ -211,41 +246,42 @@ protected: static QString converterObject(const AbstractMetaType &type) ; static QString converterObject(const TypeEntryCPtr &type); - static QString cpythonBaseName(const AbstractMetaClass *metaClass); + static QString cpythonBaseName(const AbstractMetaClassCPtr &metaClass); static QString cpythonBaseName(const TypeEntryCPtr &type); + static QString containerCpythonBaseName(const ContainerTypeEntryCPtr &ctype); static QString cpythonBaseName(const AbstractMetaType &type); - static QString cpythonTypeName(const AbstractMetaClass *metaClass); + static QString cpythonTypeName(const AbstractMetaClassCPtr &metaClass); static QString cpythonTypeName(const TypeEntryCPtr &type); + static QString cpythonTypeNameExtSet(const TypeEntryCPtr &type); + static QString cpythonTypeNameExtSet(const AbstractMetaType &type); static QString cpythonTypeNameExt(const TypeEntryCPtr &type); - static QString cpythonTypeNameExt(const AbstractMetaType &type) ; + static QString cpythonTypeNameExt(const AbstractMetaType &type); static QString cpythonCheckFunction(TypeEntryCPtr type); static QString cpythonCheckFunction(AbstractMetaType metaType); static QString cpythonIsConvertibleFunction(const TypeEntryCPtr &type); - static QString cpythonIsConvertibleFunction(AbstractMetaType metaType); + static QString cpythonIsConvertibleFunction(const AbstractMetaType &metaType); static QString cpythonIsConvertibleFunction(const AbstractMetaArgument &metaArg); - static QString cpythonToCppConversionFunction(const AbstractMetaClass *metaClass) ; - static QString cpythonToCppConversionFunction(const AbstractMetaType &type, - const AbstractMetaClass *context = nullptr); - static QString cpythonToPythonConversionFunction(const AbstractMetaType &type, - const AbstractMetaClass *context = nullptr); - static QString cpythonToPythonConversionFunction(const AbstractMetaClass *metaClass); + static QString cpythonToCppConversionFunction(const AbstractMetaClassCPtr &metaClass) ; + static QString cpythonToCppConversionFunction(const AbstractMetaType &type); + static QString cpythonToPythonConversionFunction(const AbstractMetaType &type); + static QString cpythonToPythonConversionFunction(const AbstractMetaClassCPtr &metaClass); static QString cpythonToPythonConversionFunction(const TypeEntryCPtr &type); static QString cpythonFunctionName(const AbstractMetaFunctionCPtr &func) ; static QString cpythonMethodDefinitionName(const AbstractMetaFunctionCPtr &func); - static QString cpythonGettersSettersDefinitionName(const AbstractMetaClass *metaClass); - static QString cpythonGetattroFunctionName(const AbstractMetaClass *metaClass); - static QString cpythonSetattroFunctionName(const AbstractMetaClass *metaClass); + static QString cpythonGettersSettersDefinitionName(const AbstractMetaClassCPtr &metaClass); + static QString cpythonGetattroFunctionName(const AbstractMetaClassCPtr &metaClass); + static QString cpythonSetattroFunctionName(const AbstractMetaClassCPtr &metaClass); static QString cpythonGetterFunctionName(const AbstractMetaField &metaField); static QString cpythonSetterFunctionName(const AbstractMetaField &metaField); static QString cpythonGetterFunctionName(const QPropertySpec &property, - const AbstractMetaClass *metaClass); + const AbstractMetaClassCPtr &metaClass); static QString cpythonSetterFunctionName(const QPropertySpec &property, - const AbstractMetaClass *metaClass); - static QString cpythonWrapperCPtr(const AbstractMetaClass *metaClass, - const QString &argName = QStringLiteral("self")); - static QString cpythonWrapperCPtr(const AbstractMetaType &metaType, + const AbstractMetaClassCPtr &metaClass); + static QString cpythonWrapperCPtr(const AbstractMetaClassCPtr &metaClass, + const QString &argName = QLatin1StringView("self")); + static QString cpythonWrapperCPtr(const AbstractMetaType &metaType, const QString &argName); static QString cpythonWrapperCPtr(const TypeEntryCPtr &type, const QString &argName); @@ -254,44 +290,47 @@ protected: static QString cpythonFlagsName(const FlagsTypeEntryCPtr &flagsEntry); static QString cpythonFlagsName(const AbstractMetaEnum *metaEnum); - /// Returns the special cast function name, the function used to proper cast class with multiple inheritance. - static QString cpythonSpecialCastFunctionName(const AbstractMetaClass *metaClass); + /// Returns the special cast function name, the function used to proper cast + /// class with multiple inheritance. + static QString cpythonSpecialCastFunctionName(const AbstractMetaClassCPtr &metaClass); - /// Returns the file name for the module global header. If no module name is provided the current will be used. + /// Returns the file name for the module global header. If no module name + /// is provided the current will be used. static QString getModuleHeaderFileName(const QString &moduleName = QString()); static QString getPrivateModuleHeaderFileName(const QString &moduleName = QString()); /// Includes for header (native wrapper class) or binding source - QList<IncludeGroup> classIncludes(const AbstractMetaClass *metaClass) const; - - OptionDescriptions options() const override; - bool handleOption(const QString &key, const QString &value) override; + QList<IncludeGroup> classIncludes(const AbstractMetaClassCPtr &metaClass) const; /// Returns true if the user enabled the so called "parent constructor heuristic". - bool useCtorHeuristic() const; + static bool useCtorHeuristic(); /// Returns true if the user enabled the so called "return value heuristic". - bool useReturnValueHeuristic() const; + static bool useReturnValueHeuristic(); /// Returns true if the generator should use the result of isNull()const to compute boolean casts. - bool useIsNullAsNbNonZero() const; + static bool useIsNullAsNbBool(); /// Whether to generate lean module headers - bool leanHeaders() const; + static bool leanHeaders(); /// Returns true if the generator should use operator bool to compute boolean casts. - bool useOperatorBoolAsNbNonZero() const; + static bool useOperatorBoolAsNbBool(); /// Generate implicit conversions of function arguments - bool generateImplicitConversions() const; + static bool generateImplicitConversions(); + static QString cppApiVariableNameOld(const QString &moduleName = {}); static QString cppApiVariableName(const QString &moduleName = QString()); static QString pythonModuleObjectName(const QString &moduleName = QString()); static QString convertersVariableName(const QString &moduleName = QString()); /// Returns the type index variable name for a given class. - static QString getTypeIndexVariableName(const AbstractMetaClass *metaClass); + static QString getTypeIndexVariableName(const AbstractMetaClassCPtr &metaClass); /// Returns the type index variable name for a given typedef for a template /// class instantiation made of the template class and the instantiation values - static QString getTypeAlternateTemplateIndexVariableName(const AbstractMetaClass *metaClass); + static QString getTypeAlternateTemplateIndexVariableName(const AbstractMetaClassCPtr &metaClass); static QString getTypeIndexVariableName(TypeEntryCPtr type); static QString getTypeIndexVariableName(const AbstractMetaType &type) ; + /// Collect all type names as an array for initializing the type/name struct. + void collectFullTypeNamesArray(QStringList &typeNames); + /// Returns true if the user don't want verbose error messages on the generated bindings. - bool verboseErrorMessagesDisabled() const; + static bool verboseErrorMessagesDisabled(); void collectContainerTypesFromConverterMacros(const QString &code, bool toPythonMacro); @@ -324,16 +363,20 @@ protected: private: static QString getModuleHeaderFileBaseName(const QString &moduleName = QString()); static QString cpythonGetterFunctionName(const QString &name, - const AbstractMetaClass *enclosingClass); + const AbstractMetaClassCPtr &enclosingClass); static QString cpythonSetterFunctionName(const QString &name, - const AbstractMetaClass *enclosingClass); + const AbstractMetaClassCPtr &enclosingClass); - static const GeneratorClassInfoCacheEntry &getGeneratorClassInfo(const AbstractMetaClass *scope); - static FunctionGroups getFunctionGroupsImpl(const AbstractMetaClass *scope); - static bool classNeedsGetattroFunctionImpl(const AbstractMetaClass *metaClass); + static const GeneratorClassInfoCacheEntry & + getGeneratorClassInfo(const AbstractMetaClassCPtr &scope); + static FunctionGroups getFunctionGroupsImpl(const AbstractMetaClassCPtr &scope); + static QList<AbstractMetaFunctionCList> + getNumberProtocolOperators(const AbstractMetaClassCPtr &metaClass); + static BoolCastFunctionOptional getBoolCast(const AbstractMetaClassCPtr &metaClass); + static bool classNeedsGetattroFunctionImpl(const AbstractMetaClassCPtr &metaClass); QString translateTypeForWrapperMethod(const AbstractMetaType &cType, - const AbstractMetaClass *context, + const AbstractMetaClassCPtr &context, Options opt = NoOption) const; /** @@ -342,7 +385,7 @@ private: * \param func the metafunction to be searched in subclasses. * \param seen the function's minimal signatures already seen. */ - static void getInheritedOverloads(const AbstractMetaClass *scope, + static void getInheritedOverloads(const AbstractMetaClassCPtr &scope, AbstractMetaFunctionCList *overloads); @@ -371,7 +414,7 @@ private: QString functionReturnType(const AbstractMetaFunctionCPtr &func, Options options = NoOption) const; /// Utility function for writeCodeSnips. - using ArgumentVarReplacementPair = QPair<AbstractMetaArgument, QString>; + using ArgumentVarReplacementPair = std::pair<AbstractMetaArgument, QString>; using ArgumentVarReplacementList = QList<ArgumentVarReplacementPair>; static ArgumentVarReplacementList getArgumentReplacement(const AbstractMetaFunctionCPtr &func, @@ -433,16 +476,7 @@ private: void replaceTemplateVariables(QString &code, const AbstractMetaFunctionCPtr &func) const; - bool m_useCtorHeuristic = false; - bool m_userReturnValueHeuristic = false; - bool m_verboseErrorMessagesDisabled = false; - bool m_useIsNullAsNbNonZero = false; - // FIXME PYSIDE 7 Flip m_leanHeaders default or remove? - bool m_leanHeaders = false; - bool m_useOperatorBoolAsNbNonZero = false; - // FIXME PYSIDE 7 Flip generateImplicitConversions default or remove? - bool m_generateImplicitConversions = true; - bool m_wrapperDiagnostics = false; + static ShibokenGeneratorOptions m_options; /// Type system converter variable replacement names and regular expressions. static const QHash<int, QString> &typeSystemConvName(); @@ -454,20 +488,4 @@ private: Q_DECLARE_OPERATORS_FOR_FLAGS(ShibokenGenerator::FunctionGeneration); Q_DECLARE_OPERATORS_FOR_FLAGS(ShibokenGenerator::AttroCheck); -extern const QString CPP_ARG; -extern const QString CPP_ARG_REMOVED; -extern const QString CPP_RETURN_VAR; -extern const QString CPP_SELF_VAR; -extern const QString NULL_PTR; -extern const QString PYTHON_ARG; -extern const QString PYTHON_ARGS; -extern const QString PYTHON_OVERRIDE_VAR; -extern const QString PYTHON_RETURN_VAR; -extern const QString PYTHON_TO_CPP_VAR; -extern const QString SMART_POINTER_GETTER; - -extern const QString CONV_RULE_OUT_VAR_SUFFIX; -extern const QString BEGIN_ALLOW_THREADS; -extern const QString END_ALLOW_THREADS; - #endif // SHIBOKENGENERATOR_H |