diff options
Diffstat (limited to 'sources/shiboken6/generator')
31 files changed, 10093 insertions, 8310 deletions
diff --git a/sources/shiboken6/generator/CMakeLists.txt b/sources/shiboken6/generator/CMakeLists.txt index 0296138e5..aebe2cd5e 100644 --- a/sources/shiboken6/generator/CMakeLists.txt +++ b/sources/shiboken6/generator/CMakeLists.txt @@ -1,17 +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 Python_Interpreter_FOUND)) + message(WARNING "Some dependencies were not found: shiboken6 generator compilation disabled!") + return() +endif() set(shiboken6_SRC -generator.cpp -shiboken/cppgenerator.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 @@ -20,17 +49,24 @@ 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_compile_definitions(shiboken6 PUBLIC DOCSTRINGS_ENABLED) + target_sources(shiboken6 PRIVATE + qtdoc/qtdocgenerator.cpp qtdoc/qtdocgenerator.h + qtdoc/qtxmltosphinx.cpp qtdoc/qtxmltosphinx.h + qtdoc/qtxmltosphinxinterface.h + qtdoc/rstformat.h) + target_compile_definitions(shiboken6 PUBLIC DOCSTRINGS_ENABLED QT_LEAN_HEADERS=1) endif() configure_file(shibokenconfig.h.in "${CMAKE_CURRENT_BINARY_DIR}/shibokenconfig.h" @ONLY) install(TARGETS shiboken6 - EXPORT Shiboken6Targets - DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") + EXPORT "${package_name}Targets" + DESTINATION "bin") +install(EXPORT "${package_name}Targets" + NAMESPACE "Shiboken6::" + DESTINATION ${LIB_INSTALL_DIR}/cmake/${package_name}) set(shiboken_generator_package_name "shiboken6_generator") @@ -61,3 +97,25 @@ configure_file("${shiboken_version_path}" install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_git_shiboken_generator_version.py" DESTINATION "${PYTHON_SITE_PACKAGES}/${shiboken_generator_package_name}") + +include(CMakePackageConfigHelpers) + +# Single build-tree and install-tree Config file. There's no need for separate ones because we +# don't specify any PATH_VARS, so the relative path of PACKAGE_PREFIX_DIR does not really matter. +configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/../data/${package_name}Config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/${package_name}Config.cmake" + INSTALL_DESTINATION "${LIB_INSTALL_DIR}/cmake/${package_name}" +) +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${package_name}ConfigVersion.cmake" + VERSION "${shiboken6_VERSION}" + COMPATIBILITY AnyNewerVersion + ARCH_INDEPENDENT +) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${package_name}Config.cmake" + DESTINATION "${LIB_INSTALL_DIR}/cmake/${package_name}") + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${package_name}ConfigVersion.cmake" + DESTINATION "${LIB_INSTALL_DIR}/cmake/${package_name}") diff --git a/sources/shiboken6/generator/_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 new file mode 100644 index 000000000..89cc9fa77 --- /dev/null +++ b/sources/shiboken6/generator/defaultvalue.cpp @@ -0,0 +1,120 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "defaultvalue.h" + +#include "qtcompat.h" + +#include <QtCore/QDebug> + +using namespace Qt::StringLiterals; + +// DefaultValue is used for storing default values of types for which code is +// generated in different contexts: +// +// Context | Example: "Class *" | Example: "Class" with default Constructor +// --------------------+-------------------------------+------------------------------------------ +// Variable | var{nullptr}; | var; +// initializations | | +// --------------------+-------------------------------+------------------------------------------ +// Return values | return nullptr; | return {} +// --------------------+-------------------------------+------------------------------------------ +// constructor | static_cast<Class *>(nullptr) | Class() +// arguments lists | | +// (recursive, precise | | +// matching). | | + +DefaultValue::DefaultValue(Type t, QString value) : + m_type(t), m_value(std::move(value)) +{ +} + +DefaultValue::DefaultValue(QString customValue) : + m_type(Custom), m_value(std::move(customValue)) +{ +} + +QString DefaultValue::returnValue() const +{ + switch (m_type) { + case DefaultValue::Boolean: + return u"false"_s; + case DefaultValue::CppScalar: + return u"0"_s; + case DefaultValue::Custom: + case DefaultValue::Enum: + return m_value; + case DefaultValue::Pointer: + return u"nullptr"_s; + case DefaultValue::Void: + return {}; + case DefaultValue::DefaultConstructorWithDefaultValues: + return m_value + u"()"_s; + case DefaultValue::DefaultConstructor: + break; + } + return u"{}"_s; +} + +QString DefaultValue::initialization() const +{ + switch (m_type) { + case DefaultValue::Boolean: + return u"{false}"_s; + case DefaultValue::CppScalar: + return u"{0}"_s; + case DefaultValue::Custom: + return u" = "_s + m_value; + case DefaultValue::Enum: + return u'{' + m_value + u'}'; + case DefaultValue::Pointer: + return u"{nullptr}"_s; + case DefaultValue::Void: + Q_ASSERT(false); + break; + case DefaultValue::DefaultConstructor: + case DefaultValue::DefaultConstructorWithDefaultValues: + break; + } + return {}; +} + +QString DefaultValue::constructorParameter() const +{ + switch (m_type) { + case DefaultValue::Boolean: + return u"false"_s; + case DefaultValue::CppScalar: { + // PYSIDE-846: Use static_cast in case of "unsigned long" and similar + const QString cast = m_value.contains(u' ') + ? u"static_cast<"_s + m_value + u'>' + : m_value; + return cast + u"(0)"_s; + } + case DefaultValue::Custom: + case DefaultValue::Enum: + return m_value; + case DefaultValue::Pointer: + // Be precise here to be able to differentiate between constructors + // taking different pointer types, cf + // QTreeWidgetItemIterator(QTreeWidget *) and + // QTreeWidgetItemIterator(QTreeWidgetItemIterator *). + return u"static_cast<"_s + m_value + u"*>(nullptr)"_s; + case DefaultValue::Void: + Q_ASSERT(false); + break; + case DefaultValue::DefaultConstructor: + case DefaultValue::DefaultConstructorWithDefaultValues: + break; + } + return m_value + u"()"_s; +} + +QDebug operator<<(QDebug debug, const DefaultValue &v) +{ + QDebugStateSaver saver(debug); + debug.noquote(); + debug.nospace(); + debug << "DefaultValue(" << v.type() << ", \"" << v.value() << "\")"; + return debug; +} diff --git a/sources/shiboken6/generator/defaultvalue.h b/sources/shiboken6/generator/defaultvalue.h new file mode 100644 index 000000000..d518d134f --- /dev/null +++ b/sources/shiboken6/generator/defaultvalue.h @@ -0,0 +1,46 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef DEFAULTVALUE_H +#define DEFAULTVALUE_H + +#include <QtCore/QString> + +QT_FORWARD_DECLARE_CLASS(QDebug); + +class DefaultValue +{ +public: + enum Type + { + Boolean, + CppScalar, // A C++ scalar type (int,..) specified by value() + Custom, // A custom constructor/expression, uses value() as is + DefaultConstructor, // For classes named value() + DefaultConstructorWithDefaultValues, // as DefaultConstructor, but can't return {} though. + Enum, // Enum value as specified by value() + Pointer, // Pointer of type value() + Void // "", for return values only + }; + + explicit DefaultValue(Type t, QString value = QString()); + explicit DefaultValue(QString customValue); + + QString returnValue() const; + QString initialization() const; + QString constructorParameter() const; + + QString value() const { return m_value; } + void setValue(const QString &value) { m_value = value; } + + Type type() const { return m_type; } + void setType(Type type) { m_type = type; } + +private: + Type m_type; + QString m_value; +}; + +QDebug operator<<(QDebug debug, const DefaultValue &v); + +#endif // DEFAULTVALUE_H diff --git a/sources/shiboken6/generator/generator.cpp b/sources/shiboken6/generator/generator.cpp index a50666252..a01326530 100644 --- a/sources/shiboken6/generator/generator.cpp +++ b/sources/shiboken6/generator/generator.cpp @@ -1,171 +1,43 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "generator.h" +#include "defaultvalue.h" +#include "generatorcontext.h" #include "apiextractorresult.h" -#include "ctypenames.h" +#include "abstractmetaargument.h" #include "abstractmetaenum.h" -#include "abstractmetafield.h" #include "abstractmetafunction.h" #include "abstractmetalang.h" -#include "parser/codemodel.h" #include "messages.h" +#include <optionsparser.h> #include "reporthandler.h" #include "fileout.h" -#include "apiextractor.h" -#include "typesystem.h" +#include "arraytypeentry.h" +#include "enumtypeentry.h" +#include "enumvaluetypeentry.h" +#include "namespacetypeentry.h" +#include "primitivetypeentry.h" +#include "typesystemtypeentry.h" +#include <typedatabase.h> + +#include "qtcompat.h" #include <QtCore/QDir> #include <QtCore/QFile> #include <QtCore/QFileInfo> #include <QtCore/QRegularExpression> -#include <QDebug> -#include <typedatabase.h> - -/** - * DefaultValue is used for storing default values of types for which code is - * generated in different contexts: - * - * Context | Example: "Class *" | Example: "Class" with default Constructor - * --------------------+-------------------------------+------------------------------------------ - * Variable | var{nullptr}; | var; - * initializations | | - * --------------------+-------------------------------+------------------------------------------ - * Return values | return nullptr; | return {} - * --------------------+-------------------------------+------------------------------------------ - * constructor | static_cast<Class *>(nullptr) | Class() - * arguments lists | | - * (recursive, precise | | - * matching). | | - */ - -DefaultValue::DefaultValue(Type t, QString value) : - m_type(t), m_value(std::move(value)) -{ -} - -DefaultValue::DefaultValue(QString customValue) : - m_type(Custom), m_value(std::move(customValue)) -{ -} - -QString DefaultValue::returnValue() const -{ - switch (m_type) { - case DefaultValue::Boolean: - return QLatin1String("false"); - case DefaultValue::CppScalar: - return QLatin1String("0"); - case DefaultValue::Custom: - case DefaultValue::Enum: - return m_value; - case DefaultValue::Pointer: - return QLatin1String("nullptr"); - case DefaultValue::Void: - return QString(); - case DefaultValue::DefaultConstructorWithDefaultValues: - return m_value + QLatin1String("()"); - case DefaultValue::DefaultConstructor: - break; - } - return QLatin1String("{}"); -} - -QString DefaultValue::initialization() const -{ - switch (m_type) { - case DefaultValue::Boolean: - return QLatin1String("{false}"); - case DefaultValue::CppScalar: - return QLatin1String("{0}"); - case DefaultValue::Custom: - return QLatin1String(" = ") + m_value; - case DefaultValue::Enum: - return QLatin1Char('{') + m_value + QLatin1Char('}'); - case DefaultValue::Pointer: - return QLatin1String("{nullptr}"); - case DefaultValue::Void: - Q_ASSERT(false); - break; - case DefaultValue::DefaultConstructor: - case DefaultValue::DefaultConstructorWithDefaultValues: - break; - } - return QString(); -} -QString DefaultValue::constructorParameter() const -{ - switch (m_type) { - case DefaultValue::Boolean: - return QLatin1String("false"); - case DefaultValue::CppScalar: { - // PYSIDE-846: Use static_cast in case of "unsigned long" and similar - const QString cast = m_value.contains(QLatin1Char(' ')) - ? QLatin1String("static_cast<") + m_value + QLatin1Char('>') - : m_value; - return cast + QLatin1String("(0)"); - } - case DefaultValue::Custom: - case DefaultValue::Enum: - return m_value; - case DefaultValue::Pointer: - // Be precise here to be able to differentiate between constructors - // taking different pointer types, cf - // QTreeWidgetItemIterator(QTreeWidget *) and - // QTreeWidgetItemIterator(QTreeWidgetItemIterator *). - return QLatin1String("static_cast<") + m_value + QLatin1String("*>(nullptr)"); - case DefaultValue::Void: - Q_ASSERT(false); - break; - case DefaultValue::DefaultConstructor: - case DefaultValue::DefaultConstructorWithDefaultValues: - break; - } - return m_value + QLatin1String("()"); -} +using namespace Qt::StringLiterals; -#ifndef QT_NO_DEBUG_STREAM -QDebug operator<<(QDebug debug, const DefaultValue &v) -{ - QDebugStateSaver saver(debug); - debug.noquote(); - debug.nospace(); - debug << "DefaultValue(" << v.type() << ", \"" << v.value() << "\")"; - return debug; -} -#endif // !QT_NO_DEBUG_STREAM +static constexpr auto ENABLE_PYSIDE_EXTENSIONS = "enable-pyside-extensions"_L1; +static constexpr auto AVOID_PROTECTED_HACK = "avoid-protected-hack"_L1; -QString GeneratorContext::smartPointerWrapperName() const +struct GeneratorOptions { - Q_ASSERT(m_type == SmartPointer); - return m_preciseClassType.cppSignature(); -} + bool usePySideExtensions = false; + bool avoidProtectedHack = false; +}; struct Generator::GeneratorPrivate { @@ -173,13 +45,16 @@ struct Generator::GeneratorPrivate QString outDir; // License comment QString licenseComment; - QStringList instantiatedContainersNames; - AbstractMetaTypeList instantiatedContainers; - AbstractMetaTypeList instantiatedSmartPointers; AbstractMetaClassCList m_invisibleTopNamespaces; bool m_hasPrivateClasses = false; + static GeneratorOptions m_options; }; +GeneratorOptions Generator::GeneratorPrivate::m_options; + +// Kept as a variable for a potential Qt-in-namespace support +QString Generator::m_gsp = "::"_L1; + Generator::Generator() : m_d(new GeneratorPrivate) { } @@ -193,17 +68,19 @@ bool Generator::setup(const ApiExtractorResult &api) { m_d->api = api; const auto moduleEntry = TypeDatabase::instance()->defaultTypeSystemType(); - if (!moduleEntry || !moduleEntry->generateCode()) { - qCWarning(lcShiboken) << "Couldn't find the package name!!"; + if (!moduleEntry) { + qCWarning(lcShiboken,"Couldn't find the package name!!"); + return false; + } + if (!moduleEntry->generateCode()) { + qCWarning(lcShiboken, "Code generation of root typesystem is disabled!!"); return false; } - collectInstantiatedContainersAndSmartPointers(); - - 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([&](AbstractMetaClass *ic) { + c->invisibleNamespaceRecursion([&](const AbstractMetaClassCPtr &ic) { m_d->m_invisibleTopNamespaces.append(ic); }); } @@ -212,150 +89,65 @@ bool Generator::setup(const ApiExtractorResult &api) return doSetup(); } -QString Generator::getSimplifiedContainerTypeName(const AbstractMetaType &type) -{ - const QString signature = type.cppSignature(); - if (!type.typeEntry()->isContainer() && !type.typeEntry()->isSmartPointer()) - return signature; - QString typeName = signature; - if (type.isConstant()) - typeName.remove(0, sizeof("const ") / sizeof(char) - 1); - switch (type.referenceType()) { - case NoReference: - break; - case LValueReference: - typeName.chop(1); - break; - case RValueReference: - typeName.chop(2); - break; - } - while (typeName.endsWith(QLatin1Char('*')) || typeName.endsWith(QLatin1Char(' '))) - typeName.chop(1); - return typeName; -} - -// Strip a "const QSharedPtr<const Foo> &" or similar to "QSharedPtr<Foo>" (PYSIDE-1016/454) -AbstractMetaType canonicalSmartPtrInstantiation(const AbstractMetaType &type) -{ - const AbstractMetaTypeList &instantiations = type.instantiations(); - Q_ASSERT(instantiations.size() == 1); - const bool needsFix = type.isConstant() || type.referenceType() != NoReference; - const bool pointeeNeedsFix = instantiations.constFirst().isConstant(); - if (!needsFix && !pointeeNeedsFix) - return type; - auto fixedType = type; - fixedType.setReferenceType(NoReference); - fixedType.setConstant(false); - if (pointeeNeedsFix) { - auto fixedPointeeType = instantiations.constFirst(); - fixedPointeeType.setConstant(false); - fixedType.setInstantiations(AbstractMetaTypeList(1, fixedPointeeType)); - } - return fixedType; -} - -static inline const TypeEntry *pointeeTypeEntry(const AbstractMetaType &smartPtrType) +QList<OptionDescription> Generator::options() { - return smartPtrType.instantiations().constFirst().typeEntry(); + return { + {AVOID_PROTECTED_HACK, + u"Avoid the use of the '#define protected public' hack."_s}, + {ENABLE_PYSIDE_EXTENSIONS, + u"Enable PySide extensions, such as support for signal/slots,\n" + "use this if you are creating a binding for a Qt-based library."_s} + }; } -void Generator::addInstantiatedContainersAndSmartPointers(const AbstractMetaType &type, - const QString &context) +class GeneratorOptionsParser : public OptionsParser { - for (const auto &t : type.instantiations()) - addInstantiatedContainersAndSmartPointers(t, context); - const auto typeEntry = type.typeEntry(); - const bool isContainer = typeEntry->isContainer(); - if (!isContainer - && !(typeEntry->isSmartPointer() && typeEntry->generateCode())) { - return; - } - if (type.hasTemplateChildren()) { - QString piece = isContainer ? QStringLiteral("container") : QStringLiteral("smart pointer"); - QString warning = - QString::fromLatin1("Skipping instantiation of %1 '%2' because it has template" - " arguments.").arg(piece, type.originalTypeDescription()); - if (!context.isEmpty()) - warning.append(QStringLiteral(" Calling context: ") + context); - - qCWarning(lcShiboken).noquote().nospace() << warning; - return; - - } - if (isContainer) { - const QString typeName = getSimplifiedContainerTypeName(type); - if (!m_d->instantiatedContainersNames.contains(typeName)) { - m_d->instantiatedContainersNames.append(typeName); - auto simplifiedType = type; - simplifiedType.setIndirections(0); - simplifiedType.setConstant(false); - simplifiedType.setReferenceType(NoReference); - simplifiedType.decideUsagePattern(); - m_d->instantiatedContainers.append(simplifiedType); - } - return; - } +public: + explicit GeneratorOptionsParser(GeneratorOptions *o) : m_options(o) {} - // Is smart pointer. Check if the (const?) pointee is already known for the given - // smart pointer type entry. - auto pt = pointeeTypeEntry(type); - const bool present = - std::any_of(m_d->instantiatedSmartPointers.cbegin(), m_d->instantiatedSmartPointers.cend(), - [typeEntry, pt] (const AbstractMetaType &t) { - return t.typeEntry() == typeEntry && pointeeTypeEntry(t) == pt; - }); - if (!present) - m_d->instantiatedSmartPointers.append(canonicalSmartPtrInstantiation(type)); -} - -void Generator::collectInstantiatedContainersAndSmartPointers(const AbstractMetaFunctionCPtr &func) -{ - addInstantiatedContainersAndSmartPointers(func->type(), func->signature()); - const AbstractMetaArgumentList &arguments = func->arguments(); - for (const AbstractMetaArgument &arg : arguments) - addInstantiatedContainersAndSmartPointers(arg.type(), func->signature()); -} + bool handleBoolOption(const QString &key, OptionSource source) override; -void Generator::collectInstantiatedContainersAndSmartPointers(const AbstractMetaClass *metaClass) -{ - if (!metaClass->typeEntry()->generateCode()) - return; - for (const auto &func : metaClass->functions()) - collectInstantiatedContainersAndSmartPointers(func); - for (const AbstractMetaField &field : metaClass->fields()) - addInstantiatedContainersAndSmartPointers(field.type(), field.name()); - const AbstractMetaClassList &innerClasses = metaClass->innerClasses(); - for (AbstractMetaClass *innerClass : innerClasses) - collectInstantiatedContainersAndSmartPointers(innerClass); -} +private: + GeneratorOptions *m_options; +}; -void Generator::collectInstantiatedContainersAndSmartPointers() +bool GeneratorOptionsParser::handleBoolOption(const QString & key, OptionSource source) { - for (const auto &func : m_d->api.globalFunctions()) - collectInstantiatedContainersAndSmartPointers(func); - for (auto metaClass : m_d->api.classes()) - collectInstantiatedContainersAndSmartPointers(metaClass); + 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; } -AbstractMetaTypeList Generator::instantiatedContainers() const +std::shared_ptr<OptionsParser> Generator::createOptionsParser() { - return m_d->instantiatedContainers; + return std::make_shared<GeneratorOptionsParser>(&GeneratorPrivate::m_options); } -AbstractMetaTypeList Generator::instantiatedSmartPointers() const -{ - return m_d->instantiatedSmartPointers; -} +QString Generator::fileNameForContextHelper(const GeneratorContext &context, + const QString &suffix, + FileNameFlags flags) -Generator::OptionDescriptions Generator::options() const { - return OptionDescriptions(); -} + if (!context.forSmartPointer()) { + const auto metaClass = context.metaClass(); + QString fileNameBase = flags.testFlag(FileNameFlag::UnqualifiedName) + ? metaClass->name() : metaClass->qualifiedCppName(); + if (!flags.testFlag(FileNameFlag::KeepCase)) + fileNameBase = fileNameBase.toLower(); + fileNameBase.replace(u"::"_s, u"_"_s); + return fileNameBase + suffix; + } -bool Generator::handleOption(const QString & /* key */, const QString & /* value */) -{ - return false; + // FIXME: PYSIDE7: Use the above code path for all types. Note the file + // names will then change to reflect the namespaces of the pointee + // (smart/integer2). + const AbstractMetaType &smartPointerType = context.preciseType(); + QString fileNameBase = getFileNameBaseForSmartPointer(smartPointerType); + return fileNameBase + suffix; } const AbstractMetaClassCList &Generator::invisibleTopNamespaces() const @@ -363,12 +155,12 @@ const AbstractMetaClassCList &Generator::invisibleTopNamespaces() const return m_d->m_invisibleTopNamespaces; } -PrimitiveTypeEntryList Generator::primitiveTypes() +PrimitiveTypeEntryCList Generator::primitiveTypes() { return TypeDatabase::instance()->primitiveTypes(); } -ContainerTypeEntryList Generator::containerTypes() +ContainerTypeEntryCList Generator::containerTypes() { return TypeDatabase::instance()->containerTypes(); } @@ -391,7 +183,7 @@ QString Generator::packageName() static QString getModuleName() { QString result = TypeDatabase::instance()->defaultPackageName(); - result.remove(0, result.lastIndexOf(QLatin1Char('.')) + 1); + result.remove(0, result.lastIndexOf(u'.') + 1); return result; } @@ -413,17 +205,19 @@ 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(cls)) + if (!shouldGenerate(typeEntry)) return true; const QString fileName = fileNameForContext(context); if (fileName.isEmpty()) return true; - QString filePath = outputDirectory() + QLatin1Char('/') + subDirectoryForClass(cls) - + QLatin1Char('/') + fileName; + QString filePath = outputDirectory() + u'/' + + subDirectoryForPackage(typeEntry->targetLangPackage()) + + u'/' + fileName; FileOut fileOut(filePath); generateClass(fileOut.stream, context); @@ -432,109 +226,101 @@ bool Generator::generateFileForContext(const GeneratorContext &context) return true; } -QString Generator::getFileNameBaseForSmartPointer(const AbstractMetaType &smartPointerType, - const AbstractMetaClass *smartPointerClass) +QString Generator::getFileNameBaseForSmartPointer(const AbstractMetaType &smartPointerType) { const AbstractMetaType innerType = smartPointerType.getSmartPointerInnerType(); - QString fileName = smartPointerClass->qualifiedCppName().toLower(); - fileName.replace(QLatin1String("::"), QLatin1String("_")); - fileName.append(QLatin1String("_")); + smartPointerType.typeEntry()->qualifiedCppName(); + QString fileName = smartPointerType.typeEntry()->qualifiedCppName().toLower(); + fileName.append(u'_'); fileName.append(innerType.name().toLower()); - + fileName.replace(u"::"_s, u"_"_s); // std::shared_ptr<std::string> return fileName; } -GeneratorContext Generator::contextForClass(const 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) +GeneratorContext + Generator::contextForSmartPointer(const AbstractMetaClassCPtr &c, + const AbstractMetaType &t, + const AbstractMetaClassCPtr &pointeeClass) { GeneratorContext result; result.m_metaClass = c; result.m_preciseClassType = t; result.m_type = GeneratorContext::SmartPointer; + result.m_pointeeClass = pointeeClass; return result; } bool Generator::generate() { - for (auto cls : m_d->api.classes()) { + for (const auto &cls : m_d->api.classes()) { if (!generateFileForContext(contextForClass(cls))) return false; - if (shouldGenerate(cls) && cls->typeEntry()->isPrivate()) + auto te = cls->typeEntry(); + if (shouldGenerate(te) && te->isPrivate()) m_d->m_hasPrivateClasses = true; } - const auto smartPointers = m_d->api.smartPointers(); - for (const AbstractMetaType &type : qAsConst(m_d->instantiatedSmartPointers)) { - const AbstractMetaClass *smartPointerClass = - AbstractMetaClass::findClass(smartPointers, type.typeEntry()); - if (!smartPointerClass) { - qCWarning(lcShiboken, "%s", - qPrintable(msgCannotFindSmartPointer(type.cppSignature(), - smartPointers))); + for (const auto &smp: m_d->api.instantiatedSmartPointers()) { + AbstractMetaClassCPtr pointeeClass; + const auto instantiatedType = smp.type.instantiations().constFirst().typeEntry(); + if (instantiatedType->isComplex()) // not a C++ primitive + pointeeClass = AbstractMetaClass::findClass(m_d->api.classes(), instantiatedType); + if (!generateFileForContext(contextForSmartPointer(smp.specialized, smp.type, + pointeeClass))) { return false; } - if (!generateFileForContext(contextForSmartPointer(smartPointerClass, type))) - return false; } return finishGeneration(); } -bool Generator::shouldGenerateTypeEntry(const TypeEntry *type) +bool Generator::shouldGenerate(const TypeEntryCPtr &typeEntry) const { - return type->generateCode() && NamespaceTypeEntry::isVisibleScope(type); + return typeEntry->shouldGenerate(); } -bool Generator::shouldGenerate(const AbstractMetaClass *metaClass) const +const ApiExtractorResult &Generator::api() const { - return shouldGenerateTypeEntry(metaClass->typeEntry()); + return m_d->api; } -void verifyDirectoryFor(const QString &file) +bool Generator::hasPrivateClasses() const { - QDir dir = QFileInfo(file).absoluteDir(); - if (!dir.exists()) { - if (!dir.mkpath(dir.absolutePath())) { - qCWarning(lcShiboken, "Unable to create directory '%s'", - qPrintable(QDir::toNativeSeparators(dir.absolutePath()))); - } - } + return m_d->m_hasPrivateClasses; } -const ApiExtractorResult &Generator::api() const +bool Generator::usePySideExtensions() { - return m_d->api; + return GeneratorPrivate::m_options.usePySideExtensions; } -bool Generator::hasPrivateClasses() const +bool Generator::avoidProtectedHack() { - return m_d->m_hasPrivateClasses; + return GeneratorPrivate::m_options.avoidProtectedHack; } -QString Generator::getFullTypeName(const TypeEntry *type) +QString Generator::getFullTypeName(TypeEntryCPtr type) { QString result = type->qualifiedCppName(); if (type->isArray()) - type = static_cast<const ArrayTypeEntry *>(type)->nestedTypeEntry(); - if (!type->isCppPrimitive()) - result.prepend(QLatin1String("::")); - return result; + type = std::static_pointer_cast<const ArrayTypeEntry>(type)->nestedTypeEntry(); + return isCppPrimitive(type) ? result : addGlobalScopePrefix(result); } QString Generator::getFullTypeName(const AbstractMetaType &type) { if (type.isCString()) - return QLatin1String("const char*"); + return u"const char*"_s; if (type.isVoidPointer()) - return QLatin1String("void*"); + return u"void*"_s; if (type.typeEntry()->isContainer()) - return QLatin1String("::") + type.cppSignature(); + return addGlobalScopePrefix(type.cppSignature()); QString typeName; if (type.typeEntry()->isComplex() && type.hasInstantiations()) typeName = getFullTypeNameWithoutModifiers(type); @@ -543,17 +329,19 @@ 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 QLatin1String("::") + 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) { if (type.isCString()) - return QLatin1String("const char*"); + return u"const char*"_s; if (type.isVoidPointer()) - return QLatin1String("void*"); + return u"void*"_s; if (!type.hasInstantiations()) return getFullTypeName(type.typeEntry()); QString typeName = type.cppSignature(); @@ -569,9 +357,9 @@ QString Generator::getFullTypeNameWithoutModifiers(const AbstractMetaType &type) typeName.chop(2); break; } - while (typeName.endsWith(QLatin1Char('*')) || typeName.endsWith(QLatin1Char(' '))) + while (typeName.endsWith(u'*') || typeName.endsWith(u' ')) typeName.chop(1); - return QLatin1String("::") + typeName; + return addGlobalScopePrefix(typeName); } std::optional<DefaultValue> @@ -584,29 +372,29 @@ std::optional<DefaultValue> if (type.isContainer()) { QString ctor = type.cppSignature(); - if (ctor.endsWith(QLatin1Char('*'))) { + if (ctor.endsWith(u'*')) { ctor.chop(1); return DefaultValue(DefaultValue::Pointer, ctor.trimmed()); } - if (ctor.startsWith(QLatin1String("const "))) + if (ctor.startsWith(u"const ")) ctor.remove(0, sizeof("const ") / sizeof(char) - 1); - if (ctor.endsWith(QLatin1Char('&'))) { + if (ctor.endsWith(u'&')) { ctor.chop(1); ctor = ctor.trimmed(); } - return DefaultValue(DefaultValue::DefaultConstructor, QLatin1String("::") + ctor); + return DefaultValue(DefaultValue::DefaultConstructor, u"::"_s + ctor); } if (type.isNativePointer()) return DefaultValue(DefaultValue::Pointer, type.typeEntry()->qualifiedCppName()); if (type.isPointer()) - return DefaultValue(DefaultValue::Pointer, QLatin1String("::") + 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 = static_cast<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); @@ -631,41 +419,40 @@ std::optional<DefaultValue> std::optional<DefaultValue> Generator::minimalConstructor(const ApiExtractorResult &api, - const TypeEntry *type, + const TypeEntryCPtr &type, QString *errorString) { if (!type) return {}; - if (type->isCppPrimitive()) { + if (isCppPrimitive(type)) { const QString &name = type->qualifiedCppName(); - return name == QLatin1String("bool") + return name == u"bool" ? DefaultValue(DefaultValue::Boolean) : DefaultValue(DefaultValue::CppScalar, name); } if (type->isEnum()) { - const auto enumEntry = static_cast<const EnumTypeEntry *>(type); - if (const auto *nullValue = enumEntry->nullValue()) + 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, - QLatin1String("static_cast< ::") + type->qualifiedCppName() - + QLatin1String(">(0)")); + "static_cast< "_L1 + getFullTypeName(type) + ">(0)"_L1); } if (type->isFlags()) { return DefaultValue(DefaultValue::Custom, - type->qualifiedCppName() + QLatin1String("(0)")); + type->qualifiedCppName() + u"(0)"_s); } if (type->isPrimitive()) { - QString ctor = static_cast<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 // bindings will tell. return ctor.isEmpty() - ? DefaultValue(DefaultValue::DefaultConstructorWithDefaultValues, QLatin1String("::") + ? DefaultValue(DefaultValue::DefaultConstructorWithDefaultValues, u"::"_s + type->qualifiedCppName()) : DefaultValue(DefaultValue::Custom, ctor); } @@ -684,25 +471,25 @@ std::optional<DefaultValue> } if (errorString != nullptr) - *errorString = QLatin1String("No default value could be determined."); + *errorString = u"No default value could be determined."_s; return {}; } static QString constructorCall(const QString &qualifiedCppName, const QStringList &args) { - return QLatin1String("::") + qualifiedCppName + QLatin1Char('(') - + args.join(QLatin1String(", ")) + QLatin1Char(')'); + return u"::"_s + qualifiedCppName + u'(' + + args.join(u", "_s) + u')'; } std::optional<DefaultValue> Generator::minimalConstructor(const ApiExtractorResult &api, - const AbstractMetaClass *metaClass, + const AbstractMetaClassCPtr &metaClass, QString *errorString) { if (!metaClass) return {}; - auto cType = static_cast<const ComplexTypeEntry *>(metaClass->typeEntry()); + auto cType = std::static_pointer_cast<const ComplexTypeEntry>(metaClass->typeEntry()); if (cType->hasDefaultConstructor()) return DefaultValue(DefaultValue::Custom, cType->defaultConstructor()); @@ -712,27 +499,27 @@ std::optional<DefaultValue> const auto &constructors = metaClass->queryFunctions(FunctionQueryOption::Constructors); for (const auto &ctor : constructors) { if (!ctor->isUserAdded() && !ctor->isPrivate() - && ctor->functionType() == AbstractMetaFunction::ConstructorFunction) { + && (ctor->isPublic() || !api.flags().testFlag(ApiExtractorFlag::AvoidProtectedHack))) { // No arguments: Default constructible const auto &arguments = ctor->arguments(); if (arguments.isEmpty()) { return DefaultValue(DefaultValue::DefaultConstructor, - QLatin1String("::") + qualifiedCppName); + u"::"_s + qualifiedCppName); } // First argument has unmodified default: Default constructible with values if (arguments.constFirst().hasUnmodifiedDefaultValueExpression()) { return DefaultValue(DefaultValue::DefaultConstructorWithDefaultValues, - QLatin1String("::") + qualifiedCppName); + u"::"_s + qualifiedCppName); } // Examine arguments, exclude functions taking a self parameter bool simple = true; bool suitable = true; - for (int i = 0, size = arguments.size(); + for (qsizetype i = 0, size = arguments.size(); suitable && i < size && !arguments.at(i).hasOriginalDefaultValueExpression(); ++i) { const AbstractMetaArgument &arg = arguments.at(i); - const TypeEntry *aType = arg.type().typeEntry(); + TypeEntryCPtr aType = arg.type().typeEntry(); suitable &= aType != cType; - simple &= aType->isCppPrimitive() || aType->isEnum() || arg.type().isPointer(); + simple &= isCppPrimitive(aType) || aType->isEnum() || arg.type().isPointer(); } if (suitable) candidates.insert(arguments.size() + (simple ? 0 : 100), ctor); @@ -742,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 (int 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; @@ -762,7 +548,7 @@ std::optional<DefaultValue> } QString Generator::translateType(AbstractMetaType cType, - const AbstractMetaClass *context, + const AbstractMetaClassCPtr &context, Options options) const { QString s; @@ -774,94 +560,141 @@ QString Generator::translateType(AbstractMetaType cType, } if (cType.isVoid()) { - s = QLatin1String("void"); + s = u"void"_s; } else if (cType.isArray()) { - s = translateType(*cType.arrayElementType(), context, options) + QLatin1String("[]"); + 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() && !copyType.typeEntry()->isCppPrimitive()) - s.prepend(QLatin1String("::")); - } 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); } } return s; } +static const QHash<QString, QString> &pythonOperators() +{ + static const QHash<QString, QString> result = { + // call operator + {u"operator()"_s, u"__call__"_s}, + // Arithmetic operators + {u"operator+"_s, u"__add__"_s}, + {u"operator-"_s, u"__sub__"_s}, + {u"operator*"_s, u"__mul__"_s}, + {u"operator/"_s, u"__div__"_s}, + {u"operator%"_s, u"__mod__"_s}, + // Inplace arithmetic operators + {u"operator+="_s, u"__iadd__"_s}, + {u"operator-="_s, u"__isub__"_s}, + {u"operator++"_s, u"__iadd__"_s}, + {u"operator--"_s, u"__isub__"_s}, + {u"operator*="_s, u"__imul__"_s}, + {u"operator%="_s, u"__imod__"_s}, + // Bitwise operators + {u"operator&"_s, u"__and__"_s}, + {u"operator^"_s, u"__xor__"_s}, + {u"operator|"_s, u"__or__"_s}, + {u"operator<<"_s, u"__lshift__"_s}, + {u"operator>>"_s, u"__rshift__"_s}, + {u"operator~"_s, u"__invert__"_s}, + // Inplace bitwise operators + {u"operator&="_s, u"__iand__"_s}, + {u"operator^="_s, u"__ixor__"_s}, + {u"operator|="_s, u"__ior__"_s}, + {u"operator<<="_s, u"__ilshift__"_s}, + {u"operator>>="_s, u"__irshift__"_s}, + // Comparison operators + {u"operator=="_s, u"__eq__"_s}, + {u"operator!="_s, u"__ne__"_s}, + {u"operator<"_s, u"__lt__"_s}, + {u"operator>"_s, u"__gt__"_s}, + {u"operator<="_s, u"__le__"_s}, + {u"operator>="_s, u"__ge__"_s}, + // Conversion (note bool has special handling with heuristics) + {u"operator int"_s, u"__int__"_s}, + {u"operator double"_s, u"__float__"_s} + }; + return result; +} + +QString Generator::pythonOperatorFunctionName(const QString &cppOpFuncName) +{ + return pythonOperators().value(cppOpFuncName); +} -QString Generator::subDirectoryForClass(const AbstractMetaClass *clazz) const +bool Generator::isPythonOperatorFunctionName(const QString &cppOpFuncName) { - return subDirectoryForPackage(clazz->package()); + return pythonOperators().contains(cppOpFuncName); } QString Generator::subDirectoryForPackage(QString packageNameIn) const { if (packageNameIn.isEmpty()) packageNameIn = packageName(); - packageNameIn.replace(QLatin1Char('.'), QDir::separator()); + packageNameIn.replace(u'.', QDir::separator()); return packageNameIn; } +QString Generator::addGlobalScopePrefix(const QString &t) +{ + return t.startsWith("std::"_L1) ? t : m_gsp + t; +} + +QString Generator::globalScopePrefix(const GeneratorContext &classContext) +{ + return classContext.useWrapper() ? QString{} : m_gsp; +} + template<typename T> -static QString getClassTargetFullName_(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 if (NamespaceTypeEntry::isVisibleScope(context->typeEntry())) { - name.prepend(QLatin1Char('.')); + name.prepend(u'.'); name.prepend(context->name()); } context = context->enclosingClass(); } if (includePackageName) { - name.prepend(QLatin1Char('.')); + name.prepend(u'.'); name.prepend(t->package()); } 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); } -QString getClassTargetFullName(const AbstractMetaType &metaType, bool includePackageName) -{ - QString name = metaType.cppSignature(); - name.replace(QLatin1String("::"), QLatin1String("_")); - name.replace(QLatin1Char('<'), QLatin1Char('_')); - name.remove(QLatin1Char('>')); - name.remove(QLatin1Char(' ')); - if (includePackageName) { - name.prepend(QLatin1Char('.')); - name.prepend(metaType.package()); - } - return name; -} - QString getFilteredCppSignatureString(QString signature) { - signature.replace(QLatin1String("::"), QLatin1String("_")); - signature.replace(QLatin1Char('<'), QLatin1Char('_')); - signature.replace(QLatin1Char('>'), QLatin1Char('_')); - signature.replace(QLatin1Char(' '), QLatin1Char('_')); + signature.replace(u"::"_s, u"_"_s); + signature.replace(u'<', u'_'); + signature.replace(u'>', u'_'); + signature.replace(u' ', u'_'); return signature; } diff --git a/sources/shiboken6/generator/generator.h b/sources/shiboken6/generator/generator.h index 48ce9edca..5b051b599 100644 --- a/sources/shiboken6/generator/generator.h +++ b/sources/shiboken6/generator/generator.h @@ -1,191 +1,40 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef GENERATOR_H #define GENERATOR_H -#include <abstractmetatype.h> +#include <abstractmetalang_typedefs.h> #include <typedatabase_typedefs.h> -#include <QtCore/QObject> -#include <QtCore/QSharedPointer> -#include <QtCore/QTextStream> #include <QtCore/QList> +#include <memory> #include <optional> + class ApiExtractorResult; -class AbstractMetaFunction; -class AbstractMetaClass; -class AbstractMetaEnum; -class TypeEntry; -class ComplexTypeEntry; -class AbstractMetaType; -class EnumTypeEntry; -class FlagsTypeEntry; +class GeneratorContext; +class DefaultValue; +struct OptionDescription; +class OptionsParser; class TextStream; -QT_BEGIN_NAMESPACE -class QFile; -class QDebug; -QT_END_NAMESPACE - -class PrimitiveTypeEntry; -class ContainerTypeEntry; - -void verifyDirectoryFor(const QString &file); - -QString getClassTargetFullName(const AbstractMetaClass *metaClass, bool includePackageName = true); -QString getClassTargetFullName(const AbstractMetaEnum &metaEnum, bool includePackageName = true); -QString getClassTargetFullName(const AbstractMetaType &metaType, bool includePackageName = true); +QString getClassTargetFullName(const AbstractMetaClassCPtr &metaClass, + bool includePackageName = true); +QString getClassTargetFullName(const AbstractMetaEnum &metaEnum, + bool includePackageName = true); QString getFilteredCppSignatureString(QString signature); /** - * PYSIDE-504: Handling the "protected hack" - * - * The problem: Creating wrappers when the class has private destructors. - * You can see an example on Windows in qclipboard_wrapper.h and others. - * Simply search for the text "// C++11: need to declare (unimplemented) destructor". - * - * The protected hack is the definition "#define protected public". - * For most compilers, this "hack" is enabled, because the problem of private - * destructors simply vanishes. - * - * If one does not want to use this hack, then a new problem arises: - * C++11 requires that a destructor is declared in a wrapper class when it is - * private in the base class. There is no implementation allowed! - * - * Unfortunately, MSVC in recent versions supports C++11, and due to restrictive - * rules, it is impossible to use the hack with this compiler. - * More unfortunate: Clang, when C++11 is enabled, also enforces a declaration - * of a private destructor, but it falsely then creates a linker error! - * - * Originally, we wanted to remove the protected hack. But due to the Clang - * problem, we gave up on removal of the protected hack and use it always - * when we can. This might change again when the Clang problem is solved. - */ - -#ifdef Q_CC_MSVC -const int alwaysGenerateDestructor = 1; -#else -const int alwaysGenerateDestructor = 0; -#endif - -class DefaultValue -{ -public: - enum Type - { - Boolean, - CppScalar, // A C++ scalar type (int,..) specified by value() - Custom, // A custom constructor/expression, uses value() as is - DefaultConstructor, // For classes named value() - DefaultConstructorWithDefaultValues, // as DefaultConstructor, but can't return {} though. - Enum, // Enum value as specified by value() - Pointer, // Pointer of type value() - Void // "", for return values only - }; - - explicit DefaultValue(Type t, QString value = QString()); - explicit DefaultValue(QString customValue); - - QString returnValue() const; - QString initialization() const; - QString constructorParameter() const; - - QString value() const { return m_value; } - void setValue(const QString &value) { m_value = value; } - - Type type() const { return m_type; } - void setType(Type type) { m_type = type; } - -private: - Type m_type; - QString m_value; -}; - -#ifndef QT_NO_DEBUG_STREAM -QDebug operator<<(QDebug debug, const DefaultValue &v); -#endif - -/** - * A GeneratorContext object contains a pointer to an AbstractMetaClass and/or a specialized - * AbstractMetaType, for which code is currently being generated. - * - * The main case is when the context contains only an AbstractMetaClass pointer, which is used - * by different methods to generate appropriate expressions, functions, type names, etc. - * - * The second case is for generation of code for smart pointers. In this case the m_metaClass member - * contains the generic template class of the smart pointer, and the m_preciseClassType member - * contains the instantiated template type, e.g. a concrete shared_ptr<int>. To - * distinguish this case, the member m_forSmartPointer is set to true. - * - * In the future the second case might be generalized for all template type instantiations. - */ -class GeneratorContext { - friend class ShibokenGenerator; - friend class Generator; -public: - enum Type { Class, WrappedClass, SmartPointer }; - - GeneratorContext() = default; - - const AbstractMetaClass *metaClass() const { return m_metaClass; } - const AbstractMetaType &preciseType() const { return m_preciseClassType; } - - bool forSmartPointer() const { return m_type == SmartPointer; } - bool useWrapper() const { return m_type == WrappedClass; } - - QString wrapperName() const - { - Q_ASSERT(m_type == WrappedClass); - return m_wrappername; - } - - QString smartPointerWrapperName() const; - -private: - const AbstractMetaClass *m_metaClass = nullptr; - AbstractMetaType m_preciseClassType; - QString m_wrappername; - Type m_type = Class; -}; - -/** * Base class for all generators. The default implementations does nothing, * 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, @@ -200,13 +49,19 @@ public: }; Q_DECLARE_FLAGS(Options, Option) + enum FileNameFlag { + UnqualifiedName = 0x1, + KeepCase = 0x2 + }; + Q_DECLARE_FLAGS(FileNameFlags, FileNameFlag) + Generator(); virtual ~Generator(); 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; @@ -239,6 +94,12 @@ public: bool hasPrivateClasses() const; + /// Returns true if the user enabled PySide extensions (command line option) + static bool usePySideExtensions(); + /// Returns true if the generated code should not use the + /// "#define protected public" hack. + static bool avoidProtectedHack(); + /** * Retrieves the name of the currently processed module. * While package name is a complete package idetification, e.g. 'PySide.QtCore', @@ -249,32 +110,34 @@ public: */ static QString moduleName(); + static QString pythonOperatorFunctionName(const QString &cppOpFuncName); + static bool isPythonOperatorFunctionName(const QString &cppOpFuncName); + protected: + /// Helper for determining the file name + static QString fileNameForContextHelper(const GeneratorContext &context, + const QString &suffix, + FileNameFlags flags = {}); + /// Returns all primitive types found by APIExtractor - static PrimitiveTypeEntryList primitiveTypes(); + static PrimitiveTypeEntryCList primitiveTypes(); /// Returns all container types found by APIExtractor - static ContainerTypeEntryList containerTypes(); + static ContainerTypeEntryCList containerTypes(); - virtual GeneratorContext contextForClass(const AbstractMetaClass *c) const; - static GeneratorContext contextForSmartPointer(const AbstractMetaClass *c, - const AbstractMetaType &t); + virtual GeneratorContext contextForClass(const AbstractMetaClassCPtr &c) const; + static GeneratorContext + contextForSmartPointer(const AbstractMetaClassCPtr &c, const AbstractMetaType &t, + const AbstractMetaClassCPtr &pointeeClass = {}); /// Generates a file for given AbstractMetaClass or AbstractMetaType (smart pointer case). bool generateFileForContext(const GeneratorContext &context); /// Returns the file base name for a smart pointer. - static QString getFileNameBaseForSmartPointer(const AbstractMetaType &smartPointerType, - const AbstractMetaClass *smartPointer); - - /// Returns true if the generator should generate any code for the TypeEntry. - static bool shouldGenerateTypeEntry(const TypeEntry *) ; + static QString getFileNameBaseForSmartPointer(const AbstractMetaType &smartPointerType); /// Returns true if the generator should generate any code for the AbstractMetaClass. - virtual bool shouldGenerate(const AbstractMetaClass *) const; - - /// Returns the subdirectory used to write the binding code of an AbstractMetaClass. - virtual QString subDirectoryForClass(const AbstractMetaClass *clazz) const; + virtual bool shouldGenerate(const TypeEntryCPtr &t) const; /** * Translate metatypes to binding source format. @@ -284,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; /** @@ -293,9 +156,9 @@ protected: static QString packageName(); // Returns the full name of the type. - static QString getFullTypeName(const TypeEntry *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 @@ -310,14 +173,14 @@ protected: * Returns a null string if it fails. */ static std::optional<DefaultValue> - minimalConstructor(const ApiExtractorResult &api, const TypeEntry *type, + minimalConstructor(const ApiExtractorResult &api, const TypeEntryCPtr &type, QString *errorString = nullptr); static std::optional<DefaultValue> minimalConstructor(const ApiExtractorResult &api, const AbstractMetaType &type, QString *errorString = nullptr); static std::optional<DefaultValue> minimalConstructor(const ApiExtractorResult &api, - const AbstractMetaClass *metaClass, + const AbstractMetaClassCPtr &metaClass, QString *errorString = nullptr); /** @@ -326,7 +189,6 @@ protected: * for which the file name must be returned * \return the file name used to write the binding code for the class */ - virtual QString fileNameSuffix() const = 0; virtual QString fileNameForContext(const GeneratorContext &context) const = 0; @@ -352,24 +214,20 @@ protected: */ virtual QString subDirectoryForPackage(QString packageName = QString()) const; - AbstractMetaTypeList instantiatedContainers() const; - AbstractMetaTypeList instantiatedSmartPointers() const; + static QString addGlobalScopePrefix(const QString &t); + static QString globalScopePrefix(const GeneratorContext &classContext); - static QString getSimplifiedContainerTypeName(const AbstractMetaType &type); - void addInstantiatedContainersAndSmartPointers(const AbstractMetaType &type, - const QString &context); + static QString m_gsp; private: struct GeneratorPrivate; GeneratorPrivate *m_d; - void collectInstantiatedContainersAndSmartPointers(const AbstractMetaFunctionCPtr &func); - void collectInstantiatedContainersAndSmartPointers(const AbstractMetaClass *metaClass); - void collectInstantiatedContainersAndSmartPointers(); }; Q_DECLARE_OPERATORS_FOR_FLAGS(Generator::Options) -using GeneratorPtr = QSharedPointer<Generator>; +Q_DECLARE_OPERATORS_FOR_FLAGS(Generator::FileNameFlags) + +using GeneratorPtr = std::shared_ptr<Generator>; using Generators = QList<GeneratorPtr>; #endif // GENERATOR_H - diff --git a/sources/shiboken6/generator/generatorcontext.cpp b/sources/shiboken6/generator/generatorcontext.cpp new file mode 100644 index 000000000..b50c2effb --- /dev/null +++ b/sources/shiboken6/generator/generatorcontext.cpp @@ -0,0 +1,38 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "generatorcontext.h" +#include <abstractmetalang.h> + +#include <QtCore/QDebug> + +using namespace Qt::StringLiterals; + +QString GeneratorContext::wrapperName() const +{ + Q_ASSERT(m_type == WrappedClass); + return m_wrappername; +} + +QString GeneratorContext::effectiveClassName() const +{ + if (m_type == SmartPointer) + return m_preciseClassType.cppSignature(); + return m_type == WrappedClass ? m_wrappername : m_metaClass->qualifiedCppName(); +} + +QDebug operator<<(QDebug debug, const GeneratorContext &c) +{ + QDebugStateSaver saver(debug); + debug.noquote(); + debug.nospace(); + debug << "GeneratorContext(\"" << c.metaClass()->name() << "\" "; + if (c.useWrapper()) + debug << "[wrapper]"; + else if (c.forSmartPointer()) + debug << "[smart pointer] \"" << c.preciseType().cppSignature() << '"'; + else + debug << "[class]"; + debug << ')'; + return debug; +} diff --git a/sources/shiboken6/generator/generatorcontext.h b/sources/shiboken6/generator/generatorcontext.h new file mode 100644 index 000000000..2e58d4346 --- /dev/null +++ b/sources/shiboken6/generator/generatorcontext.h @@ -0,0 +1,56 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef GENERATORCONTEXT_H +#define GENERATORCONTEXT_H + +#include <abstractmetalang_typedefs.h> +#include <abstractmetatype.h> +#include <QtCore/QList> + +QT_FORWARD_DECLARE_CLASS(QDebug); + +// A GeneratorContext object contains a pointer to an AbstractMetaClass and/or a specialized +// AbstractMetaType, for which code is currently being generated. +// +// The main case is when the context contains only an AbstractMetaClass pointer, which is used +// by different methods to generate appropriate expressions, functions, type names, etc. +// +// The second case is for generation of code for smart pointers. In this case the m_metaClass +// member contains the generic template class of the smart pointer, and the m_preciseClassType +// member contains the instantiated template type, e.g. a concrete shared_ptr<int>. To +// distinguish this case, the member m_forSmartPointer is set to true. +// +// In the future the second case might be generalized for all template type instantiations. + +class GeneratorContext { + friend class ShibokenGenerator; + friend class Generator; +public: + enum Type { Class, WrappedClass, SmartPointer }; + + GeneratorContext() = default; + + AbstractMetaClassCPtr metaClass() const { return m_metaClass; } + const AbstractMetaType &preciseType() const { return m_preciseClassType; } + AbstractMetaClassCPtr pointeeClass() const { return m_pointeeClass; } + + bool forSmartPointer() const { return m_type == SmartPointer; } + bool useWrapper() const { return m_type == WrappedClass; } + + QString wrapperName() const; + /// Returns the wrapper name in case of useWrapper(), the qualified class + /// name or the smart pointer specialization. + QString effectiveClassName() const; + +private: + AbstractMetaClassCPtr m_metaClass; + AbstractMetaClassCPtr m_pointeeClass; + AbstractMetaType m_preciseClassType; + QString m_wrappername; + Type m_type = Class; +}; + +QDebug operator<<(QDebug debug, const GeneratorContext &c); + +#endif // GENERATORCONTEXT_H diff --git a/sources/shiboken6/generator/main.cpp b/sources/shiboken6/generator/main.cpp index 315e963c4..9871df206 100644 --- a/sources/shiboken6/generator/main.cpp +++ b/sources/shiboken6/generator/main.cpp @@ -1,701 +1,369 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QCoreApplication> -#include <QLibrary> -#include <QtCore/QFile> -#include <QtCore/QDir> -#include <QtCore/QVariant> -#include <iostream> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "shibokenconfig.h" +#include "cppgenerator.h" +#include "generator.h" +#include "headergenerator.h" +#include "qtdocgenerator.h" + #include <apiextractor.h> #include <apiextractorresult.h> +#include <exception.h> #include <fileout.h> +#include <messages.h> +#include <optionsparser.h> #include <reporthandler.h> #include <typedatabase.h> -#include <messages.h> -#include "generator.h" -#include "shibokenconfig.h" -#include "cppgenerator.h" -#include "headergenerator.h" -#include "qtdocgenerator.h" -#include <exception> +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QLibrary> +#include <QtCore/QVariant> -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 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"); } +#include "qtcompat.h" -static const char helpHint[] = "Note: use --help or -h for more information.\n"; - -using OptionDescriptions = Generator::OptionDescriptions; +#include <exception> +#include <iostream> -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()); - } +using namespace Qt::StringLiterals; - QVariantMap options; // string,stringlist for path lists, etc. - QStringList positionalArguments; -}; +static const char helpHint[] = "Note: use --help or -h for more information.\n"; +static const char appName[] = "shiboken"; -void CommandLineArguments::addToOptionsList(const QString &option, - const QString &value) +static inline Generators docGenerators() { - 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); - } + Generators result; +#ifdef DOCSTRINGS_ENABLED + result.append(GeneratorPtr(new QtDocGenerator)); +#endif + return result; } -void CommandLineArguments::addToOptionsList(const QString &option, - const QStringList &value) +static inline Generators shibokenGenerators() { - 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); - } + Generators result; + result << GeneratorPtr(new CppGenerator) << GeneratorPtr(new HeaderGenerator); + return result; } -void CommandLineArguments::addToOptionsList(const QString &option, - const QString &listValue, - QChar separator) +struct CommonOptions { - const auto newValues = listValue.split(separator, Qt::SkipEmptyParts); - addToOptionsList(option, newValues); -} + 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; +}; -static void printOptions(QTextStream &s, const OptionDescriptions &options) +class CommonOptionsParser : public OptionsParser { - s.setFieldAlignment(QTextStream::AlignLeft); - for (const auto &od : options) { - if (!od.first.startsWith(QLatin1Char('-'))) - s << "--"; - s << od.first; - if (od.second.isEmpty()) { - s << ", "; - } else { - s << Qt::endl; - const auto lines = QStringView{od.second}.split(QLatin1Char('\n')); - for (const auto &line : lines) - s << " " << line << Qt::endl; - s << Qt::endl; - } - } -} +public: + explicit CommonOptionsParser(CommonOptions *o) : m_options(o) {} -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 {}; - } + bool handleBoolOption(const QString &key, OptionSource source) override; + bool handleOption(const QString &key, const QString &value, OptionSource source) override; - CommandLineArguments args; - - while (!projectFile.atEnd()) { - line = projectFile.readLine().trimmed(); - if (line.isEmpty()) - continue; - - int split = line.indexOf('='); - QByteArray key; - QString value; - if (split > 0) { - key = line.left(split).trimmed(); - value = QString::fromUtf8(line.mid(split + 1).trimmed()); - } else { - key = line; - } + static OptionDescriptions optionDescriptions(); - if (key == "include-path") { - args.addToOptionsList(includePathOption(), - QDir::toNativeSeparators(value)); - } else if (key == "framework-include-path") { - args.addToOptionsList(frameworkIncludePathOption(), - QDir::toNativeSeparators(value)); - } else if (key == "system-include-paths") { - args.addToOptionsList(systemIncludePathOption(), - QDir::toNativeSeparators(value)); - } else if (key == "typesystem-path") { - args.addToOptionsList(typesystemPathOption(), - QDir::toNativeSeparators(value)); - } else if (key == "language-level") { - args.options.insert(languageLevelOption(), value); - } else if (key == "clang-option") { - args.addToOptionsList(clangOptionsOption(), value); - } else if (key == "clang-options") { - args.addToOptionsList(clangOptionsOption(), - value, clangOptionsSplitter); - } else if (key == "api-version") { - args.addToOptionsList(apiVersionOption(), - value, apiVersionSplitter); - } else if (key == "keywords") { - args.addToOptionsList(keywordsOption(), - value, keywordsSplitter); - } else if (key == "drop-type-entries") { - args.addToOptionsList(dropTypeEntriesOption(), - value, dropTypeEntriesSplitter); - } else if (key == "header-file") { - args.positionalArguments.prepend(value); - } else if (key == "typesystem-file") { - args.positionalArguments.append(value); - } else { - args.options.insert(QString::fromUtf8(key), value); - } - } +private: + CommonOptions *m_options; +}; - return args; +OptionDescriptions CommonOptionsParser::optionDescriptions() +{ + return { + {u"debug-level=[sparse|medium|full]"_s, + u"Set the debug level"_s}, + {u"documentation-only"_s, + u"Do not generates any code, just the documentation"_s}, + {u"compiler=<type>"_s, + u"Emulated compiler type (g++, msvc, clang)"_s}, + {u"platform=<name>"_s, + u"Emulated platform (windows, darwin, unix)"_s}, + {u"compiler-path=<file>"_s, + u"Path to the compiler for determining builtin include paths"_s}, + {u"generator-set=<\"generator module\">"_s, + u"generator-set to be used. e.g. qtdoc"_s}, + {u"diff"_s, u"Print a diff of wrapper files"_s}, + {u"dry-run"_s, u"Dry run, do not generate wrapper files"_s}, + {u"-h"_s, {} }, + {u"help"_s, u"Display this help and exit"_s}, + {u"-I<path>"_s, {} }, + {u"include-paths="_s + OptionsParser::pathSyntax(), + u"Include paths used by the C++ parser"_s}, + {u"license-file=<license-file>"_s, + u"File used for copyright headers of generated files"_s}, + {u"no-suppress-warnings"_s, + u"Show all warnings"_s}, + {u"output-directory=<path>"_s, + u"The directory where the generated files will be written"_s}, + {u"project-file=<file>"_s, + u"text file containing a description of the binding project.\n" + "Replaces and overrides command line arguments"_s}, + {u"silent"_s, u"Avoid printing any message"_s}, + {u"print-builtin-types"_s, + u"Print information about builtin types"_s}, + {u"version"_s, + u"Output version information and exit"_s} + }; } -static std::optional<CommandLineArguments> getProjectFileArguments() +bool CommonOptionsParser::handleBoolOption(const QString &key, OptionSource source) { - QStringList arguments = QCoreApplication::arguments(); - QString appName = arguments.constFirst(); - arguments.removeFirst(); - - QString projectFileName; - for (const QString &arg : qAsConst(arguments)) { - if (arg.startsWith(QLatin1String("--project-file"))) { - int split = arg.indexOf(QLatin1Char('=')); - if (split > 0) - projectFileName = arg.mid(split + 1).trimmed(); - break; + if (source == OptionSource::CommandLineSingleDash) { + if (key == u"h") { + m_options->help = true; + return true; } + return false; } - if (projectFileName.isEmpty()) - return CommandLineArguments{}; - - if (!QFile::exists(projectFileName)) { - std::cerr << qPrintable(appName) << ": Project file \"" - << qPrintable(projectFileName) << "\" not found.\n"; - return {}; + if (key == u"version") { + m_options->version = true; + return true; } - - QFile projectFile(projectFileName); - if (!projectFile.open(QIODevice::ReadOnly)) { - std::cerr << qPrintable(appName) << ": Cannot open project file \"" - << qPrintable(projectFileName) << "\" : " << qPrintable(projectFile.errorString()) - << '\n'; - return {}; + if (key == u"help") { + m_options->help = true; + return true; } - return processProjectFile(appName, projectFile); -} - -static void getCommandLineArg(QString arg, int &argNum, CommandLineArguments &args) -{ - if (arg.startsWith(QLatin1String("--"))) { - arg.remove(0, 2); - const int split = arg.indexOf(QLatin1Char('=')); - if (split < 0) { - args.options.insert(arg, QString()); - return; - } - const QString option = arg.left(split); - const QString value = arg.mid(split + 1).trimmed(); - if (option == includePathOption() || option == frameworkIncludePathOption() - || option == systemIncludePathOption() || option == typesystemPathOption()) { - args.addToOptionsPathList(option, value); - } else if (option == apiVersionOption()) { - args.addToOptionsList(apiVersionOption(), value, apiVersionSplitter); - } else if (option == dropTypeEntriesOption()) { - args.addToOptionsList(dropTypeEntriesOption(), value, dropTypeEntriesSplitter); - } else if (option == clangOptionOption()) { - args.addToOptionsList(clangOptionsOption(), value); - } else if (option == clangOptionsOption()) { - args.addToOptionsList(clangOptionsOption(), value, clangOptionsSplitter); - } else if (option == keywordsOption()) { - args.addToOptionsList(keywordsOption(), value, keywordsSplitter); - } else { - args.options.insert(option, value); - } - return; + if (key == u"diff") { + FileOut::setDiff(true); + return true; } - if (arg.startsWith(QLatin1Char('-'))) { - arg.remove(0, 1); - if (arg.startsWith(QLatin1Char('I'))) // Shorthand path arguments -I/usr/include... - args.addToOptionsPathList(includePathOption(), arg.mid(1)); - else if (arg.startsWith(QLatin1Char('F'))) - args.addToOptionsPathList(frameworkIncludePathOption(), arg.mid(1)); - else if (arg.startsWith(QLatin1String("isystem"))) - args.addToOptionsPathList(systemIncludePathOption(), arg.mid(7)); - else if (arg.startsWith(QLatin1Char('T'))) - args.addToOptionsPathList(typesystemPathOption(), arg.mid(1)); - else if (arg == QLatin1String("h")) - args.options.insert(helpOption(), QString()); - else if (arg.startsWith(QLatin1String("std="))) - args.options.insert(languageLevelOption(), arg.mid(4)); - else - args.options.insert(arg, QString()); - return; + 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; } - 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 (int i = 1, size = arguments.size(); i < size; ++i) - getCommandLineArg(arguments.at(i).trimmed(), argNum, args); -} -static inline Generators docGenerators() -{ - Generators result; -#ifdef DOCSTRINGS_ENABLED - result.append(GeneratorPtr(new QtDocGenerator)); -#endif - return result; + return false; } -static inline Generators shibokenGenerators() +bool CommonOptionsParser::handleOption(const QString &key, const QString &value, + OptionSource source) { - Generators result; - result << GeneratorPtr(new CppGenerator) << GeneratorPtr(new HeaderGenerator); - return result; -} + 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; + } + } -static inline QString languageLevelDescription() -{ - return QLatin1String("C++ Language level (c++11..c++17, default=") - + QLatin1String(clang::languageLevelOption(clang::emulatedCompilerLanguageLevel())) - + QLatin1Char(')'); + return false; } void printUsage() { - const QChar pathSplitter = QDir::listSeparator(); + const auto generatorOptions = Generator::options(); + 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 = { - {QLatin1String("api-version=<\"package mask\">,<\"version\">"), - QLatin1String("Specify the supported api version used to generate the bindings")}, - {QLatin1String("debug-level=[sparse|medium|full]"), - QLatin1String("Set the debug level")}, - {QLatin1String("documentation-only"), - QLatin1String("Do not generates any code, just the documentation")}, - {QLatin1String("drop-type-entries=\"<TypeEntry0>[;TypeEntry1;...]\""), - QLatin1String("Semicolon separated list of type system entries (classes, namespaces,\n" - "global functions and enums) to be dropped from generation.")}, - {keywordsOption() + QStringLiteral("=keyword1[,keyword2,...]"), - QLatin1String("A comma-separated list of keywords for conditional typesystem parsing")}, - {clangOptionOption(), - QLatin1String("Option to be passed to clang")}, - {clangOptionsOption(), - QLatin1String("A comma-separated list of options to be passed to clang")}, - {QLatin1String("-F<path>"), {} }, - {QLatin1String("framework-include-paths=") + pathSyntax, - QLatin1String("Framework include paths used by the C++ parser")}, - {QLatin1String("-isystem<path>"), {} }, - {QLatin1String("system-include-paths=") + pathSyntax, - QLatin1String("System include paths used by the C++ parser")}, - {useGlobalHeaderOption(), - QLatin1String("Use the global headers in generated code.")}, - {QLatin1String("generator-set=<\"generator module\">"), - QLatin1String("generator-set to be used. e.g. qtdoc")}, - {skipDeprecatedOption(), - QLatin1String("Skip deprecated functions")}, - {diffOption(), QLatin1String("Print a diff of wrapper files")}, - {dryrunOption(), QLatin1String("Dry run, do not generate wrapper files")}, - {QLatin1String("-h"), {} }, - {helpOption(), QLatin1String("Display this help and exit")}, - {QLatin1String("-I<path>"), {} }, - {QLatin1String("include-paths=") + pathSyntax, - QLatin1String("Include paths used by the C++ parser")}, - {languageLevelOption() + QLatin1String("=, -std=<level>"), - languageLevelDescription()}, - {QLatin1String("license-file=<license-file>"), - QLatin1String("File used for copyright headers of generated files")}, - {QLatin1String("no-suppress-warnings"), - QLatin1String("Show all warnings")}, - {QLatin1String("output-directory=<path>"), - QLatin1String("The directory where the generated files will be written")}, - {QLatin1String("project-file=<file>"), - QLatin1String("text file containing a description of the binding project.\n" - "Replaces and overrides command line arguments")}, - {QLatin1String("silent"), QLatin1String("Avoid printing any message")}, - {QLatin1String("-T<path>"), {} }, - {QLatin1String("typesystem-paths=") + pathSyntax, - QLatin1String("Paths used when searching for typesystems")}, - {QLatin1String("version"), - QLatin1String("Output version information and exit")} - }; - 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()); - } - } + << "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(QLatin1Char(' ')); + 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(QLatin1String("version")); - 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(QLatin1String("generator-set")); - if (ait == args.options.end()) // Also check QLatin1String("generatorSet") command line argument for backward compatibility. - ait = args.options.find(QLatin1String("generatorSet")); - 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 == QLatin1String("qtdoc")) { + if (commonOptions.generatorSet == u"qtdoc") { generators = docGenerators(); if (generators.isEmpty()) { - errorPrint(QLatin1String("Doc strings extractions was not enabled in this shiboken build.")); + errorPrint(u"Doc strings extractions was not enabled in this shiboken build."_s, argV); return EXIT_FAILURE; } - } else if (generatorSet.isEmpty() || generatorSet == QLatin1String("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(QLatin1String("Unknown generator set, try \"shiboken\" or \"qtdoc\".")); + errorPrint(u"Unknown generator set, try \"shiboken\" or \"qtdoc\"."_s, argV); return EXIT_FAILURE; } - ait = args.options.find(QLatin1String("help")); - 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(QLatin1String("license-file")); - 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 = QLatin1String("out"); - ait = args.options.find(QLatin1String("output-directory")); - 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(QLatin1String("silent")); - if (ait != args.options.end()) { - extractor.setSilent(true); - args.options.erase(ait); - } else { - ait = args.options.find(QLatin1String("debug-level")); - if (ait != args.options.end()) { - const QString value = ait.value().toString(); - if (!ReportHandler::setDebugLevelFromArg(value)) { - errorPrint(QLatin1String("Invalid debug level: ") + value); - return EXIT_FAILURE; - } - args.options.erase(ait); - } - } - ait = args.options.find(QLatin1String("no-suppress-warnings")); - 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(QLatin1Char(',')); - QString package; - QString version; - package = parts.count() == 1 ? QLatin1String("*") : parts.constFirst(); - version = parts.constLast(); - if (!extractor.setApiVersion(package, version)) { - errorPrint(msgInvalidVersion(package, version)); - return EXIT_FAILURE; - } + 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; } - } - - 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); + commonOptions.typeSystemFileName = options.positionalArguments.takeLast(); + commonOptions.headers = options.positionalArguments; } - 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); - } - - parseIncludePathOption(includePathOption(), HeaderType::Standard, - args, extractor); - parseIncludePathOption(frameworkIncludePathOption(), HeaderType::Framework, - args, extractor); - parseIncludePathOption(systemIncludePathOption(), HeaderType::System, - args, extractor); - - if (args.positionalArguments.size() < 2) { - errorPrint(QLatin1String("Insufficient positional arguments, specify header-file and typesystem-file.")); - std::cout << '\n'; - printUsage(); - return EXIT_FAILURE; - } - - const QString typeSystemFileName = args.positionalArguments.takeLast(); - QString messagePrefix = QFileInfo(typeSystemFileName).baseName(); - if (messagePrefix.startsWith(QLatin1String("typesystem_"))) + QString messagePrefix = QFileInfo(commonOptions.typeSystemFileName).baseName(); + if (messagePrefix.startsWith(u"typesystem_")) messagePrefix.remove(0, 11); - ReportHandler::setPrefix(QLatin1Char('(') + messagePrefix + QLatin1Char(')')); + ReportHandler::setPrefix(u'(' + messagePrefix + u')'); QFileInfoList cppFileNames; - for (const QString &cppFileName : qAsConst(args.positionalArguments)) { + for (const QString &cppFileName : std::as_const(commonOptions.headers)) { const QFileInfo cppFileNameFi(cppFileName); if (!cppFileNameFi.isFile() && !cppFileNameFi.isSymLink()) { - errorPrint(QLatin1Char('"') + cppFileName + QLatin1String("\" does not exist.")); + 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 : qAsConst(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); - } + optionParser.process(&options); + optionParser.clear(); - /* 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(QLatin1String("project-file")); - for (auto it = projectFileArguments.options.cbegin(), end = projectFileArguments.options.cend(); - it != end; ++it) { - args.options.remove(it.key()); - } - - 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); - - auto shibokenGenerator = dynamic_cast<const ShibokenGenerator *>(generators.constFirst().data()); - const bool usePySideExtensions = shibokenGenerator && shibokenGenerator->usePySideExtensions(); + extractor.setTypeSystem(commonOptions.typeSystemFileName); - const std::optional<ApiExtractorResult> apiOpt = extractor.run(usePySideExtensions); + ApiExtractorFlags apiExtractorFlags; + if (generators.constFirst()->usePySideExtensions()) + apiExtractorFlags.setFlag(ApiExtractorFlag::UsePySideExtensions); + if (generators.constFirst()->avoidProtectedHack()) + apiExtractorFlags.setFlag(ApiExtractorFlag::AvoidProtectedHack); + const std::optional<ApiExtractorResult> apiOpt = extractor.run(apiExtractorFlags); if (!apiOpt.has_value()) { - errorPrint(QLatin1String("Error running ApiExtractor.")); + errorPrint(u"Error running ApiExtractor."_s, argV); return EXIT_FAILURE; } @@ -708,32 +376,59 @@ int shibokenMain(int argc, char *argv[]) << "\n\nType datase:\n" << *TypeDatabase::instance(); } - for (const GeneratorPtr &g : qAsConst(generators)) { - g->setOutputDirectory(outputDirectory); - g->setLicenseComment(licenseComment); - ReportHandler::startProgress(QByteArray("Running ") + g->name() + "..."); + if (commonOptions.printBuiltinTypes) + TypeDatabase::instance()->formatBuiltinTypes(qInfo()); + + for (const GeneratorPtr &g : std::as_const(generators)) { + g->setOutputDirectory(commonOptions.outputDirectory); + g->setLicenseComment(commonOptions.licenseComment); + ReportHandler::startProgress("Ran "_ba + g->name() + '.'); const bool ok = g->setup(apiOpt.value()) && g->generate(); ReportHandler::endProgress(); if (!ok) { - errorPrint(QLatin1String("Error running generator: ") - + QLatin1String(g->name()) + QLatin1Char('.')); + errorPrint(u"Error running generator: "_s + + QLatin1StringView(g->name()) + u'.', argV); return EXIT_FAILURE; } } + if (commonOptions.logUnmatched) + TypeDatabase::instance()->logUnmatched(); + const QByteArray doneMessage = ReportHandler::doneMessage(); std::cout << doneMessage.constData() << std::endl; return EXIT_SUCCESS; } +#ifndef Q_OS_WIN + +static inline QString argvToString(const char *arg) +{ + return QString::fromLocal8Bit(arg); +} + int main(int argc, char *argv[]) +#else + +static inline QString argvToString(const wchar_t *arg) +{ + return QString::fromWCharArray(arg); +} + +int wmain(int argc, wchar_t *argv[]) +#endif { int ex = EXIT_SUCCESS; + + QStringList argV; + argV.reserve(argc - 1); + std::transform(argv + 1, argv + argc, std::back_inserter(argV), argvToString); + try { - ex = shibokenMain(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 401441d1b..1634a7e83 100644 --- a/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp +++ b/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp @@ -1,33 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "qtdocgenerator.h" +#include "generatorcontext.h" +#include "codesnip.h" #include "exception.h" +#include "abstractmetaargument.h" #include "apiextractorresult.h" #include "qtxmltosphinx.h" #include "rstformat.h" @@ -37,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> @@ -44,25 +23,97 @@ #include <reporthandler.h> #include <textstream.h> #include <typedatabase.h> -#include <typesystem.h> +#include <functiontypeentry.h> +#include <enumtypeentry.h> +#include <complextypeentry.h> +#include <flagstypeentry.h> +#include <primitivetypeentry.h> #include <qtdocparser.h> #include <doxygenparser.h> +#include "qtcompat.h" + #include <QtCore/QTextStream> #include <QtCore/QFile> #include <QtCore/QDir> +#include <QtCore/QJsonArray> +#include <QtCore/QJsonDocument> +#include <QtCore/QJsonObject> +#include <QtCore/QSet> #include <algorithm> #include <limits> -static inline QString additionalDocumentationOption() { return QStringLiteral("additional-documentation"); } +using namespace Qt::StringLiterals; + +static inline QString classScope(const AbstractMetaClassCPtr &metaClass) +{ + return metaClass->fullName(); +} + +struct DocPackage +{ + QStringList classPages; + QStringList decoratorPages; + AbstractMetaFunctionCList globalFunctions; + AbstractMetaEnumList globalEnums; +}; + +struct DocGeneratorOptions +{ + QtXmlToSphinxParameters parameters; + QString extraSectionDir; + QString additionalDocumentationList; + QString inheritanceFile; + bool doxygen = false; + bool inheritanceDiagram = true; +}; + +struct GeneratorDocumentation +{ + struct Property + { + QString name; + Documentation documentation; + AbstractMetaType type; + AbstractMetaFunctionCPtr getter; + AbstractMetaFunctionCPtr setter; + AbstractMetaFunctionCPtr reset; + AbstractMetaFunctionCPtr notify; + }; + + AbstractMetaFunctionCList allFunctions; + AbstractMetaFunctionCList tocNormalFunctions; // Index lists + AbstractMetaFunctionCList tocVirtuals; + AbstractMetaFunctionCList tocSignalFunctions; + AbstractMetaFunctionCList tocSlotFunctions; + AbstractMetaFunctionCList tocStaticFunctions; + + QList<Property> properties; +}; + +static bool operator<(const GeneratorDocumentation::Property &lhs, + const GeneratorDocumentation::Property &rhs) +{ + return lhs.name < rhs.name; +} -static inline QString none() { return QStringLiteral("None"); } +static QString propertyRefTarget(const QString &name) +{ + QString result = name; + // For sphinx referencing, disambiguate the target from the getter name + // by appending an invisible "Hangul choseong filler" character. + result.append(QChar(0x115F)); + return result; +} + +constexpr auto additionalDocumentationOption = "additional-documentation"_L1; + +constexpr auto none = "None"_L1; static bool shouldSkip(const AbstractMetaFunctionCPtr &func) { - // Constructors go to separate section - if (DocParser::skipForQuery(func) || func->isConstructor()) + if (DocParser::skipForQuery(func)) return true; // Search a const clone (QImage::bits() vs QImage::bits() const) @@ -75,12 +126,12 @@ static bool shouldSkip(const AbstractMetaFunctionCPtr &func) if (f != func && f->isConstant() && f->name() == func->name() - && f->arguments().count() == funcArgs.count()) { + && f->arguments().size() == funcArgs.size()) { // Compare each argument bool cloneFound = true; const AbstractMetaArgumentList fargs = f->arguments(); - for (int i = 0, max = funcArgs.count(); i < max; ++i) { + for (qsizetype i = 0, max = funcArgs.size(); i < max; ++i) { if (funcArgs.at(i).type().typeEntry() != fargs.at(i).type().typeEntry()) { cloneFound = false; break; @@ -95,116 +146,205 @@ 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 TypeEntry *te) +static inline QVersionNumber versionOf(const TypeEntryCPtr &te) { if (te) { const auto version = te->version(); if (!version.isNull() && version > QVersionNumber(0, 0)) return version; } - return QVersionNumber(); -} - -static const QHash<QString, QString> &operatorMapping() -{ - static const QHash<QString, QString> result = { - {QLatin1String("operator+"), QLatin1String("__add__")}, - {QLatin1String("operator+="), QLatin1String("__iadd__")}, - {QLatin1String("operator-"), QLatin1String("__sub__")}, - {QLatin1String("operator-="), QLatin1String("__isub__")}, - {QLatin1String("operator*"), QLatin1String("__mul__")}, - {QLatin1String("operator*="), QLatin1String("__imul__")}, - {QLatin1String("operator/"), QLatin1String("__div__")}, - {QLatin1String("operator/="), QLatin1String("__idiv__")}, - {QLatin1String("operator%"), QLatin1String("__mod__")}, - {QLatin1String("operator%="), QLatin1String("__imod__")}, - {QLatin1String("operator<<"), QLatin1String("__lshift__")}, - {QLatin1String("operator<<="), QLatin1String("__ilshift__")}, - {QLatin1String("operator>>"), QLatin1String("__rshift__")}, - {QLatin1String("operator>>="), QLatin1String("__irshift__")}, - {QLatin1String("operator&"), QLatin1String("__and__")}, - {QLatin1String("operator&="), QLatin1String("__iand__")}, - {QLatin1String("operator|"), QLatin1String("__or__")}, - {QLatin1String("operator|="), QLatin1String("__ior__")}, - {QLatin1String("operator^"), QLatin1String("__xor__")}, - {QLatin1String("operator^="), QLatin1String("__ixor__")}, - {QLatin1String("operator=="), QLatin1String("__eq__")}, - {QLatin1String("operator!="), QLatin1String("__ne__")}, - {QLatin1String("operator<"), QLatin1String("__lt__")}, - {QLatin1String("operator<="), QLatin1String("__le__")}, - {QLatin1String("operator>"), QLatin1String("__gt__")}, - {QLatin1String("operator>="), QLatin1String("__ge__")}, - }; - return result; + return {}; +} + +struct docRef +{ + explicit docRef(const char *kind, QAnyStringView name) : + m_kind(kind), m_name(name) {} + + const char *m_kind; + QAnyStringView m_name; +}; + +static TextStream &operator<<(TextStream &s, const docRef &dr) +{ + s << ':' << dr.m_kind << ":`" << dr.m_name << '`'; + return s; } -static QString getFuncName(const AbstractMetaFunctionCPtr& cppFunc) +static QString fileNameToTocEntry(const QString &fileName) { - const auto it = operatorMapping().constFind(cppFunc->name()); - QString result = it != operatorMapping().cend() ? it.value() : cppFunc->name(); - result.replace(QLatin1String("::"), QLatin1String(".")); + constexpr auto rstSuffix = ".rst"_L1; + + QString result = fileName; + if (result.endsWith(rstSuffix)) + result.chop(rstSuffix.size()); // Remove the .rst extension + // skip namespace if necessary + auto lastDot = result.lastIndexOf(u'.'); + if (lastDot != -1) + result.remove(0, lastDot + 1); return result; } +static void readExtraDoc(const QFileInfo &fi, + const QString &moduleName, + const QString &outputDir, + DocPackage *docPackage, QStringList *extraTocEntries) +{ + // Strip to "Property.rst" in output directory + const QString newFileName = fi.fileName().mid(moduleName.size() + 1); + QFile sourceFile(fi.absoluteFilePath()); + if (!sourceFile.open(QIODevice::ReadOnly|QIODevice::Text)) { + qCWarning(lcShibokenDoc, "%s", qPrintable(msgCannotOpenForReading(sourceFile))); + return; + } + const QByteArray contents = sourceFile.readAll(); + sourceFile.close(); + QFile targetFile(outputDir + u'/' + newFileName); + if (!targetFile.open(QIODevice::WriteOnly|QIODevice::Text)) { + qCWarning(lcShibokenDoc, "%s", qPrintable(msgCannotOpenForWriting(targetFile))); + return; + } + targetFile.write(contents); + if (contents.contains("decorator::")) + docPackage->decoratorPages.append(newFileName); + else + docPackage->classPages.append(newFileName); + extraTocEntries->append(fileNameToTocEntry(newFileName)); +} + +// Format a short documentation reference (automatically dropping the prefix +// by using '~'), usable for property/attributes ("attr"). +struct shortDocRef +{ + explicit shortDocRef(const char *kind, QAnyStringView name) : + m_kind(kind), m_name(name) {} + + const char *m_kind; + QAnyStringView m_name; +}; + +static TextStream &operator<<(TextStream &s, const shortDocRef &sdr) +{ + s << ':' << sdr.m_kind << ":`~" << sdr.m_name << '`'; + return s; +} + +struct functionRef : public docRef +{ + explicit functionRef(QAnyStringView name) : docRef("meth", name) {} +}; + +struct classRef : public shortDocRef +{ + explicit classRef(QAnyStringView name) : shortDocRef("class", name) {} +}; + +struct propRef : public shortDocRef // Attribute/property (short) reference +{ + explicit propRef(const QString &target) : + shortDocRef("attr", target) {} +}; + +struct headline +{ + explicit headline(QAnyStringView title, char underLineChar = '-') : + m_title(title), m_underLineChar(underLineChar) {} + + QAnyStringView m_title; + char m_underLineChar; +}; + +static TextStream &operator<<(TextStream &s, const headline &h) +{ + s << h.m_title << '\n' << Pad(h.m_underLineChar, h.m_title.size()) << "\n\n"; + return s; +} + +struct pyClass +{ + explicit pyClass(QAnyStringView name) : m_name(name) {} + + QAnyStringView m_name; +}; + +static TextStream &operator<<(TextStream &s, pyClass c) +{ + s << ".. py:class:: " << c.m_name << "\n\n"; + return s; +} + +struct currentModule +{ + explicit currentModule(QAnyStringView module) : m_module(module) {} + + QAnyStringView m_module; +}; + +static TextStream &operator<<(TextStream &s, const currentModule &m) +{ + s << ".. currentmodule:: " << m.m_module << "\n\n\n"; + return s; +} + +DocGeneratorOptions QtDocGenerator::m_options; + QtDocGenerator::QtDocGenerator() { - m_parameters.snippetComparison = + m_options.parameters.snippetComparison = ReportHandler::debugLevel() >= ReportHandler::FullDebug; } QtDocGenerator::~QtDocGenerator() = default; -QString QtDocGenerator::fileNameSuffix() const +QString QtDocGenerator::fileNameSuffix() { - return QLatin1String(".rst"); + return u".rst"_s; } -bool QtDocGenerator::shouldGenerate(const AbstractMetaClass *cls) const +bool QtDocGenerator::shouldGenerate(const TypeEntryCPtr &te) const { - return Generator::shouldGenerate(cls) - && cls->typeEntry()->type() != TypeEntry::SmartPointerType; + return Generator::shouldGenerate(te) + && te->type() != TypeEntry::SmartPointerType; } QString QtDocGenerator::fileNameForContext(const GeneratorContext &context) const { - const AbstractMetaClass *metaClass = context.metaClass(); - if (!context.forSmartPointer()) { - return metaClass->name() + fileNameSuffix(); - } - const AbstractMetaType &smartPointerType = context.preciseType(); - QString fileNameBase = getFileNameBaseForSmartPointer(smartPointerType, metaClass); - return fileNameBase + fileNameSuffix(); + return fileNameForContextHelper(context, fileNameSuffix(), + FileNameFlag::UnqualifiedName + | FileNameFlag::KeepCase); } 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(QLatin1Char('\n')); + const auto lines = QStringView{doc}.split(u'\n'); int typesystemIndentation = std::numeric_limits<int>::max(); // check how many spaces must be removed from the beginning of each line for (const auto &line : lines) { @@ -225,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; + + recurseClassHierarchy(metaClass, [&res, metaClass](const AbstractMetaClassCPtr &c) { + if (c.get() != metaClass.get()) + res.append(c); + return false; + }); - s << "**Inherited by:** "; - QStringList classes; - for (auto c : qAsConst(res)) - classes << QLatin1String(":ref:`") + c->name() + QLatin1Char('`'); - s << classes.join(QLatin1String(", ")) << "\n\n"; + 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.count()) << "\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()); @@ -278,123 +439,113 @@ void QtDocGenerator::generateClass(TextStream &s, const GeneratorContext &classC if (metaClass->attributes().testFlag(AbstractMetaClass::Deprecated)) s << rstDeprecationNote("class"); - writeFunctionList(s, metaClass); - - //Function list - auto functionList = metaClass->functions(); - std::sort(functionList.begin(), functionList.end(), functionSort); + const GeneratorDocumentation doc = generatorDocumentation(metaClass); - s << "\nDetailed Description\n" - "--------------------\n\n" - << ".. _More:\n"; + if (!doc.allFunctions.isEmpty() || !doc.properties.isEmpty()) { + s << '\n' << headline("Synopsis"); + writePropertyToc(s, doc); + writeFunctionToc(s, u"Methods"_s, doc.tocNormalFunctions); + writeFunctionToc(s, u"Virtual methods"_s, doc.tocVirtuals); + writeFunctionToc(s, u"Slots"_s, doc.tocSlotFunctions); + writeFunctionToc(s, u"Signals"_s, doc.tocSignalFunctions); + writeFunctionToc(s, u"Static functions"_s, doc.tocStaticFunctions); + } - writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, metaClass, nullptr); - if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, metaClass, nullptr)) - writeFormattedDetailedText(s, documentation, metaClass); + 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"; - if (!metaClass->isNamespace()) - writeConstructors(s, metaClass); - writeEnums(s, metaClass); - if (!metaClass->isNamespace()) - writeFields(s, metaClass); + s << '\n' << headline("Detailed Description") << ".. _More:\n"; + writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, metaClass); + if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, metaClass)) + writeFormattedDetailedText(s, documentation, scope); + writeInjectDocumentation(s, TypeSystem::DocModificationAppend, metaClass); - QStringList uniqueFunctions; - for (const auto &func : qAsConst(functionList)) { - if (shouldSkip(func)) - continue; + writeEnums(s, metaClass->enums(), scope); - if (func->isStatic()) - s << ".. staticmethod:: "; - else - s << ".. method:: "; + if (!doc.properties.isEmpty()) + writeProperties(s, doc, metaClass); - writeFunction(s, metaClass, func, !uniqueFunctions.contains(func->name())); - uniqueFunctions.append(func->name()); - } + if (!metaClass->isNamespace()) + writeFields(s, metaClass); - writeInjectDocumentation(s, TypeSystem::DocModificationAppend, metaClass, nullptr); + writeFunctions(s, doc.allFunctions, metaClass, scope); } -void QtDocGenerator::writeFunctionList(TextStream& s, const AbstractMetaClass* cppClass) +void QtDocGenerator::writeFunctionToc(TextStream &s, const QString &title, + const AbstractMetaFunctionCList &functions) { - QStringList functionList; - QStringList virtualList; - QStringList signalList; - QStringList slotList; - QStringList staticFunctionList; - - const auto &classFunctions = cppClass->functions(); - for (const auto &func : classFunctions) { - if (shouldSkip(func)) - continue; - - QString className; - if (!func->isConstructor()) - className = cppClass->fullName() + QLatin1Char('.'); - else if (func->implementingClass() && func->implementingClass()->enclosingClass()) - className = func->implementingClass()->enclosingClass()->fullName() + QLatin1Char('.'); - QString funcName = getFuncName(func); - - QString str = QLatin1String("def :meth:`"); - - str += funcName; - str += QLatin1Char('<'); - if (!funcName.startsWith(className)) - str += className; - str += funcName; - str += QLatin1String(">` ("); - str += parseArgDocStyle(cppClass, func); - str += QLatin1Char(')'); - - if (func->isStatic()) - staticFunctionList << str; - else if (func->isVirtual()) - virtualList << str; - else if (func->isSignal()) - signalList << str; - else if (func->isSlot()) - slotList << str; - else - functionList << str; + if (!functions.isEmpty()) { + s << headline(title, '^') + << ".. container:: function_list\n\n" << indent; + // Functions are sorted by the Metabuilder; erase overloads + QStringList toc; + toc.reserve(functions.size()); + std::transform(functions.cbegin(), functions.end(), + std::back_inserter(toc), getFuncName); + toc.erase(std::unique(toc.begin(), toc.end()), toc.end()); + for (const auto &func : toc) + s << "* def " << functionRef(func) << '\n'; + s << outdent << "\n\n"; } +} - if (!functionList.isEmpty() || !staticFunctionList.isEmpty()) { - QtXmlToSphinx::Table functionTable; - - s << "\nSynopsis\n--------\n\n"; +void QtDocGenerator::writePropertyToc(TextStream &s, + const GeneratorDocumentation &doc) +{ + if (doc.properties.isEmpty()) + return; - writeFunctionBlock(s, QLatin1String("Functions"), functionList); - writeFunctionBlock(s, QLatin1String("Virtual functions"), virtualList); - writeFunctionBlock(s, QLatin1String("Slots"), slotList); - writeFunctionBlock(s, QLatin1String("Signals"), signalList); - writeFunctionBlock(s, QLatin1String("Static functions"), staticFunctionList); + s << headline("Properties", '^') + << ".. container:: function_list\n\n" << indent; + for (const auto &prop : doc.properties) { + s << "* " << propRef(propertyRefTarget(prop.name)); + if (prop.documentation.hasBrief()) + s << " - " << prop.documentation.brief(); + s << '\n'; } + s << outdent << "\n\n"; } -void QtDocGenerator::writeFunctionBlock(TextStream& s, const QString& title, QStringList& functions) +void QtDocGenerator::writeProperties(TextStream &s, + const GeneratorDocumentation &doc, + const AbstractMetaClassCPtr &cppClass) const { - if (!functions.isEmpty()) { - s << title << '\n' - << Pad('^', title.size()) << '\n'; - - std::sort(functions.begin(), functions.end()); - - s << ".. container:: function_list\n\n"; - Indentation indentation(s); - for (const QString &func : qAsConst(functions)) - s << "* " << func << '\n'; - s << "\n\n"; + s << "\n.. note:: Properties can be used directly when " + << "``from __feature__ import true_property`` is used or via accessor " + << "functions otherwise.\n\n"; + + const QString scope = classScope(cppClass); + for (const auto &prop : doc.properties) { + const QString type = translateToPythonType(prop.type, cppClass, /* createRef */ false); + s << ".. py:property:: " << propertyRefTarget(prop.name) + << "\n :type: " << type << "\n\n\n"; + if (!prop.documentation.isEmpty()) + writeFormattedText(s, prop.documentation.detailed(), Documentation::Native, scope); + s << "**Access functions:**\n"; + if (prop.getter) + s << " * " << functionRef(prop.getter->name()) << '\n'; + if (prop.setter) + s << " * " << functionRef(prop.setter->name()) << '\n'; + if (prop.reset) + s << " * " << functionRef(prop.reset->name()) << '\n'; + if (prop.notify) + s << " * Signal " << functionRef(prop.notify->name()) << '\n'; + s << '\n'; } } -void QtDocGenerator::writeEnums(TextStream& s, const AbstractMetaClass* cppClass) const +void QtDocGenerator::writeEnums(TextStream &s, const AbstractMetaEnumList &enums, + const QString &scope) const { - static const QString section_title = QLatin1String(".. attribute:: "); - - 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); @@ -402,116 +553,61 @@ 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 = QLatin1String(".. attribute:: "); + 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); - } -} - -void QtDocGenerator::writeConstructors(TextStream& s, const AbstractMetaClass* cppClass) const -{ - static const QString sectionTitle = QLatin1String(".. class:: "); - - auto lst = cppClass->queryFunctions(FunctionQueryOption::Constructors | FunctionQueryOption::Visible); - for (int i = lst.size() - 1; i >= 0; --i) { - if (lst.at(i)->isModifiedRemoved() || lst.at(i)->functionType() == AbstractMetaFunction::MoveConstructorFunction) - lst.removeAt(i); - } - - bool first = true; - QHash<QString, AbstractMetaArgument> arg_map; - - if (lst.isEmpty()) { - s << sectionTitle << cppClass->fullName(); - } else { - QByteArray pad; - for (const auto &func : qAsConst(lst)) { - 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->attributes().testFlag(AbstractMetaFunction::Deprecated)) - 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); + writeFormattedDetailedText(s, field.documentation(), scope); } - - s << '\n'; - - for (const auto &func : qAsConst(lst)) - writeFormattedDetailedText(s, func->documentation(), cppClass); } -QString QtDocGenerator::parseArgDocStyle(const AbstractMetaClass* /* cppClass */, - const AbstractMetaFunctionCPtr &func) +QString QtDocGenerator::formatArgs(const AbstractMetaFunctionCPtr &func) { - QString ret; + QString ret = u"("_s; int optArgs = 0; const AbstractMetaArgumentList &arguments = func->arguments(); for (const AbstractMetaArgument &arg : arguments) { - if (func->argumentRemoved(arg.argumentIndex() + 1)) + if (arg.isModifiedRemoved()) continue; bool thisIsoptional = !arg.defaultValueExpression().isEmpty(); if (optArgs || thisIsoptional) { - ret += QLatin1Char('['); + ret += u'['; optArgs++; } if (arg.argumentIndex() > 0) - ret += QLatin1String(", "); + ret += u", "_s; ret += arg.name(); if (thisIsoptional) { QString defValue = arg.defaultValueExpression(); - if (defValue == QLatin1String("QString()")) { - defValue = QLatin1String("\"\""); - } else if (defValue == QLatin1String("QStringList()") - || defValue.startsWith(QLatin1String("QVector")) - || defValue.startsWith(QLatin1String("QList"))) { - defValue = QLatin1String("list()"); - } else if (defValue == QLatin1String("QVariant()")) { - defValue = none(); + if (defValue == u"QString()") { + defValue = u"\"\""_s; + } else if (defValue == u"QStringList()" + || defValue.startsWith(u"QVector") + || defValue.startsWith(u"QList")) { + defValue = u"list()"_s; + } else if (defValue == u"QVariant()") { + defValue = none; } else { - defValue.replace(QLatin1String("::"), QLatin1String(".")); - if (defValue == QLatin1String("nullptr")) - defValue = none(); - else if (defValue == QLatin1String("0") && arg.type().isObject()) - defValue = none(); + defValue.replace(u"::"_s, u"."_s); + if (defValue == u"nullptr") + defValue = none; + else if (defValue == u"0" && arg.type().isObject()) + defValue = none; } - ret += QLatin1Char('=') + defValue; + ret += u'=' + defValue; } } - ret += QString(optArgs, QLatin1Char(']')); + ret += QString(optArgs, u']') + u')'; return ret; } @@ -521,11 +617,9 @@ void QtDocGenerator::writeDocSnips(TextStream &s, TypeSystem::Language language) { Indentation indentation(s); - QStringList invalidStrings; - const static QString startMarkup = QLatin1String("[sphinx-begin]"); - const static QString endMarkup = QLatin1String("[sphinx-end]"); - - invalidStrings << QLatin1String("*") << QLatin1String("//") << QLatin1String("/*") << QLatin1String("*/"); + static const QStringList invalidStrings{u"*"_s, u"//"_s, u"/*"_s, u"*/"_s}; + const static QString startMarkup = u"[sphinx-begin]"_s; + const static QString endMarkup = u"[sphinx-end]"_s; for (const CodeSnip &snip : codeSnips) { if ((snip.position != position) || @@ -534,19 +628,19 @@ void QtDocGenerator::writeDocSnips(TextStream &s, QString code = snip.code(); while (code.contains(startMarkup) && code.contains(endMarkup)) { - int startBlock = code.indexOf(startMarkup) + startMarkup.size(); - int endBlock = code.indexOf(endMarkup); + const auto startBlock = code.indexOf(startMarkup) + startMarkup.size(); + const auto endBlock = code.indexOf(endMarkup); if ((startBlock == -1) || (endBlock == -1)) break; QString codeBlock = code.mid(startBlock, endBlock - startBlock); - const QStringList rows = codeBlock.split(QLatin1Char('\n')); + const QStringList rows = codeBlock.split(u'\n'); int currentRow = 0; - int offset = 0; + qsizetype offset = 0; for (QString row : rows) { - for (const QString &invalidString : qAsConst(invalidStrings)) + for (const QString &invalidString : std::as_const(invalidStrings)) row.remove(invalidString); if (row.trimmed().size() == 0) { @@ -558,9 +652,9 @@ void QtDocGenerator::writeDocSnips(TextStream &s, if (currentRow == 0) { //find offset for (auto c : row) { - if (c == QLatin1Char(' ')) + if (c == u' ') offset++; - else if (c == QLatin1Char('\n')) + else if (c == u'\n') offset = 0; else break; @@ -575,304 +669,472 @@ 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 = cppClass->typeEntry()->docModifications(); for (const DocModification &mod : mods) { if (mod.mode() == mode) { - bool modOk = func ? mod.signature() == func->minimalSignature() : mod.signature().isEmpty(); - - if (modOk) { - Documentation::Format fmt; - - if (mod.format() == TypeSystem::NativeCode) - fmt = Documentation::Native; - else if (mod.format() == TypeSystem::TargetLangCode) - fmt = Documentation::Target; - else - continue; - - writeFormattedText(s, mod.code(), fmt, cppClass); + switch (mod.format()) { + case TypeSystem::NativeCode: + writeFormattedText(s, mod.code(), Documentation::Native, scope); didSomething = true; + break; + case TypeSystem::TargetLangCode: + writeFormattedText(s, mod.code(), Documentation::Target, scope); + didSomething = true; + break; + default: + break; } } } + return didSomething; +} +bool QtDocGenerator::writeInjectDocumentation(TextStream &s, + TypeSystem::DocModificationMode mode, + const AbstractMetaClassCPtr &cppClass) const +{ + const bool didSomething = + writeDocModifications(s, DocParser::getDocModifications(cppClass), + mode, classScope(cppClass)); s << '\n'; - // TODO: Deprecate the use of doc string on glue code. + // 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; + const bool didSomething = writeDocModifications(s, modifications, mode, scope); + s << '\n'; - funcName = cppClass->fullName(); - if (!func->isConstructor()) - funcName += QLatin1Char('.') + getFuncName(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; +} - return funcName + QLatin1Char('(') + parseArgDocStyle(cppClass, func) - + QLatin1Char(')'); +static QString inline toRef(const QString &t) +{ + return ":class:`~"_L1 + t + u'`'; } QString QtDocGenerator::translateToPythonType(const AbstractMetaType &type, - const AbstractMetaClass* cppClass) const + 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() }, - { QLatin1String("uchar"), pyStrT() }, - { QLatin1String("QStringList"), QLatin1String("list of strings") }, - { qVariantT(), pyObjectT() }, - { QLatin1String("quint32"), intT() }, - { QLatin1String("uint32_t"), intT() }, - { QLatin1String("quint64"), intT() }, - { QLatin1String("qint64"), intT() }, - { QLatin1String("size_t"), intT() }, - { QLatin1String("int64_t"), intT() }, - { QLatin1String("qreal"), floatT() } + if (type.typeUsagePattern() == AbstractMetaType::PrimitivePattern) { + const auto &basicName = basicReferencedTypeEntry(type.typeEntry())->name(); + if (AbstractMetaType::cppSignedIntTypes().contains(basicName) + || AbstractMetaType::cppUnsignedIntTypes().contains(basicName)) { + return intT; + } + if (AbstractMetaType::cppFloatTypes().contains(basicName)) + return floatT; + } + + static const QSet<QString> stringTypes = { + u"uchar"_s, u"std::string"_s, u"std::wstring"_s, + u"std::stringview"_s, u"std::wstringview"_s, + qStringT, u"QStringView"_s, u"QAnyStringView"_s, u"QUtf8StringView"_s + }; + if (stringTypes.contains(name)) + return pyStrT; + + static const QHash<QString, QString> typeMap = { + { cPyObjectT, pyObjectT }, + { u"QStringList"_s, u"list of strings"_s }, + { qVariantT, pyObjectT } }; - const auto found = typeMap.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 == QLatin1String("char") && type.indirections() == 1) { - strType = QLatin1String("str"); - } 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(QLatin1Char('*')); - strType.remove(QLatin1Char('>')); - strType.remove(QLatin1Char('<')); - strType.replace(QLatin1String("::"), QLatin1String(".")); - if (strType.contains(QLatin1String("QList")) || strType.contains(QLatin1String("QVector"))) { - strType.replace(QLatin1String("QList"), QLatin1String("list of ")); - strType.replace(QLatin1String("QVector"), QLatin1String("list of ")); - } else if (strType.contains(QLatin1String("QHash")) || strType.contains(QLatin1String("QMap"))) { - strType.remove(QLatin1String("QHash")); - strType.remove(QLatin1String("QMap")); - QStringList types = strType.split(QLatin1Char(',')); + strType.remove(u'*'); + strType.remove(u'>'); + strType.remove(u'<'); + strType.replace(u"::"_s, u"."_s); + if (strType.contains(u"QList") || strType.contains(u"QVector")) { + strType.replace(u"QList"_s, u"list of "_s); + strType.replace(u"QVector"_s, u"list of "_s); + } else if (strType.contains(u"QHash") || strType.contains(u"QMap")) { + strType.remove(u"QHash"_s); + strType.remove(u"QMap"_s); + QStringList types = strType.split(u','); strType = QString::fromLatin1("Dictionary with keys of type %1 and values of type %2.") .arg(types[0], types[1]); } - } else { - auto k = AbstractMetaClass::findClass(api().classes(), type.typeEntry()); - strType = k ? k->fullName() : type.name(); - strType = QStringLiteral(":any:`") + strType + QLatin1Char('`'); + 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; } -void QtDocGenerator::writeParameterType(TextStream& s, const AbstractMetaClass* cppClass, +QString QtDocGenerator::getFuncName(const AbstractMetaFunctionCPtr &cppFunc) +{ + if (cppFunc->isConstructor()) + return "__init__"_L1; + QString result = cppFunc->name(); + if (cppFunc->isOperatorOverload()) { + const QString pythonOperator = Generator::pythonOperatorFunctionName(result); + if (!pythonOperator.isEmpty()) + return pythonOperator; + } + result.replace(u"::"_s, u"."_s); + return result; +} + +void QtDocGenerator::writeParameterType(TextStream &s, + const AbstractMetaClassCPtr &cppClass, const AbstractMetaArgument &arg) const { s << ":param " << arg.name() << ": " << translateToPythonType(arg.type(), cppClass) << '\n'; } -void QtDocGenerator::writeFunctionParametersType(TextStream &s, const AbstractMetaClass *cppClass, +void QtDocGenerator::writeFunctionParametersType(TextStream &s, + const AbstractMetaClassCPtr &cppClass, const AbstractMetaFunctionCPtr &func) const { s << '\n'; const AbstractMetaArgumentList &funcArgs = func->arguments(); for (const AbstractMetaArgument &arg : funcArgs) { - - if (func->argumentRemoved(arg.argumentIndex() + 1)) - continue; - - writeParameterType(s, cppClass, arg); + if (!arg.isModifiedRemoved()) + 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->cppAttributes().testFlag(FunctionAttribute::Final)) + s << "\n:final:"; + else if (func->isAbstract()) + s << "\n:abstractmethod:"; s << "\n\n"; writeFunctionParametersType(s, cppClass, func); const auto version = versionOf(func->typeEntry()); if (!version.isNull()) s << rstVersionAdded(version); - if (func->attributes().testFlag(AbstractMetaFunction::Deprecated)) + 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); + + writeFunctionDocumentation(s, func, modifications, scope); + + if (auto propIndex = func->propertySpecIndex(); propIndex >= 0) { + const QString name = cppClass->propertySpecs().at(propIndex).name(); + const QString target = propertyRefTarget(name); + if (func->isPropertyReader()) + s << "\nGetter of property " << propRef(target) << " .\n\n"; + else if (func->isPropertyWriter()) + s << "\nSetter of property " << propRef(target) << " .\n\n"; + else if (func->isPropertyResetter()) + s << "\nReset function of property " << propRef(target) << " .\n\n"; + else if (func->attributes().testFlag(AbstractMetaFunction::Attribute::PropertyNotify)) + s << "\nNotification signal of property " << propRef(target) << " .\n\n"; + } +} + +void QtDocGenerator::writeFunctionDocumentation(TextStream &s, const AbstractMetaFunctionCPtr &func, + const DocModificationList &modifications, + const QString &scope) const + +{ + writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, modifications, func, scope); + if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, modifications, func, scope)) { + writeFormattedBriefText(s, func->documentation(), scope); + writeFormattedDetailedText(s, func->documentation(), scope); + } + writeInjectDocumentation(s, TypeSystem::DocModificationAppend, modifications, func, scope); +} + +static QStringList fileListToToc(const QStringList &items) +{ + QStringList result; + result.reserve(items.size()); + std::transform(items.cbegin(), items.cend(), std::back_inserter(result), + fileNameToTocEntry); + return result; +} + +static QStringList functionListToToc(const AbstractMetaFunctionCList &functions) +{ + QStringList result; + result.reserve(functions.size()); + for (const auto &f : functions) + result.append(f->name()); + // Functions are sorted by the Metabuilder; erase overloads + result.erase(std::unique(result.begin(), result.end()), result.end()); + return result; +} + +static QStringList enumListToToc(const AbstractMetaEnumList &enums) +{ + QStringList result; + result.reserve(enums.size()); + for (const auto &e : enums) + result.append(e.name()); + return result; +} + +// Sort entries for a TOC by first character, dropping the +// leading common Qt prefixes like 'Q'. +static QChar sortKey(const QString &key) +{ + const auto size = key.size(); + if (size >= 2 && (key.at(0) == u'Q' || key.at(0) == u'q') + && (key.at(1).isUpper() || key.at(1).isDigit())) { + return key.at(1); // "QClass" -> 'C', "qSin()" -> 'S', 'Q3DSurfaceWidget' -> '3' } - writeInjectDocumentation(s, TypeSystem::DocModificationAppend, cppClass, func); + 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, const QStringList& items) +static void writeFancyToc(TextStream& s, QAnyStringView title, + const QStringList& items, + QLatin1StringView referenceType) { using TocMap = QMap<QChar, QStringList>; + + if (items.isEmpty()) + return; + TocMap tocMap; - QChar Q = QLatin1Char('Q'); - 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(QLatin1Char('.')).last(); - if (className.startsWith(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; QtXmlToSphinx::Table table; for (auto it = tocMap.cbegin(), end = tocMap.cend(); it != end; ++it) { QtXmlToSphinx::TableRow row; - const QString charEntry = QLatin1String("**") + it.key() + QLatin1String("**"); + const QString charEntry = u"**"_s + it.key() + u"**"_s; row << QtXmlToSphinx::TableCell(charEntry); - for (const QString &item : qAsConst(it.value())) { + for (const QString &item : std::as_const(it.value())) { if (row.size() >= numColumns) { table.appendRow(row); row.clear(); row << QtXmlToSphinx::TableCell(QString{}); } - const QString entry = QLatin1String("* :doc:`") + item + QLatin1Char('`'); + const QString entry = "* :"_L1 + referenceType + ":`"_L1 + item + u'`'; row << QtXmlToSphinx::TableCell(entry); } - if (!row.isEmpty()) + if (row.size() > 1) table.appendRow(row); } 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_options.inheritanceFile.isEmpty() && !writeInheritanceFile()) + return false; + return true; +} + +bool QtDocGenerator::writeInheritanceFile() +{ + QFile inheritanceFile(m_options.inheritanceFile); + if (!inheritanceFile.open(QIODevice::WriteOnly | QIODevice::Text)) + throw Exception(msgCannotOpenForWriting(m_options.inheritanceFile)); + + QJsonObject dict; + for (const auto &c : api().classes()) { + const auto &bases = c->baseClasses(); + if (!bases.isEmpty()) { + QJsonArray list; + for (const auto &base : bases) + list.append(QJsonValue(base->fullName())); + dict[c->fullName()] = list; + } + } + QJsonDocument document; + document.setObject(dict); + inheritanceFile.write(document.toJson(QJsonDocument::Compact)); return true; } +// Remove function entries that have extra documentation pages +static inline void removeExtraDocs(const QStringList &extraTocEntries, + AbstractMetaFunctionCList *functions) +{ + auto predicate = [&extraTocEntries](const AbstractMetaFunctionCPtr &f) { + return extraTocEntries.contains(f->name()); + }; + functions->erase(std::remove_if(functions->begin(),functions->end(), predicate), + functions->end()); +} + void QtDocGenerator::writeModuleDocumentation() { - 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(QLatin1Char('.'), QLatin1Char('/')); - QString outputDir = outputDirectory() + QLatin1Char('/') + key; - FileOut output(outputDir + QLatin1String("/index.rst")); + key.replace(u'.', u'/'); + QString outputDir = outputDirectory() + u'/' + key; + FileOut output(outputDir + u"/index.rst"_s); TextStream& s = output.stream; const QString &title = it.key(); - s << ".. module:: " << title << "\n\n" - << 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 // doesn't include the PySide# prefix in their names. QString moduleName = it.key(); - const int lastIndex = moduleName.lastIndexOf(QLatin1Char('.')); + const int lastIndex = moduleName.lastIndexOf(u'.'); if (lastIndex >= 0) 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); } // Filter for "QtCore.Property.rst", skipping module doc "QtCore.rst" - const QString filter = moduleName + QLatin1String(".?*.rst"); + 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 + QLatin1Char('/') + 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); } - writeFancyToc(s, it.value()); + 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 : qAsConst(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 + QLatin1Char('/') + moduleName - + QLatin1String(".rst")); + QFile moduleDoc(m_options.extraSectionDir + u'/' + moduleName + + u".rst"_s); if (moduleDoc.open(QIODevice::ReadOnly | QIODevice::Text)) { s << moduleDoc.readAll(); moduleDoc.close(); @@ -882,16 +1144,50 @@ 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(); } } + + writeFancyToc(s, "List of Classes", fileListToToc(docPackage.classPages), + "class"_L1); + writeFancyToc(s, "List of Decorators", fileListToToc(docPackage.decoratorPages), + "deco"_L1); + writeFancyToc(s, "List of Functions", functionListToToc(docPackage.globalFunctions), + "py:func"_L1); + writeFancyToc(s, "List of Enumerations", enumListToToc(docPackage.globalEnums), + "any"_L1); + output.done(); + + if (hasGlobals) + writeGlobals(it.key(), outputDir + u'/' + globalsPage, docPackage); } } +void QtDocGenerator::writeGlobals(const QString &package, + const QString &fileName, + const DocPackage &docPackage) +{ + FileOut output(fileName); + TextStream &s = output.stream; + + // Write out functions with injected documentation + if (!docPackage.globalFunctions.isEmpty()) { + s << currentModule(package) << headline("Functions"); + writeFunctions(s, docPackage.globalFunctions, {}, {}); + } + + if (!docPackage.globalEnums.isEmpty()) { + s << headline("Enumerations"); + writeEnums(s, docPackage.globalEnums, package); + } + + output.done(); +} + static inline QString msgNonExistentAdditionalDocFile(const QString &dir, const QString &fileName) { @@ -904,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)); @@ -923,14 +1219,14 @@ void QtDocGenerator::writeAdditionalDocumentation() const continue; const QString line = QFile::decodeName(lineBA); // Parse "[directory]" specification - if (line.size() > 2 && line.startsWith(QLatin1Char('[')) && line.endsWith(QLatin1Char(']'))) { + if (line.size() > 2 && line.startsWith(u'[') && line.endsWith(u']')) { const QString dir = line.mid(1, line.size() - 2); - if (dir.isEmpty() || dir == QLatin1String(".")) { + if (dir.isEmpty() || dir == u".") { 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); } @@ -938,11 +1234,11 @@ void QtDocGenerator::writeAdditionalDocumentation() const } } else { // Normal file entry - QFileInfo fi(m_parameters.docDataDir + QLatin1Char('/') + line); + QFileInfo fi(m_options.parameters.docDataDir + u'/' + line); if (fi.isFile()) { const QString rstFileName = fi.baseName() + rstSuffix; - const QString rstFile = targetDir + QLatin1Char('/') + rstFileName; - const QString context = targetDir.mid(targetDir.lastIndexOf(QLatin1Char('/')) + 1); + const QString rstFile = targetDir + u'/' + rstFileName; + const QString context = targetDir.mid(targetDir.lastIndexOf(u'/') + 1); if (convertToRst(fi.absoluteFilePath(), rstFile, context, &errorMessage)) { ++successCount; @@ -956,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; } @@ -975,79 +1271,133 @@ 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() { return { - {QLatin1String("doc-parser=<parser>"), - QLatin1String("The documentation parser used to interpret the documentation\n" - "input files (qdoc|doxygen)")}, - {QLatin1String("documentation-code-snippets-dir=<dir>"), - QLatin1String("Directory used to search code snippets used by the documentation")}, - {QLatin1String("documentation-data-dir=<dir>"), - QLatin1String("Directory with XML files generated by documentation tool")}, - {QLatin1String("documentation-extra-sections-dir=<dir>"), - QLatin1String("Directory used to search for extra documentation sections")}, - {QLatin1String("library-source-dir=<dir>"), - QLatin1String("Directory where library source code is located")}, - {additionalDocumentationOption() + QLatin1String("=<file>"), - QLatin1String("List of additional XML files to be converted to .rst files\n" - "(for example, tutorials).")} + {u"doc-parser=<parser>"_s, + u"The documentation parser used to interpret the documentation\n" + "input files (qdoc|doxygen)"_s}, + {u"documentation-code-snippets-dir=<dir>"_s, + u"Directory used to search code snippets used by the documentation"_s}, + {u"snippets-path-rewrite=old:new"_s, + u"Replacements in code snippet path to find .cpp/.h snippets converted to Python"_s}, + {u"documentation-data-dir=<dir>"_s, + u"Directory with XML files generated by documentation tool"_s}, + {u"documentation-extra-sections-dir=<dir>"_s, + u"Directory used to search for extra documentation sections"_s}, + {u"library-source-dir=<dir>"_s, + u"Directory where library source code is located"_s}, + {additionalDocumentationOption + u"=<file>"_s, + u"List of additional XML files to be converted to .rst files\n" + "(for example, tutorials)."_s}, + {u"inheritance-file=<file>"_s, + u"Generate a JSON file containing the class inheritance."_s}, + {u"disable-inheritance-diagram"_s, + u"Disable the generation of the inheritance diagram."_s} }; } -bool QtDocGenerator::handleOption(const QString &key, const QString &value) +class QtDocGeneratorOptionsParser : public OptionsParser { - if (key == QLatin1String("library-source-dir")) { - m_parameters.libSourceDir = 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; } - if (key == QLatin1String("documentation-data-dir")) { - m_parameters.docDataDir = value; + return false; +} + +bool QtDocGeneratorOptionsParser::handleOption(const QString &key, const QString &value, + OptionSource source) +{ + if (source == OptionSource::CommandLineSingleDash) + return false; + if (key == u"library-source-dir") { + m_options->parameters.libSourceDir = value; return true; } - if (key == QLatin1String("documentation-code-snippets-dir")) { - m_parameters.codeSnippetDirs = value.split(QLatin1Char(PATH_SEP)); + if (key == u"documentation-data-dir") { + m_options->parameters.docDataDir = value; return true; } - if (key == QLatin1String("documentation-extra-sections-dir")) { - m_extraSectionDir = value; + if (key == u"documentation-code-snippets-dir") { + m_options->parameters.codeSnippetDirs = value.split(QLatin1Char(PATH_SEP)); return true; } - if (key == QLatin1String("doc-parser")) { + + if (key == u"snippets-path-rewrite") { + const auto pos = value.indexOf(u':'); + if (pos == -1) + return false; + m_options->parameters.codeSnippetRewriteOld= value.left(pos); + m_options->parameters.codeSnippetRewriteNew = value.mid(pos + 1); + return true; + } + + if (key == u"documentation-extra-sections-dir") { + m_options->extraSectionDir = value; + return true; + } + if (key == u"doc-parser") { qCDebug(lcShibokenDoc).noquote().nospace() << "doc-parser: " << value; - if (value == QLatin1String("doxygen")) - m_docParser.reset(new DoxygenParser); + if (value == u"doxygen") + 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_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, @@ -1063,20 +1413,65 @@ 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 AbstractMetaClassCPtr &cppClass) +{ + GeneratorDocumentation result; + const auto allFunctions = cppClass->functions(); + result.allFunctions.reserve(allFunctions.size()); + std::remove_copy_if(allFunctions.cbegin(), allFunctions.cend(), + std::back_inserter(result.allFunctions), shouldSkip); + + std::stable_sort(result.allFunctions.begin(), result.allFunctions.end(), functionSort); + + for (const auto &func : std::as_const(result.allFunctions)) { + if (func->isStatic()) + result.tocStaticFunctions.append(func); + else if (func->isVirtual()) + result.tocVirtuals.append(func); + else if (func->isSignal()) + result.tocSignalFunctions.append(func); + else if (func->isSlot()) + result.tocSlotFunctions.append(func); + else + result.tocNormalFunctions.append(func); + } + + // Find the property getters/setters + for (const auto &spec: cppClass->propertySpecs()) { + GeneratorDocumentation::Property property; + property.name = spec.name(); + property.type = spec.type(); + property.documentation = spec.documentation(); + if (!spec.read().isEmpty()) + property.getter = AbstractMetaFunction::find(result.allFunctions, spec.read()); + if (!spec.write().isEmpty()) + property.setter = AbstractMetaFunction::find(result.allFunctions, spec.write()); + if (!spec.reset().isEmpty()) + property.reset = AbstractMetaFunction::find(result.allFunctions, spec.reset()); + if (!spec.notify().isEmpty()) + property.notify = AbstractMetaFunction::find(result.tocSignalFunctions, spec.notify()); + result.properties.append(property); + } + std::sort(result.properties.begin(), result.properties.end()); + + return result; +} + // QtXmlToSphinxDocGeneratorInterface QString QtDocGenerator::expandFunction(const QString &function) const { - const int firstDot = function.indexOf(QLatin1Char('.')); - 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; @@ -1097,12 +1492,11 @@ QString QtDocGenerator::expandClass(const QString &context, return typeEntry->qualifiedTargetLangName(); // fall back to the old heuristic if the type wasn't found. QString result = name; - const auto rawlinklist = QStringView{name}.split(QLatin1Char('.')); - QStringList splittedContext = context.split(QLatin1Char('.')); + const auto rawlinklist = QStringView{name}.split(u'.'); + QStringList splittedContext = context.split(u'.'); if (rawlinklist.size() == 1 || rawlinklist.constFirst() == splittedContext.constLast()) { splittedContext.removeLast(); - result.prepend(QLatin1Char('~') + splittedContext.join(QLatin1Char('.')) - + QLatin1Char('.')); + result.prepend(u'~' + splittedContext.join(u'.') + u'.'); } return result; } @@ -1110,10 +1504,10 @@ QString QtDocGenerator::expandClass(const QString &context, QString QtDocGenerator::resolveContextForMethod(const QString &context, const QString &methodName) const { - const auto currentClass = QStringView{context}.split(QLatin1Char('.')).constLast(); + 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; @@ -1128,8 +1522,8 @@ QString QtDocGenerator::resolveContextForMethod(const QString &context, funcList.append(func); } - const AbstractMetaClass *implementingClass = nullptr; - for (const auto &func : qAsConst(funcList)) { + AbstractMetaClassCPtr implementingClass; + for (const auto &func : std::as_const(funcList)) { implementingClass = func->implementingClass(); if (implementingClass->name() == currentClass) break; @@ -1139,10 +1533,59 @@ QString QtDocGenerator::resolveContextForMethod(const QString &context, return implementingClass->typeEntry()->qualifiedTargetLangName(); } - return QLatin1Char('~') + context; + return u'~' + context; } const QLoggingCategory &QtDocGenerator::loggingCategory() const { return lcShibokenDoc(); } + +static bool isRelativeHtmlFile(const QString &linkRef) +{ + return !linkRef.startsWith(u"http") + && (linkRef.endsWith(u".html") || linkRef.contains(u".html#")); +} + +// Resolve relative, local .html documents links to doc.qt.io as they +// otherwise will not work and neither be found in the HTML tree. +QtXmlToSphinxLink QtDocGenerator::resolveLink(const QtXmlToSphinxLink &link) const +{ + if (link.type != QtXmlToSphinxLink::Reference || !isRelativeHtmlFile(link.linkRef)) + return link; + static const QString prefix = "https://doc.qt.io/qt-"_L1 + + QString::number(QT_VERSION_MAJOR) + u'/'; + QtXmlToSphinxLink resolved = link; + resolved.type = QtXmlToSphinxLink::External; + resolved.linkRef = prefix + link.linkRef; + if (resolved.linkText.isEmpty()) { + resolved.linkText = link.linkRef; + const qsizetype anchor = resolved.linkText.lastIndexOf(u'#'); + if (anchor != -1) + resolved.linkText.truncate(anchor); + } + return resolved; +} + +QtXmlToSphinxDocGeneratorInterface::Image + QtDocGenerator::resolveImage(const QString &href, const QString &context) const +{ + QString relativeSourceDir = href; + const QString source = m_options.parameters.docDataDir + u'/' + relativeSourceDir; + if (!QFileInfo::exists(source)) + throw Exception(msgCannotFindImage(href, context,source)); + + // Determine target directory from context, "Pyside2.QtGui.QPainter" ->"Pyside2/QtGui". + // FIXME: Not perfect yet, should have knowledge about namespaces (DataVis3D) or + // nested classes "Pyside2.QtGui.QTouchEvent.QTouchPoint". + QString relativeTargetDir = context; + const auto lastDot = relativeTargetDir.lastIndexOf(u'.'); + if (lastDot != -1) + relativeTargetDir.truncate(lastDot); + relativeTargetDir.replace(u'.', u'/'); + if (!relativeTargetDir.isEmpty()) + relativeTargetDir += u'/'; + relativeTargetDir += href; + + return {relativeSourceDir, relativeTargetDir}; +} diff --git a/sources/shiboken6/generator/qtdoc/qtdocgenerator.h b/sources/shiboken6/generator/qtdoc/qtdocgenerator.h index 2b18d1edd..56e15e2a1 100644 --- a/sources/shiboken6/generator/qtdoc/qtdocgenerator.h +++ b/sources/shiboken6/generator/qtdoc/qtdocgenerator.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef DOCGENERATOR_H #define DOCGENERATOR_H @@ -34,11 +9,15 @@ #include "generator.h" #include "documentation.h" +#include <optionsparser.h> #include "typesystem_enums.h" -#include "typesystem_typedefs.h" +#include "modifications_typedefs.h" #include "qtxmltosphinxinterface.h" class DocParser; +struct DocGeneratorOptions; +struct GeneratorDocumentation; +struct DocPackage; /** * The DocGenerator generates documentation from library being binded. @@ -46,6 +25,8 @@ class DocParser; class QtDocGenerator : public Generator, public QtXmlToSphinxDocGeneratorInterface { public: + Q_DISABLE_COPY_MOVE(QtDocGenerator) + QtDocGenerator(); ~QtDocGenerator(); @@ -56,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; @@ -66,64 +47,84 @@ public: QString resolveContextForMethod(const QString &context, const QString &methodName) const override; const QLoggingCategory &loggingCategory() const override; + QtXmlToSphinxLink resolveLink(const QtXmlToSphinxLink &) const override; + Image resolveImage(const QString &href, const QString &context) const override; + + static QString getFuncName(const AbstractMetaFunctionCPtr &cppFunc); + static QString formatArgs(const AbstractMetaFunctionCPtr &func); protected: - bool shouldGenerate(const AbstractMetaClass *) const override; - QString fileNameSuffix() const override; + bool shouldGenerate(const TypeEntryCPtr &) const override; + static QString fileNameSuffix(); QString fileNameForContext(const GeneratorContext &context) const override; void generateClass(TextStream &ts, const GeneratorContext &classContext) override; bool finishGeneration() override; private: - void writeEnums(TextStream& s, const 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 writeFunctionList(TextStream& s, const AbstractMetaClass* cppClass); - static void writeFunctionBlock(TextStream& s, const QString& title, - QStringList& functions); - void writeParameterType(TextStream &s, const AbstractMetaClass *cppClass, + static void writeFunctionToc(TextStream &s, const QString &title, + const AbstractMetaFunctionCList &functions); + static void writePropertyToc(TextStream &s, + const GeneratorDocumentation &doc); + void writeProperties(TextStream &s, + const GeneratorDocumentation &doc, + const AbstractMetaClassCPtr &cppClass) const; + void writeParameterType(TextStream &s, const AbstractMetaClassCPtr &cppClass, const AbstractMetaArgument &arg) const; - - void writeConstructors(TextStream &s, const AbstractMetaClass *cppClass) 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; - - bool writeInjectDocumentation(TextStream& s, TypeSystem::DocModificationMode mode, - const AbstractMetaClass* cppClass, - const AbstractMetaFunctionCPtr &func); + const QString &scope = {}) const; + + bool writeInjectDocumentation(TextStream &s, TypeSystem::DocModificationMode mode, + const AbstractMetaClassCPtr &cppClass) const; + bool writeInjectDocumentation(TextStream &s, TypeSystem::DocModificationMode mode, + const DocModificationList &modifications, + const AbstractMetaFunctionCPtr &func, + const QString &scope = {}) const; + bool writeDocModifications(TextStream &s, const DocModificationList &mods, + TypeSystem::DocModificationMode mode, + const QString &scope = {}) const; static void writeDocSnips(TextStream &s, const CodeSnipList &codeSnips, TypeSystem::CodeSnipPosition position, TypeSystem::Language language); void writeModuleDocumentation(); + void writeGlobals(const QString &package, const QString &fileName, + const DocPackage &docPackage); void writeAdditionalDocumentation() const; + bool writeInheritanceFile(); - static QString parseArgDocStyle(const AbstractMetaClass *cppClass, - const AbstractMetaFunctionCPtr &func); - QString translateToPythonType(const AbstractMetaType &type, const AbstractMetaClass *cppClass) const; + QString translateToPythonType(const AbstractMetaType &type, + const AbstractMetaClassCPtr &cppClass, + bool createRef = true) const; bool convertToRst(const QString &sourceFileName, const QString &targetFileName, const QString &context = QString(), QString *errorMessage = nullptr) const; - QString m_extraSectionDir; + static GeneratorDocumentation generatorDocumentation(const AbstractMetaClassCPtr &cppClass); + QStringList m_functionList; - QMap<QString, QStringList> m_packages; + QMap<QString, DocPackage> m_packages; QScopedPointer<DocParser> m_docParser; - QtXmlToSphinxParameters m_parameters; - QString m_additionalDocumentationList; + static DocGeneratorOptions m_options; }; #endif // DOCGENERATOR_H diff --git a/sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp b/sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp index 4a362ac54..b8fec836c 100644 --- a/sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp +++ b/sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "qtxmltosphinx.h" #include "exception.h" @@ -32,16 +7,17 @@ #include <codesniphelpers.h> #include "rstformat.h" +#include "qtcompat.h" + #include <QtCore/QDebug> #include <QtCore/QDir> #include <QtCore/QFileInfo> +#include <QtCore/QHash> #include <QtCore/QLoggingCategory> #include <QtCore/QRegularExpression> #include <QtCore/QXmlStreamReader> -static inline QString nameAttribute() { return QStringLiteral("name"); } -static inline QString titleAttribute() { return QStringLiteral("title"); } -static inline QString fullTitleAttribute() { return QStringLiteral("fulltitle"); } +using namespace Qt::StringLiterals; QString msgTagWarning(const QXmlStreamReader &reader, const QString &context, const QString &tag, const QString &message) @@ -59,16 +35,23 @@ QString msgTagWarning(const QXmlStreamReader &reader, const QString &context, return result; } -QString msgFallbackWarning(const QXmlStreamReader &reader, const QString &context, - const QString &tag, const QString &location, - const QString &identifier, const QString &fallback) +QString msgFallbackWarning(const QString &location, const QString &identifier, + const QString &fallback) { - QString message = QLatin1String("Falling back to \"") - + QDir::toNativeSeparators(fallback) + QLatin1String("\" for \"") - + location + QLatin1Char('"'); + QString message = u"Falling back to \""_s + + QDir::toNativeSeparators(fallback) + u"\" for \""_s + + location + u'"'; if (!identifier.isEmpty()) - message += QLatin1String(" [") + identifier + QLatin1Char(']'); - return msgTagWarning(reader, context, tag, message); + message += u" ["_s + identifier + u']'; + return message; +} + +QString msgSnippetsResolveError(const QString &path, const QStringList &locations) +{ + QString result; + QTextStream(&result) << "Could not resolve \"" << path << R"(" in ")" + << locations.join(uR"(", ")"_s); + return result; } static bool isHttpLink(const QString &ref) @@ -76,66 +59,106 @@ static bool isHttpLink(const QString &ref) return ref.startsWith(u"http://") || ref.startsWith(u"https://"); } -struct QtXmlToSphinx::LinkContext +static QString trimRight(QString s) { - enum Type - { - Method = 0x1, Function = 0x2, - FunctionMask = Method | Function, - Class = 0x4, Attribute = 0x8, Module = 0x10, - Reference = 0x20, External= 0x40 + while (!s.isEmpty() && s.crbegin()->isSpace()) + s.chop(1); + return s; +} + +static QString trimLeadingNewlines(QString s) +{ + while (!s.isEmpty() && s.at(0) == u'\n') + s.remove(0, 1); + return s; +} + +QDebug operator<<(QDebug d, const QtXmlToSphinxLink &l) +{ + static const QHash<QtXmlToSphinxLink::Type, const char *> typeName = { + {QtXmlToSphinxLink::Method, "Method"}, + {QtXmlToSphinxLink::Function, "Function"}, + {QtXmlToSphinxLink::Class, "Class"}, + {QtXmlToSphinxLink::Attribute, "Attribute"}, + {QtXmlToSphinxLink::Module, "Module"}, + {QtXmlToSphinxLink::Reference, "Reference"}, + {QtXmlToSphinxLink::External, "External"}, }; - enum Flags { InsideBold = 0x1, InsideItalic = 0x2 }; + QDebugStateSaver saver(d); + d.noquote(); + d.nospace(); + d << "QtXmlToSphinxLinkContext(" << typeName.value(l.type, "") << ", ref=\"" + << l.linkRef << '"'; + if (!l.linkText.isEmpty()) + d << ", text=\"" << l.linkText << '"'; + d << ')'; + return d; +} - explicit LinkContext(const QString &ref) : linkRef(ref) {} +QDebug operator<<(QDebug debug, const QtXmlToSphinx::TableCell &c) +{ + QDebugStateSaver saver(debug); + debug.noquote(); + debug.nospace(); + debug << "Cell(\"" << c.data << '"'; + if (c.colSpan != 0) + debug << ", colSpan=" << c.colSpan; + if (c.rowSpan != 0) + debug << ", rowSpan=" << c.rowSpan; + debug << ')'; + return debug; +} - QString linkRef; - QString linkText; - Type type = Reference; - int flags = 0; -}; +QDebug operator<<(QDebug debug, const QtXmlToSphinx::Table &t) +{ + QDebugStateSaver saver(debug); + debug.noquote(); + debug.nospace(); + t.formatDebug(debug); + return debug; +} -static const char *linkKeyWord(QtXmlToSphinx::LinkContext::Type type) +static const char *linkKeyWord(QtXmlToSphinxLink::Type type) { switch (type) { - case QtXmlToSphinx::LinkContext::Method: + case QtXmlToSphinxLink::Method: return ":meth:"; - case QtXmlToSphinx::LinkContext::Function: + case QtXmlToSphinxLink::Function: return ":func:"; - case QtXmlToSphinx::LinkContext::Class: + case QtXmlToSphinxLink::Class: return ":class:"; - case QtXmlToSphinx::LinkContext::Attribute: + case QtXmlToSphinxLink::Attribute: return ":attr:"; - case QtXmlToSphinx::LinkContext::Module: + case QtXmlToSphinxLink::Module: return ":mod:"; - case QtXmlToSphinx::LinkContext::Reference: + case QtXmlToSphinxLink::Reference: return ":ref:"; - case QtXmlToSphinx::LinkContext::External: + case QtXmlToSphinxLink::External: break; - case QtXmlToSphinx::LinkContext::FunctionMask: + case QtXmlToSphinxLink::FunctionMask: break; } return ""; } -TextStream &operator<<(TextStream &str, const QtXmlToSphinx::LinkContext &linkContext) +TextStream &operator<<(TextStream &str, const QtXmlToSphinxLink &linkContext) { // Temporarily turn off bold/italic since links do not work within - if (linkContext.flags & QtXmlToSphinx::LinkContext::InsideBold) + if (linkContext.flags & QtXmlToSphinxLink::InsideBold) str << "**"; - else if (linkContext.flags & QtXmlToSphinx::LinkContext::InsideItalic) + else if (linkContext.flags & QtXmlToSphinxLink::InsideItalic) str << '*'; str << ' ' << linkKeyWord(linkContext.type) << '`'; - const bool isExternal = linkContext.type == QtXmlToSphinx::LinkContext::External; + const bool isExternal = linkContext.type == QtXmlToSphinxLink::External; if (!linkContext.linkText.isEmpty()) { writeEscapedRstText(str, linkContext.linkText); - if (isExternal && !linkContext.linkText.endsWith(QLatin1Char(' '))) + if (isExternal && !linkContext.linkText.endsWith(u' ')) str << ' '; str << '<'; } // Convert page titles to RST labels - str << (linkContext.type == QtXmlToSphinx::LinkContext::Reference + str << (linkContext.type == QtXmlToSphinxLink::Reference ? toRstLabel(linkContext.linkRef) : linkContext.linkRef); if (!linkContext.linkText.isEmpty()) str << '>'; @@ -143,9 +166,9 @@ TextStream &operator<<(TextStream &str, const QtXmlToSphinx::LinkContext &linkCo if (isExternal) str << '_'; str << ' '; - if (linkContext.flags & QtXmlToSphinx::LinkContext::InsideBold) + if (linkContext.flags & QtXmlToSphinxLink::InsideBold) str << "**"; - else if (linkContext.flags & QtXmlToSphinx::LinkContext::InsideItalic) + else if (linkContext.flags & QtXmlToSphinxLink::InsideItalic) str << '*'; return str; } @@ -244,9 +267,8 @@ QtXmlToSphinx::QtXmlToSphinx(const QtXmlToSphinxDocGeneratorInterface *docGenera const QtXmlToSphinxParameters ¶meters, const QString& doc, const QString& context) : m_output(static_cast<QString *>(nullptr)), - m_tableHasHeader(false), m_context(context), - m_generator(docGenerator), m_parameters(parameters), - m_insideBold(false), m_insideItalic(false) + m_context(context), + m_generator(docGenerator), m_parameters(parameters) { m_result = transform(doc); } @@ -283,7 +305,7 @@ void QtXmlToSphinx::callHandler(WebXmlTag t, QXmlStreamReader &r) handleTableTag(r); break; case WebXmlTag::header: - handleRowTag(r); + handleHeaderTag(r); break; case WebXmlTag::row: handleRowTag(r); @@ -395,35 +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.setHeaderEnabled(m_tableHasHeader); - m_currentTable.normalize(); + table.normalize(); m_output << '\n'; - m_currentTable.format(m_output); + table.format(m_output); } void QtXmlToSphinx::pushOutputBuffer() { - auto *buffer = new QString(); - m_buffers << buffer; - m_output.setString(buffer); + m_buffers.append(std::make_shared<QString>()); + m_output.setString(m_buffers.top().get()); } QString QtXmlToSphinx::popOutputBuffer() { Q_ASSERT(!m_buffers.isEmpty()); - QString* str = m_buffers.pop(); - QString strcpy(*str); - delete str; - m_output.setString(m_buffers.isEmpty() ? 0 : m_buffers.top()); - return strcpy; + QString result(*m_buffers.top()); + m_buffers.pop(); + m_output.setString(m_buffers.isEmpty() ? nullptr : m_buffers.top().get()); + return result; +} + +constexpr auto autoTranslatedPlaceholder = "AUTO_GENERATED\n"_L1; +constexpr auto autoTranslatedNote = +R"(.. warning:: + This section contains snippets that were automatically + translated from C++ to Python and may contain errors. + +)"_L1; + +void QtXmlToSphinx::setAutoTranslatedNote(QString *str) const +{ + if (m_containsAutoTranslations) + str->replace(autoTranslatedPlaceholder, autoTranslatedNote); + else + str->remove(autoTranslatedPlaceholder); } QString QtXmlToSphinx::transform(const QString& doc) { Q_ASSERT(m_buffers.isEmpty()); - Indentation indentation(m_output); if (doc.trimmed().isEmpty()) return doc; @@ -431,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()) { @@ -462,7 +501,7 @@ QString QtXmlToSphinx::transform(const QString& doc) if (!m_inlineImages.isEmpty()) { // Write out inline image definitions stored in handleInlineImageTag(). m_output << '\n' << disableIndent; - for (const InlineImage &img : qAsConst(m_inlineImages)) + for (const InlineImage &img : std::as_const(m_inlineImages)) m_output << ".. |" << img.tag << "| image:: " << img.href << '\n'; m_output << '\n' << enableIndent; m_inlineImages.clear(); @@ -471,13 +510,14 @@ QString QtXmlToSphinx::transform(const QString& doc) m_output.flush(); QString retval = popOutputBuffer(); Q_ASSERT(m_buffers.isEmpty()); + setAutoTranslatedNote(&retval); return retval; } static QString resolveFile(const QStringList &locations, const QString &path) { for (QString location : locations) { - location.append(QLatin1Char('/')); + location.append(u'/'); location.append(path); if (QFileInfo::exists(location)) return location; @@ -485,25 +525,170 @@ static QString resolveFile(const QStringList &locations, const QString &path) return QString(); } -QString QtXmlToSphinx::readFromLocations(const QStringList &locations, const QString &path, - const QString &identifier, QString *errorMessage) +enum class SnippetType { + Other, // .qdoc, .qml,... + CppSource, CppHeader // Potentially converted to Python +}; + +SnippetType snippetType(const QString &path) +{ + if (path.endsWith(u".cpp")) + return SnippetType::CppSource; + if (path.endsWith(u".h")) + return SnippetType::CppHeader; + return SnippetType::Other; +} + +// Return the name of a .cpp/.h snippet converted to Python by snippets-translate +static QString pySnippetName(const QString &path, SnippetType type) +{ + switch (type) { + case SnippetType::CppSource: + return path.left(path.size() - 3) + u"py"_s; + break; + case SnippetType::CppHeader: + return path + u".py"_s; + break; + default: + break; + } + return {}; +} + +QtXmlToSphinx::Snippet QtXmlToSphinx::readSnippetFromLocations(const QString &path, + const QString &identifier, + const QString &fallbackPath, + QString *errorMessage) +{ + // For anything else but C++ header/sources (no conversion to Python), + // use existing fallback paths first. + const auto type = snippetType(path); + if (type == SnippetType::Other && !fallbackPath.isEmpty()) { + const QString code = readFromLocation(fallbackPath, identifier, errorMessage); + return {code, code.isNull() ? Snippet::Error : Snippet::Fallback}; + } + + // For C++ header/sources, try snippets converted to Python first. QString resolvedPath; - if (path.endsWith(QLatin1String(".cpp"))) { - const QString pySnippet = path.left(path.size() - 3) + QLatin1String("py"); - resolvedPath = resolveFile(locations, pySnippet); + const auto &locations = m_parameters.codeSnippetDirs; + + if (type != SnippetType::Other) { + if (!fallbackPath.isEmpty() && !m_parameters.codeSnippetRewriteOld.isEmpty()) { + // Try looking up Python converted snippets by rewriting snippets paths + QString rewrittenPath = pySnippetName(fallbackPath, type); + if (!rewrittenPath.isEmpty()) { + rewrittenPath.replace(m_parameters.codeSnippetRewriteOld, + m_parameters.codeSnippetRewriteNew); + const QString code = readFromLocation(rewrittenPath, identifier, errorMessage); + m_containsAutoTranslations = true; + return {code, code.isNull() ? Snippet::Error : Snippet::Converted}; + } + } + + resolvedPath = resolveFile(locations, pySnippetName(path, type)); + if (!resolvedPath.isEmpty()) { + const QString code = readFromLocation(resolvedPath, identifier, errorMessage); + return {code, code.isNull() ? Snippet::Error : Snippet::Converted}; + } + } + + resolvedPath = resolveFile(locations, path); + if (!resolvedPath.isEmpty()) { + const QString code = readFromLocation(resolvedPath, identifier, errorMessage); + return {code, code.isNull() ? Snippet::Error : Snippet::Resolved}; + } + + if (!fallbackPath.isEmpty()) { + *errorMessage = msgFallbackWarning(path, identifier, fallbackPath); + const QString code = readFromLocation(fallbackPath, identifier, errorMessage); + return {code, code.isNull() ? Snippet::Error : Snippet::Fallback}; + } + + *errorMessage = msgSnippetsResolveError(path, locations); + return {{}, Snippet::Error}; +} + +// Helpers for extracting qdoc snippets "#/// [id]" +static QString fileNameOfDevice(const QIODevice *inputFile) +{ + const auto *file = qobject_cast<const QFile *>(inputFile); + return file ? QDir::toNativeSeparators(file->fileName()) : u"<stdin>"_s; +} + +static QString msgSnippetNotFound(const QIODevice &inputFile, + const QString &identifier) +{ + return u"Code snippet file found ("_s + fileNameOfDevice(&inputFile) + + u"), but snippet ["_s + identifier + u"] not found."_s; +} + +static QString msgEmptySnippet(const QIODevice &inputFile, int lineNo, + const QString &identifier) +{ + return u"Empty code snippet ["_s + identifier + u"] at "_s + + fileNameOfDevice(&inputFile) + u':' + QString::number(lineNo); +} + +// Pattern to match qdoc snippet IDs with "#/// [id]" comments and helper to find ID +static const QRegularExpression &snippetIdPattern() +{ + static const QRegularExpression result(uR"RX((//|#) *! *\[([^]]+)\])RX"_s); + Q_ASSERT(result.isValid()); + return result; +} + +static bool matchesSnippetId(QRegularExpressionMatchIterator it, + const QString &identifier) +{ + while (it.hasNext()) { + if (it.next().captured(2) == identifier) + return true; + } + return false; +} + +QString QtXmlToSphinx::readSnippet(QIODevice &inputFile, const QString &identifier, + QString *errorMessage) +{ + const QByteArray identifierBA = identifier.toUtf8(); + // Lambda that matches the snippet id + const auto snippetIdPred = [&identifierBA, &identifier](const QByteArray &lineBA) + { + const bool isComment = lineBA.contains('/') || lineBA.contains('#'); + if (!isComment || !lineBA.contains(identifierBA)) + return false; + const QString line = QString::fromUtf8(lineBA); + return matchesSnippetId(snippetIdPattern().globalMatch(line), identifier); + }; + + // Find beginning, skip over + int lineNo = 1; + for (; !inputFile.atEnd() && !snippetIdPred(inputFile.readLine()); + ++lineNo) { } - if (resolvedPath.isEmpty()) - resolvedPath = resolveFile(locations, path); - if (resolvedPath.isEmpty()) { - QTextStream(errorMessage) << "Could not resolve \"" << path << "\" in \"" - << locations.join(QLatin1String("\", \"")); - return QString(); // null + + 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; + } } - qCDebug(m_generator->loggingCategory()).noquote().nospace() - << "snippet file " << path - << " [" << identifier << ']' << " resolved to " << resolvedPath; - return readFromLocation(resolvedPath, identifier, errorMessage); + + if (code.isEmpty()) + *errorMessage = msgEmptySnippet(inputFile, lineNo, identifier); + + return code; } QString QtXmlToSphinx::readFromLocation(const QString &location, const QString &identifier, @@ -515,44 +700,18 @@ 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 = QLatin1String(""); // non-null + QString code = u""_s; // non-null if (identifier.isEmpty()) { while (!inputFile.atEnd()) code += QString::fromUtf8(inputFile.readLine()); return CodeSnipHelpers::fixSpaces(code); } - const QRegularExpression searchString(QLatin1String("//!\\s*\\[") - + identifier + QLatin1String("\\]")); - Q_ASSERT(searchString.isValid()); - static const QRegularExpression codeSnippetCode(QLatin1String("//!\\s*\\[[\\w\\d\\s]+\\]")); - Q_ASSERT(codeSnippetCode.isValid()); - - bool getCode = false; - - while (!inputFile.atEnd()) { - QString line = QString::fromUtf8(inputFile.readLine()); - if (getCode && !line.contains(searchString)) { - line.remove(codeSnippetCode); - 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) @@ -562,7 +721,7 @@ void QtXmlToSphinx::handleHeadingTag(QXmlStreamReader& reader) static char types[] = { '-', '^' }; QXmlStreamReader::TokenType token = reader.tokenType(); if (token == QXmlStreamReader::StartElement) { - uint typeIdx = reader.attributes().value(QLatin1String("level")).toUInt(); + uint typeIdx = reader.attributes().value(u"level"_s).toUInt(); if (typeIdx >= sizeof(types)) type = types[sizeof(types)-1]; else @@ -579,80 +738,133 @@ void QtXmlToSphinx::handleHeadingTag(QXmlStreamReader& reader) void QtXmlToSphinx::handleParaTag(QXmlStreamReader& reader) { - QXmlStreamReader::TokenType token = reader.tokenType(); - if (token == QXmlStreamReader::StartElement) { - pushOutputBuffer(); - } else if (token == QXmlStreamReader::EndElement) { - QString result = popOutputBuffer().simplified(); - if (result.startsWith(QLatin1String("**Warning:**"))) - result.replace(0, 12, QLatin1String(".. warning:: ")); - else if (result.startsWith(QLatin1String("**Note:**"))) - result.replace(0, 9, QLatin1String(".. note:: ")); + switch (reader.tokenType()) { + case QXmlStreamReader::StartElement: + handleParaTagStart(); + break; + case QXmlStreamReader::EndElement: + handleParaTagEnd(); + break; + case QXmlStreamReader::Characters: + handleParaTagText(reader); + break; + default: + break; + } +} - m_output << result << "\n\n"; - } else if (token == QXmlStreamReader::Characters) { - const auto text = reader.text(); - const QChar end = m_output.lastChar(); - if (!text.isEmpty() && m_output.indentation() == 0 && !end.isNull()) { - QChar start = text[0]; - if ((end == QLatin1Char('*') || end == QLatin1Char('`')) && start != QLatin1Char(' ') && !start.isPunct()) - m_output << '\\'; - } - m_output << escape(text); +void QtXmlToSphinx::handleParaTagStart() +{ + pushOutputBuffer(); +} + +void QtXmlToSphinx::handleParaTagText(QXmlStreamReader& reader) +{ + const auto text = reader.text(); + const QChar end = m_output.lastChar(); + if (!text.isEmpty() && m_output.indentation() == 0 && !end.isNull()) { + QChar start = text[0]; + if ((end == u'*' || end == u'`') && start != u' ' && !start.isPunct()) + m_output << '\\'; } + m_output << escape(text); +} + +void QtXmlToSphinx::handleParaTagEnd() +{ + QString result = popOutputBuffer().simplified(); + if (result.startsWith(u"**Warning:**")) + result.replace(0, 12, ".. warning:: "_L1); + else if (result.startsWith(u"**Note:**")) + result.replace(0, 9, ".. note:: "_L1); + m_output << result << "\n\n"; } void QtXmlToSphinx::handleItalicTag(QXmlStreamReader& reader) { - QXmlStreamReader::TokenType token = reader.tokenType(); - if (token == QXmlStreamReader::StartElement || token == QXmlStreamReader::EndElement) { - m_insideItalic = !m_insideItalic; - m_output << '*'; - } else if (token == QXmlStreamReader::Characters) { + switch (reader.tokenType()) { + case QXmlStreamReader::StartElement: + if (m_formattingDepth++ == 0) { + m_insideItalic = true; + m_output << rstItalic; + } + break; + case QXmlStreamReader::EndElement: + if (--m_formattingDepth == 0) { + m_insideItalic = false; + m_output << rstItalicOff; + } + break; + case QXmlStreamReader::Characters: m_output << escape(reader.text().trimmed()); + break; + default: + break; } } void QtXmlToSphinx::handleBoldTag(QXmlStreamReader& reader) { - QXmlStreamReader::TokenType token = reader.tokenType(); - if (token == QXmlStreamReader::StartElement || token == QXmlStreamReader::EndElement) { - m_insideBold = !m_insideBold; - m_output << "**"; - } else if (token == QXmlStreamReader::Characters) { + switch (reader.tokenType()) { + case QXmlStreamReader::StartElement: + if (m_formattingDepth++ == 0) { + m_insideBold = true; + m_output << rstBold; + } + break; + case QXmlStreamReader::EndElement: + if (--m_formattingDepth == 0) { + m_insideBold = false; + m_output << rstBoldOff; + } + break; + case QXmlStreamReader::Characters: m_output << escape(reader.text().trimmed()); + break; + default: + break; } } void QtXmlToSphinx::handleArgumentTag(QXmlStreamReader& reader) { - QXmlStreamReader::TokenType token = reader.tokenType(); - if (token == QXmlStreamReader::StartElement || token == QXmlStreamReader::EndElement) - m_output << "``"; - else if (token == QXmlStreamReader::Characters) + switch (reader.tokenType()) { + case QXmlStreamReader::StartElement: + if (m_formattingDepth++ == 0) + m_output << rstCode; + break; + case QXmlStreamReader::EndElement: + if (--m_formattingDepth == 0) + m_output << rstCodeOff; + break; + case QXmlStreamReader::Characters: m_output << reader.text().trimmed(); + break; + default: + break; + } } -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 == QLatin1String("property")) - return functionLinkType(); - if (type == QLatin1String("typedef")) - return classLinkType(); + if (type == u"property") + return functionLinkType; + if (type == u"typedef") + return classLinkType; return type.toString(); } static inline QString linkSourceAttribute(const QString &type) { - if (type == functionLinkType() || type == classLinkType()) - return QLatin1String("raw"); - return type == QLatin1String("enum") || type == QLatin1String("page") - ? type : QLatin1String("href"); + if (type == functionLinkType || type == classLinkType) + return u"raw"_s; + return type == u"enum" || type == u"page" + ? type : u"href"_s; } // "See also" links may appear as nested links: @@ -674,8 +886,8 @@ void QtXmlToSphinx::handleSeeAlsoTag(QXmlStreamReader& reader) if (!textR.isEmpty()) { const QString text = textR.toString(); if (m_seeAlsoContext.isNull()) { - const QString type = text.endsWith(QLatin1String("()")) - ? functionLinkType() : classLinkType(); + const QString type = text.endsWith(u"()") + ? functionLinkType : classLinkType; m_seeAlsoContext.reset(handleLinkStart(type, text)); } handleLinkText(m_seeAlsoContext.data(), text); @@ -694,12 +906,12 @@ 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) { - const auto lines = QStringView{snippet}.split(QLatin1Char('\n')); + const auto lines = QStringView{snippet}.split(u'\n'); for (const auto &line : lines) { if (!line.trimmed().isEmpty()) str << indent << line; @@ -725,55 +937,48 @@ void QtXmlToSphinx::handleSnippetTag(QXmlStreamReader& reader) { QXmlStreamReader::TokenType token = reader.tokenType(); if (token == QXmlStreamReader::StartElement) { - const bool consecutiveSnippet = m_lastTagName == QLatin1String("snippet") - || m_lastTagName == QLatin1String("dots") || m_lastTagName == QLatin1String("codeline"); + const bool consecutiveSnippet = m_lastTagName == u"snippet" + || m_lastTagName == u"dots" || m_lastTagName == u"codeline"; if (consecutiveSnippet) { m_output.flush(); - m_output.string()->chop(2); + m_output.string()->chop(1); // Strip newline from previous snippet } - QString location = reader.attributes().value(QLatin1String("location")).toString(); - QString identifier = reader.attributes().value(QLatin1String("identifier")).toString(); + QString location = reader.attributes().value(u"location"_s).toString(); + QString identifier = reader.attributes().value(u"identifier"_s).toString(); + QString fallbackPath; + if (reader.attributes().hasAttribute(fallbackPathAttribute)) + fallbackPath = reader.attributes().value(fallbackPathAttribute).toString(); QString errorMessage; - const QString pythonCode = - readFromLocations(m_parameters.codeSnippetDirs, location, identifier, &errorMessage); + + const Snippet snippet = readSnippetFromLocations(location, identifier, + fallbackPath, &errorMessage); if (!errorMessage.isEmpty()) warn(msgTagWarning(reader, m_context, m_lastTagName, errorMessage)); - // Fall back to C++ snippet when "path" attribute is present. - // Also read fallback snippet when comparison is desired. - QString fallbackCode; - if ((pythonCode.isEmpty() || m_parameters.snippetComparison) - && reader.attributes().hasAttribute(fallbackPathAttribute())) { - const QString fallback = reader.attributes().value(fallbackPathAttribute()).toString(); - if (QFileInfo::exists(fallback)) { - if (pythonCode.isEmpty()) - warn(msgFallbackWarning(reader, m_context, m_lastTagName, location, identifier, fallback)); - fallbackCode = readFromLocation(fallback, identifier, &errorMessage); - if (!errorMessage.isEmpty()) - warn(msgTagWarning(reader, m_context, m_lastTagName, errorMessage)); - } - } - if (!pythonCode.isEmpty() && !fallbackCode.isEmpty() && m_parameters.snippetComparison) - debug(msgSnippetComparison(location, identifier, pythonCode, fallbackCode)); + if (m_parameters.snippetComparison && snippet.result == Snippet::Converted + && !fallbackPath.isEmpty()) { + const QString fallbackCode = readFromLocation(fallbackPath, identifier, &errorMessage); + debug(msgSnippetComparison(location, identifier, snippet.code, fallbackCode)); + } if (!consecutiveSnippet) m_output << "::\n\n"; Indentation indentation(m_output); - const QString code = pythonCode.isEmpty() ? fallbackCode : pythonCode; - if (code.isEmpty()) + if (snippet.result == Snippet::Error) m_output << "<Code snippet \"" << location << ':' << identifier << "\" not found>\n"; else - m_output << code << ensureEndl; + m_output << snippet.code << ensureEndl; m_output << '\n'; } } + void QtXmlToSphinx::handleDotsTag(QXmlStreamReader& reader) { QXmlStreamReader::TokenType token = reader.tokenType(); if (token == QXmlStreamReader::StartElement) { - const bool consecutiveSnippet = m_lastTagName == QLatin1String("snippet") - || m_lastTagName == QLatin1String("dots") || m_lastTagName == QLatin1String("codeline"); + const bool consecutiveSnippet = m_lastTagName == u"snippet" + || m_lastTagName == u"dots" || m_lastTagName == u"codeline"; if (consecutiveSnippet) { m_output.flush(); m_output.string()->chop(2); @@ -781,7 +986,7 @@ void QtXmlToSphinx::handleDotsTag(QXmlStreamReader& reader) m_output << "::\n\n"; } pushOutputBuffer(); - int indent = reader.attributes().value(QLatin1String("indent")).toInt() + int indent = reader.attributes().value(u"indent"_s).toInt() + m_output.indentation() * m_output.tabWidth(); for (int i = 0; i < indent; ++i) m_output << ' '; @@ -796,12 +1001,15 @@ void QtXmlToSphinx::handleTableTag(QXmlStreamReader& reader) { QXmlStreamReader::TokenType token = reader.tokenType(); if (token == QXmlStreamReader::StartElement) { - m_currentTable.clear(); - m_tableHasHeader = false; + if (parentTag() == WebXmlTag::para) + handleParaTagEnd(); // End <para> to prevent the table from being rst-escaped + m_tables.push({}); } else if (token == QXmlStreamReader::EndElement) { // write the table on m_output formatCurrentTable(); - m_currentTable.clear(); + m_tables.pop(); + if (parentTag() == WebXmlTag::para) + handleParaTagStart(); } } @@ -811,11 +1019,11 @@ void QtXmlToSphinx::handleTermTag(QXmlStreamReader& reader) if (token == QXmlStreamReader::StartElement) { pushOutputBuffer(); } else if (token == QXmlStreamReader::Characters) { - m_output << reader.text().toString().replace(QLatin1String("::"), QLatin1String(".")); + m_output << reader.text().toString().replace(u"::"_s, u"."_s); } else if (token == QXmlStreamReader::EndElement) { TableCell cell; cell.data = popOutputBuffer().trimmed(); - m_currentTable.appendRow(TableRow(1, cell)); + m_tables.back().appendRow(TableRow(1, cell)); } } @@ -824,70 +1032,83 @@ 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(QLatin1String("colspan")).toShort(); - cell.rowSpan = reader.attributes().value(QLatin1String("rowspan")).toShort(); + 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; } } } -void QtXmlToSphinx::handleRowTag(QXmlStreamReader& reader) +void QtXmlToSphinx::handleHeaderTag(QXmlStreamReader &reader) { - QXmlStreamReader::TokenType token = reader.tokenType(); - if (token == QXmlStreamReader::StartElement) { - m_tableHasHeader = reader.name() == QLatin1String("header"); - m_currentTable.appendRow({}); + // <header> in WebXML is either a table header or a description of a + // C++ header with "name"/"href" attributes. + if (reader.tokenType() == QXmlStreamReader::StartElement + && !reader.attributes().hasAttribute(u"name"_s)) { + auto &table = m_tables.back(); + table.setHeaderEnabled(true); + table.appendRow({}); } } +void QtXmlToSphinx::handleRowTag(QXmlStreamReader& reader) +{ + if (reader.tokenType() == QXmlStreamReader::StartElement) + m_tables.back().appendRow({}); +} + enum ListType { BulletList, OrderedList, EnumeratedList }; static inline ListType webXmlListType(QStringView t) { - if (t == QLatin1String("enum")) + if (t == u"enum") return EnumeratedList; - if (t == QLatin1String("ordered")) + if (t == u"ordered") return OrderedList; return BulletList; } 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) { - listType = webXmlListType(reader.attributes().value(QLatin1String("type"))); + m_tables.push({}); + auto &table = m_tables.back(); + listType = webXmlListType(reader.attributes().value(u"type"_s)); if (listType == EnumeratedList) { - m_currentTable.appendRow(TableRow{TableCell(QLatin1String("Constant")), - TableCell(QLatin1String("Description"))}); - m_tableHasHeader = 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()) { - const auto itemLines = QStringView{cell.data}.split(QLatin1Char('\n')); + for (const TableCell &cell : table.constFirst()) { + const auto itemLines = QStringView{cell.data}.split(u'\n'); m_output << separator << itemLines.constFirst() << '\n'; - for (int i = 1, max = itemLines.count(); i < max; ++i) + for (qsizetype i = 1, max = itemLines.size(); i < max; ++i) m_output << indentLine << itemLines[i] << '\n'; } m_output << '\n'; @@ -898,7 +1119,7 @@ void QtXmlToSphinx::handleListTag(QXmlStreamReader& reader) break; } } - m_currentTable.clear(); + m_tables.pop(); } } @@ -908,7 +1129,7 @@ void QtXmlToSphinx::handleLinkTag(QXmlStreamReader& reader) case QXmlStreamReader::StartElement: { // <link> embedded in <see-also> means the characters of <see-also> are no link. m_seeAlsoContext.reset(); - const QString type = fixLinkType(reader.attributes().value(QLatin1String("type"))); + const QString type = fixLinkType(reader.attributes().value(u"type"_s)); const QString ref = reader.attributes().value(linkSourceAttribute(type)).toString(); m_linkContext.reset(handleLinkStart(type, ref)); } @@ -927,122 +1148,109 @@ void QtXmlToSphinx::handleLinkTag(QXmlStreamReader& reader) } } -QtXmlToSphinx::LinkContext *QtXmlToSphinx::handleLinkStart(const QString &type, QString ref) const +QtXmlToSphinxLink *QtXmlToSphinx::handleLinkStart(const QString &type, QString ref) const { - ref.replace(QLatin1String("::"), QLatin1String(".")); - ref.remove(QLatin1String("()")); - auto *result = new LinkContext(ref); + ref.replace(u"::"_s, u"."_s); + ref.remove(u"()"_s); + auto *result = new QtXmlToSphinxLink(ref); if (m_insideBold) - result->flags |= LinkContext::InsideBold; + result->flags |= QtXmlToSphinxLink::InsideBold; else if (m_insideItalic) - result->flags |= LinkContext::InsideItalic; + result->flags |= QtXmlToSphinxLink::InsideItalic; if (type == u"external" || isHttpLink(ref)) { - result->type = LinkContext::External; - } else if (type == functionLinkType() && !m_context.isEmpty()) { - result->type = LinkContext::Method; - const auto rawlinklist = QStringView{result->linkRef}.split(QLatin1Char('.')); + result->type = QtXmlToSphinxLink::External; + } else if (type == functionLinkType && !m_context.isEmpty()) { + result->type = QtXmlToSphinxLink::Method; + const auto rawlinklist = QStringView{result->linkRef}.split(u'.'); if (rawlinklist.size() == 1 || rawlinklist.constFirst() == m_context) { const auto lastRawLink = rawlinklist.constLast().toString(); QString context = m_generator->resolveContextForMethod(m_context, lastRawLink); if (!result->linkRef.startsWith(context)) - result->linkRef.prepend(context + QLatin1Char('.')); + result->linkRef.prepend(context + u'.'); } else { result->linkRef = m_generator->expandFunction(result->linkRef); } - } else if (type == functionLinkType() && m_context.isEmpty()) { - result->type = LinkContext::Function; - } else if (type == classLinkType()) { - result->type = LinkContext::Class; + } else if (type == functionLinkType && m_context.isEmpty()) { + result->type = QtXmlToSphinxLink::Function; + } else if (type == classLinkType) { + result->type = QtXmlToSphinxLink::Class; result->linkRef = m_generator->expandClass(m_context, result->linkRef); - } else if (type == QLatin1String("enum")) { - result->type = LinkContext::Attribute; - } else if (type == QLatin1String("page")) { + } else if (type == u"enum") { + result->type = QtXmlToSphinxLink::Attribute; + } else if (type == u"page") { // Module, external web page or reference if (result->linkRef == m_parameters.moduleName) - result->type = LinkContext::Module; + result->type = QtXmlToSphinxLink::Module; else - result->type = LinkContext::Reference; + result->type = QtXmlToSphinxLink::Reference; } else { - result->type = LinkContext::Reference; + result->type = QtXmlToSphinxLink::Reference; } return result; } // <link raw="Model/View Classes" href="model-view-programming.html#model-view-classes" // type="page" page="Model/View Programming">Model/View Classes</link> -// <link type="page" page="http://doc.qt.io/qt-5/class.html">QML types</link> +// <link type="page" page="https://doc.qt.io/qt-5/class.html">QML types</link> // <link raw="Qt Quick" href="qtquick-index.html" type="page" page="Qt Quick">Qt Quick</link> // <link raw="QObject" href="qobject.html" type="class">QObject</link> // <link raw="Qt::Window" href="qt.html#WindowType-enum" type="enum" enum="Qt::WindowType">Qt::Window</link> // <link raw="QNetworkSession::reject()" href="qnetworksession.html#reject" type="function">QNetworkSession::reject()</link> -static QString fixLinkText(const QtXmlToSphinx::LinkContext *linkContext, +static QString fixLinkText(const QtXmlToSphinxLink *linkContext, QString linktext) { - if (linkContext->type == QtXmlToSphinx::LinkContext::External - || linkContext->type == QtXmlToSphinx::LinkContext::Reference) { + if (linkContext->type == QtXmlToSphinxLink::External + || linkContext->type == QtXmlToSphinxLink::Reference) { return linktext; } // For the language reference documentation, strip the module name. // Clear the link text if that matches the function/class/enumeration name. - const int lastSep = linktext.lastIndexOf(QLatin1String("::")); + const int lastSep = linktext.lastIndexOf(u"::"); if (lastSep != -1) linktext.remove(0, lastSep + 2); else QtXmlToSphinx::stripPythonQualifiers(&linktext); if (linkContext->linkRef == linktext) - return QString(); - if ((linkContext->type & QtXmlToSphinx::LinkContext::FunctionMask) != 0 - && (linkContext->linkRef + QLatin1String("()")) == linktext) { - return QString(); + return {}; + if ((linkContext->type & QtXmlToSphinxLink::FunctionMask) != 0 + && (linkContext->linkRef + u"()"_s) == linktext) { + return {}; } return linktext; } -void QtXmlToSphinx::handleLinkText(LinkContext *linkContext, const QString &linktext) +void QtXmlToSphinx::handleLinkText(QtXmlToSphinxLink *linkContext, const QString &linktext) { linkContext->linkText = fixLinkText(linkContext, linktext); } -void QtXmlToSphinx::handleLinkEnd(LinkContext *linkContext) +void QtXmlToSphinx::handleLinkEnd(QtXmlToSphinxLink *linkContext) { - m_output << *linkContext; + m_output << m_generator->resolveLink(*linkContext); +} + +WebXmlTag QtXmlToSphinx::parentTag() const +{ + const auto index = m_tagStack.size() - 2; + return index >= 0 ? m_tagStack.at(index) : WebXmlTag::Unknown; } // Copy images that are placed in a subdirectory "images" under the webxml files // by qdoc to a matching subdirectory under the "rst/PySide6/<module>" directory -static bool copyImage(const QString &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 = QLatin1Char('/'); - 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(QLatin1Char('.')); - if (lastDot != -1) - relativeTargetDir.truncate(lastDot); - relativeTargetDir.replace(QLatin1Char('.'), 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) @@ -1051,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; @@ -1082,7 +1291,7 @@ void QtXmlToSphinx::handleImageTag(QXmlStreamReader& reader) { if (reader.tokenType() != QXmlStreamReader::StartElement) return; - const QString href = reader.attributes().value(QLatin1String("href")).toString(); + const QString href = reader.attributes().value(u"href"_s).toString(); if (copyImage(href)) m_output << ".. image:: " << href << "\n\n"; } @@ -1091,17 +1300,17 @@ void QtXmlToSphinx::handleInlineImageTag(QXmlStreamReader& reader) { if (reader.tokenType() != QXmlStreamReader::StartElement) return; - const QString href = reader.attributes().value(QLatin1String("href")).toString(); + const QString href = reader.attributes().value(u"href"_s).toString(); if (!copyImage(href)) return; // Handle inline images by substitution references. Insert a unique tag // enclosed by '|' and define it further down. Determine tag from the base //file name with number. QString tag = href; - int pos = tag.lastIndexOf(QLatin1Char('/')); + auto pos = tag.lastIndexOf(u'/'); if (pos != -1) tag.remove(0, pos + 1); - pos = tag.indexOf(QLatin1Char('.')); + pos = tag.indexOf(u'.'); if (pos != -1) tag.truncate(pos); tag += QString::number(m_inlineImages.size() + 1); @@ -1113,7 +1322,7 @@ void QtXmlToSphinx::handleRawTag(QXmlStreamReader& reader) { QXmlStreamReader::TokenType token = reader.tokenType(); if (token == QXmlStreamReader::StartElement) { - QString format = reader.attributes().value(QLatin1String("format")).toString(); + QString format = reader.attributes().value(u"format"_s).toString(); m_output << ".. raw:: " << format.toLower() << "\n\n"; } else if (token == QXmlStreamReader::Characters) { Indentation indent(m_output); @@ -1166,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); @@ -1183,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()); } @@ -1203,14 +1412,14 @@ void QtXmlToSphinx::handleAnchorTag(QXmlStreamReader& reader) QXmlStreamReader::TokenType token = reader.tokenType(); if (token == QXmlStreamReader::StartElement) { QString anchor; - if (reader.attributes().hasAttribute(QLatin1String("id"))) - anchor = reader.attributes().value(QLatin1String("id")).toString(); - else if (reader.attributes().hasAttribute(QLatin1String("name"))) - anchor = reader.attributes().value(QLatin1String("name")).toString(); + if (reader.attributes().hasAttribute(u"id"_s)) + anchor = reader.attributes().value(u"id"_s).toString(); + else if (reader.attributes().hasAttribute(u"name"_s)) + anchor = reader.attributes().value(u"name"_s).toString(); if (!anchor.isEmpty() && m_opened_anchor != anchor) { m_opened_anchor = anchor; if (!m_context.isEmpty()) - anchor.prepend(m_context + QLatin1Char('_')); + anchor.prepend(m_context + u'_'); m_output << rstLabel(anchor); } } else if (token == QXmlStreamReader::EndElement) { @@ -1229,7 +1438,7 @@ void QtXmlToSphinx::handleQuoteFileTag(QXmlStreamReader& reader) QXmlStreamReader::TokenType token = reader.tokenType(); if (token == QXmlStreamReader::Characters) { QString location = reader.text().toString(); - location.prepend(m_parameters.libSourceDir + QLatin1Char('/')); + location.prepend(m_parameters.libSourceDir + u'/'); QString errorMessage; QString code = readFromLocation(location, QString(), &errorMessage); if (!errorMessage.isEmpty()) @@ -1244,51 +1453,72 @@ void QtXmlToSphinx::handleQuoteFileTag(QXmlStreamReader& reader) } } +bool QtXmlToSphinx::Table::hasEmptyLeadingRow() const +{ + return !m_rows.isEmpty() && m_rows.constFirst().isEmpty(); +} + +bool QtXmlToSphinx::Table::hasEmptyTrailingRow() const +{ + return !m_rows.isEmpty() && m_rows.constLast().isEmpty(); +} + void QtXmlToSphinx::Table::normalize() { - if (m_normalized || isEmpty()) + if (m_normalized) + return; + + // Empty leading/trailing rows have been observed with nested tables + if (hasEmptyLeadingRow() || hasEmptyLeadingRow()) { + qWarning() << "QtXmlToSphinx: Table with leading/trailing empty columns found: " << *this; + while (hasEmptyTrailingRow()) + m_rows.pop_back(); + while (hasEmptyLeadingRow()) + m_rows.pop_front(); + } + + if (isEmpty()) return; //QDoc3 generates tables with wrong number of columns. We have to //check and if necessary, merge the last columns. - int maxCols = -1; - for (const auto &row : qAsConst(m_rows)) { - if (row.count() > maxCols) - maxCols = row.count(); + qsizetype maxCols = -1; + for (const auto &row : std::as_const(m_rows)) { + if (row.size() > maxCols) + maxCols = row.size(); } if (maxCols <= 0) return; // add col spans - for (int row = 0; row < m_rows.count(); ++row) { - for (int col = 0; col < m_rows.at(row).count(); ++col) { + for (qsizetype row = 0; row < m_rows.size(); ++row) { + for (qsizetype col = 0; col < m_rows.at(row).size(); ++col) { QtXmlToSphinx::TableCell& cell = m_rows[row][col]; bool mergeCols = (col >= maxCols); if (cell.colSpan > 0) { QtXmlToSphinx::TableCell newCell; newCell.colSpan = -1; - for (int i = 0, max = cell.colSpan-1; i < max; ++i) { + for (int i = 0, max = cell.colSpan-1; i < max; ++i) m_rows[row].insert(col + 1, newCell); - } cell.colSpan = 0; col++; } else if (mergeCols) { - m_rows[row][maxCols - 1].data += QLatin1Char(' ') + cell.data; + m_rows[row][maxCols - 1].data += u' ' + cell.data; } } } // row spans - const int numCols = m_rows.constFirst().count(); - for (int col = 0; col < numCols; ++col) { - for (int row = 0; row < m_rows.count(); ++row) { - if (col < m_rows[row].count()) { + const qsizetype numCols = m_rows.constFirst().size(); + for (qsizetype col = 0; col < numCols; ++col) { + for (qsizetype row = 0; row < m_rows.size(); ++row) { + if (col < m_rows[row].size()) { QtXmlToSphinx::TableCell& cell = m_rows[row][col]; if (cell.rowSpan > 0) { QtXmlToSphinx::TableCell newCell; newCell.rowSpan = -1; - int targetRow = row + 1; - const int targetEndRow = - std::min(targetRow + cell.rowSpan - 1, int(m_rows.count())); + qsizetype targetRow = row + 1; + const qsizetype targetEndRow = + std::min(targetRow + cell.rowSpan - 1, m_rows.size()); cell.rowSpan = 0; for ( ; targetRow < targetEndRow; ++targetRow) m_rows[targetRow].insert(col, newCell); @@ -1308,16 +1538,17 @@ void QtXmlToSphinx::Table::format(TextStream& s) const Q_ASSERT(isNormalized()); // calc width and height of each column and row - const int headerColumnCount = m_rows.constFirst().count(); - QList<int> colWidths(headerColumnCount, 0); - QList<int> rowHeights(m_rows.count(), 0); - for (int i = 0, maxI = m_rows.count(); i < maxI; ++i) { + const qsizetype headerColumnCount = m_rows.constFirst().size(); + QList<qsizetype> colWidths(headerColumnCount, 0); + QList<qsizetype> rowHeights(m_rows.size(), 0); + for (qsizetype i = 0, maxI = m_rows.size(); i < maxI; ++i) { const QtXmlToSphinx::TableRow& row = m_rows.at(i); - for (int j = 0, maxJ = std::min(row.count(), colWidths.size()); j < maxJ; ++j) { - const auto rowLines = QStringView{row[j].data}.split(QLatin1Char('\n')); // cache this would be a good idea + for (qsizetype j = 0, maxJ = std::min(row.size(), colWidths.size()); j < maxJ; ++j) { + // cache this would be a good idea + const auto rowLines = QStringView{row[j].data}.split(u'\n'); for (const auto &str : rowLines) - colWidths[j] = std::max(colWidths[j], int(str.size())); - rowHeights[i] = std::max(rowHeights[i], int(rowLines.size())); + colWidths[j] = std::max(colWidths[j], str.size()); + rowHeights[i] = std::max(rowHeights[i], rowLines.size()); } } @@ -1325,44 +1556,41 @@ void QtXmlToSphinx::Table::format(TextStream& s) const return; // empty table (table with empty cells) // create a horizontal line to be used later. - QString horizontalLine = QLatin1String("+"); - for (int i = 0, max = colWidths.count(); i < max; ++i) { - horizontalLine += QString(colWidths.at(i), QLatin1Char('-')); - horizontalLine += QLatin1Char('+'); - } + QString horizontalLine = u"+"_s; + for (auto colWidth : colWidths) + horizontalLine += QString(colWidth, u'-') + u'+'; // write table rows - for (int i = 0, maxI = m_rows.count(); i < maxI; ++i) { // for each row + for (qsizetype i = 0, maxI = m_rows.size(); i < maxI; ++i) { // for each row const QtXmlToSphinx::TableRow& row = m_rows.at(i); // print line s << '+'; - for (int col = 0; col < headerColumnCount; ++col) { - char c; - if (col >= row.length() || row[col].rowSpan == -1) + for (qsizetype col = 0; col < headerColumnCount; ++col) { + 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'; // Print the table cells - for (int rowLine = 0; rowLine < rowHeights[i]; ++rowLine) { // for each line in a row - int j = 0; - for (int maxJ = std::min(int(row.count()), headerColumnCount); j < maxJ; ++j) { // for each column + for (qsizetype rowLine = 0; rowLine < rowHeights.at(i); ++rowLine) { // for each line in a row + qsizetype j = 0; + for (qsizetype maxJ = std::min(row.size(), headerColumnCount); j < maxJ; ++j) { // for each column const QtXmlToSphinx::TableCell& cell = row[j]; - const auto rowLines = QStringView{cell.data}.split(QLatin1Char('\n')); // FIXME: Cache this!!! + // FIXME: Cache this!!! + const auto rowLines = QStringView{cell.data}.split(u'\n'); if (!j || !cell.colSpan) s << '|'; else s << ' '; - const int width = colWidths.at(j); - if (rowLine < rowLines.count()) + const auto width = int(colWidths.at(j)); + if (rowLine < rowLines.size()) s << AlignedField(rowLines.at(rowLine), width); else s << Pad(' ', width); @@ -1375,9 +1603,31 @@ void QtXmlToSphinx::Table::format(TextStream& s) const s << horizontalLine << "\n\n"; } +void QtXmlToSphinx::Table::formatDebug(QDebug &debug) const +{ + const auto rowCount = m_rows.size(); + debug << "Table(" <<rowCount << " rows"; + if (m_hasHeader) + debug << ", [header]"; + if (m_normalized) + debug << ", [normalized]"; + for (qsizetype r = 0; r < rowCount; ++r) { + const auto &row = m_rows.at(r); + const auto &colCount = row.size(); + debug << ", row " << r << " [" << colCount << "]={"; + for (qsizetype c = 0; c < colCount; ++c) { + if (c > 0) + debug << ", "; + debug << row.at(c); + } + debug << '}'; + } + debug << ')'; +} + void QtXmlToSphinx::stripPythonQualifiers(QString *s) { - const int lastSep = s->lastIndexOf(QLatin1Char('.')); + const int lastSep = s->lastIndexOf(u'.'); if (lastSep != -1) s->remove(0, lastSep + 1); } diff --git a/sources/shiboken6/generator/qtdoc/qtxmltosphinx.h b/sources/shiboken6/generator/qtdoc/qtxmltosphinx.h index 5c3a90be5..398c5bc97 100644 --- a/sources/shiboken6/generator/qtdoc/qtxmltosphinx.h +++ b/sources/shiboken6/generator/qtdoc/qtxmltosphinx.h @@ -1,48 +1,25 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef QTXMLTOSPHINX_H #define QTXMLTOSPHINX_H #include <textstream.h> -#include <QtCore/QHash> #include <QtCore/QList> #include <QtCore/QScopedPointer> #include <QtCore/QStack> -#include <QtCore/QTextStream> + +#include <memory> QT_BEGIN_NAMESPACE +class QDebug; class QXmlStreamReader; QT_END_NAMESPACE class QtXmlToSphinxDocGeneratorInterface; struct QtXmlToSphinxParameters; +struct QtXmlToSphinxLink; enum class WebXmlTag; @@ -51,8 +28,6 @@ class QtXmlToSphinx public: Q_DISABLE_COPY_MOVE(QtXmlToSphinx) - struct LinkContext; - struct InlineImage { QString tag; @@ -66,7 +41,7 @@ public: QString data; TableCell(const QString& text = QString()) : data(text) {} - TableCell(const char* text) : data(QLatin1String(text)) {} + TableCell(const char* text) : data(QString::fromLatin1(text)) {} }; using TableRow = QList<TableCell>; @@ -95,20 +70,19 @@ 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(); } void format(TextStream& s) const; + void formatDebug(QDebug &debug) const; private: + bool hasEmptyLeadingRow() const; + bool hasEmptyTrailingRow() const; + QList<TableRow> m_rows; bool m_hasHeader = false; bool m_normalized = false; @@ -127,11 +101,20 @@ public: static void stripPythonQualifiers(QString *s); + // For testing + static QString readSnippet(QIODevice &inputFile, const QString &identifier, + QString *errorMessage); + private: + using StringSharedPtr = std::shared_ptr<QString>; + QString transform(const QString& doc); void handleHeadingTag(QXmlStreamReader& reader); void handleParaTag(QXmlStreamReader& reader); + void handleParaTagStart(); + void handleParaTagText(QXmlStreamReader &reader); + void handleParaTagEnd(); void handleItalicTag(QXmlStreamReader& reader); void handleBoldTag(QXmlStreamReader& reader); void handleArgumentTag(QXmlStreamReader& reader); @@ -149,6 +132,7 @@ private: // table tagsvoid QtXmlToSphinx::handleValueTag(QXmlStreamReader& reader) void handleTableTag(QXmlStreamReader& reader); + void handleHeaderTag(QXmlStreamReader& reader); void handleRowTag(QXmlStreamReader& reader); void handleItemTag(QXmlStreamReader& reader); void handleRawTag(QXmlStreamReader& reader); @@ -162,9 +146,10 @@ private: void handleAnchorTag(QXmlStreamReader& reader); void handleRstPassTroughTag(QXmlStreamReader& reader); - LinkContext *handleLinkStart(const QString &type, QString ref) const; - static void handleLinkText(LinkContext *linkContext, const QString &linktext) ; - void handleLinkEnd(LinkContext *linkContext); + QtXmlToSphinxLink *handleLinkStart(const QString &type, QString ref) const; + static void handleLinkText(QtXmlToSphinxLink *linkContext, const QString &linktext) ; + void handleLinkEnd(QtXmlToSphinxLink *linkContext); + WebXmlTag parentTag() const; void warn(const QString &message) const; void debug(const QString &message) const; @@ -173,23 +158,42 @@ private: TextStream m_output; QString m_result; - QStack<QString*> m_buffers; + QStack<StringSharedPtr> m_buffers; // Maintain address stability since it used in TextStream - Table m_currentTable; - QScopedPointer<LinkContext> m_linkContext; // for <link> - QScopedPointer<LinkContext> m_seeAlsoContext; // for <see-also>foo()</see-also> - bool m_tableHasHeader; + QStack<Table> m_tables; // Stack of tables, used for <table><list> with nested <item> + QScopedPointer<QtXmlToSphinxLink> m_linkContext; // for <link> + QScopedPointer<QtXmlToSphinxLink> m_seeAlsoContext; // for <see-also>foo()</see-also> QString m_context; const QtXmlToSphinxDocGeneratorInterface *m_generator; const QtXmlToSphinxParameters &m_parameters; - bool m_insideBold; - bool m_insideItalic; + int m_formattingDepth = 0; + bool m_insideBold = false; + bool m_insideItalic = false; QString m_lastTagName; QString m_opened_anchor; QList<InlineImage> m_inlineImages; - QString readFromLocations(const QStringList &locations, const QString &path, - const QString &identifier, QString *errorMessage); + bool m_containsAutoTranslations = false; + + struct Snippet + { + enum Result { + Converted, // C++ converted to Python + Resolved, // Otherwise resolved in snippet paths + Fallback, // Fallback from XML + Error + }; + + QString code; + Result result; + }; + + void setAutoTranslatedNote(QString *str) const; + + Snippet readSnippetFromLocations(const QString &path, + const QString &identifier, + const QString &fallbackPath, + QString *errorMessage); static QString readFromLocation(const QString &location, const QString &identifier, QString *errorMessage); void pushOutputBuffer(); @@ -205,4 +209,8 @@ inline TextStream& operator<<(TextStream& s, const QtXmlToSphinx& xmlToSphinx) return s << xmlToSphinx.result(); } +QDebug operator<<(QDebug d, const QtXmlToSphinxLink &l); +QDebug operator<<(QDebug debug, const QtXmlToSphinx::Table &t); +QDebug operator<<(QDebug debug, const QtXmlToSphinx::TableCell &c); + #endif // QTXMLTOSPHINX_H diff --git a/sources/shiboken6/generator/qtdoc/qtxmltosphinxinterface.h b/sources/shiboken6/generator/qtdoc/qtxmltosphinxinterface.h index c4bdedd21..d4a098a12 100644 --- a/sources/shiboken6/generator/qtdoc/qtxmltosphinxinterface.h +++ b/sources/shiboken6/generator/qtdoc/qtxmltosphinxinterface.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef QTXMLTOSPHINXINTERFACE_H #define QTXMLTOSPHINXINTERFACE_H @@ -40,9 +15,31 @@ struct QtXmlToSphinxParameters QString outputDirectory; QString libSourceDir; QStringList codeSnippetDirs; + QString codeSnippetRewriteOld; + QString codeSnippetRewriteNew; bool snippetComparison = false; }; +struct QtXmlToSphinxLink +{ + enum Type + { + Method = 0x1, Function = 0x2, + FunctionMask = Method | Function, + Class = 0x4, Attribute = 0x8, Module = 0x10, + Reference = 0x20, External= 0x40 + }; + + enum Flags { InsideBold = 0x1, InsideItalic = 0x2 }; + + explicit QtXmlToSphinxLink(const QString &ref) : linkRef(ref) {} + + QString linkRef; + QString linkText; + Type type = Reference; + int flags = 0; +}; + class QtXmlToSphinxDocGeneratorInterface { public: @@ -54,6 +51,17 @@ public: virtual const QLoggingCategory &loggingCategory() const = 0; + virtual QtXmlToSphinxLink resolveLink(const QtXmlToSphinxLink &) const = 0; + + // Resolve images paths relative to doc data directory/output directory. + struct Image + { + QString source; + QString target; + }; + + virtual Image resolveImage(const QString &href, const QString &context) const = 0; + virtual ~QtXmlToSphinxDocGeneratorInterface() = default; }; diff --git a/sources/shiboken6/generator/qtdoc/rstformat.h b/sources/shiboken6/generator/qtdoc/rstformat.h index d14ecc55d..8af7671fb 100644 --- a/sources/shiboken6/generator/qtdoc/rstformat.h +++ b/sources/shiboken6/generator/qtdoc/rstformat.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef RSTFORMAT_H #define RSTFORMAT_H @@ -55,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) { @@ -116,14 +69,14 @@ inline TextStream &operator<<(TextStream &str, const escape &e) // RST anchor string: Anything else but letters, numbers, '_' or '.' replaced by '-' inline bool isValidRstLabelChar(QChar c) { - return c.isLetterOrNumber() || c == QLatin1Char('_') || c == QLatin1Char('.'); + return c.isLetterOrNumber() || c == u'_' || c == u'.'; } inline QString toRstLabel(QString s) { for (int i = 0, size = s.size(); i < size; ++i) { if (!isValidRstLabelChar(s.at(i))) - s[i] = QLatin1Char('-'); + s[i] = u'-'; } return 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 182e3a112..97a38a08d 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp +++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp @@ -1,44 +1,27 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <memory> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "cppgenerator.h" +#include "configurablescope.h" +#include "generatorargument.h" +#include "generatorstrings.h" +#include "defaultvalue.h" +#include "generatorcontext.h" +#include "codesnip.h" +#include "customconversion.h" +#include "headergenerator.h" #include "apiextractorresult.h" #include "ctypenames.h" #include <exception.h> #include "pytypenames.h" #include "fileout.h" #include "overloaddata.h" +#include "pymethoddefentry.h" #include <abstractmetaenum.h> #include <abstractmetafield.h> #include <abstractmetafunction.h> #include <abstractmetalang.h> +#include <abstractmetalang_helpers.h> #include <messages.h> #include <modifications.h> #include <propertyspec.h> @@ -46,80 +29,115 @@ #include <sourcelocation.h> #include <textstream.h> #include <typedatabase.h> +#include <containertypeentry.h> +#include <enumtypeentry.h> +#include <flagstypeentry.h> +#include <functiontypeentry.h> +#include <namespacetypeentry.h> +#include <primitivetypeentry.h> +#include <smartpointertypeentry.h> +#include <typesystemtypeentry.h> +#include <valuetypeentry.h> #include <parser/enumvalue.h> +#include "qtcompat.h" + +#include <QtCore/QDebug> #include <QtCore/QDir> #include <QtCore/QMetaObject> +#include <QtCore/QMetaType> #include <QtCore/QRegularExpression> #include <QtCore/QTextStream> -#include <QtCore/QDebug> -#include <QMetaType> #include <algorithm> #include <cstring> +#include <memory> +#include <set> + +using namespace Qt::StringLiterals; -static const char CPP_ARG0[] = "cppArg0"; +static const char shibokenErrorsOccurred[] = "Shiboken::Errors::occurred() != nullptr"; -static inline QString reprFunction() { return QStringLiteral("__repr__"); } +static constexpr auto virtualMethodStaticReturnVar = "result"_L1; +static constexpr auto initFuncPrefix = "init_"_L1; -QString CppGenerator::m_currentErrorCode(QLatin1String("{}")); +static constexpr auto sbkObjectTypeF = "SbkObject_TypeF()"_L1; +static const char initInheritanceFunction[] = "initInheritance"; -static const char typeNameFunc[] = R"CPP( -template <class T> -static const char *typeNameOf(const T &t) +static QString mangleName(QString name) { - const char *typeName = typeid(t).name(); - auto size = std::strlen(typeName); -#if defined(Q_CC_MSVC) // MSVC: "class QPaintDevice * __ptr64" - if (auto lastStar = strchr(typeName, '*')) { - // MSVC: "class QPaintDevice * __ptr64" - while (*--lastStar == ' ') { - } - size = lastStar - typeName + 1; - } -#else // g++, Clang: "QPaintDevice *" -> "P12QPaintDevice" - if (size > 2 && typeName[0] == 'P' && std::isdigit(typeName[1])) { - ++typeName; - --size; - } -#endif - char *result = new char[size + 1]; - result[size] = '\0'; - memcpy(result, typeName, size); - return result; + if (name == u"None" || name == u"False" || name == u"True" || name == u"from") + name += u'_'; + return name; } -)CPP"; -// utility functions -inline AbstractMetaType getTypeWithoutContainer(const AbstractMetaType &arg) +struct sbkUnusedVariableCast { - if (arg.typeEntry()->isContainer()) { - // only support containers with 1 type - if (arg.instantiations().size() == 1) - return arg.instantiations().constFirst(); - } - return arg; -} + explicit sbkUnusedVariableCast(QAnyStringView name) : m_name(name) {} -// A helper for writing C++ return statements for either void ("return;") -// or some return value ("return value;") -class returnStatement + const QAnyStringView m_name; +}; + +TextStream &operator<<(TextStream &str, const sbkUnusedVariableCast &c) { -public: - explicit returnStatement(QString s) : m_returnValue(std::move(s)) {} + str << "SBK_UNUSED(" << c.m_name << ")\n"; + return str; +} - friend TextStream &operator<<(TextStream &s, const returnStatement &r); +struct pyTypeGetSlot +{ + explicit pyTypeGetSlot(QAnyStringView funcType, QAnyStringView typeObject, + QAnyStringView aSlot) : + m_funcType(funcType), m_typeObject(typeObject), m_slot(aSlot) {} -private: - const QString m_returnValue; + const QAnyStringView m_funcType; + const QAnyStringView m_typeObject; + const QAnyStringView m_slot; }; -TextStream &operator<<(TextStream &s, const returnStatement &r) +TextStream &operator<<(TextStream &str, const pyTypeGetSlot &p) +{ + str << "reinterpret_cast<" << p.m_funcType << ">(PepType_GetSlot(" + << p.m_typeObject << ", " << p.m_slot << "));\n"; + return str; +} + +TextStream &operator<<(TextStream &s, CppGenerator::ErrorReturn r) { s << "return"; - if (!r.m_returnValue.isEmpty()) - s << ' ' << r.m_returnValue; - s << ';'; + switch (r) { + case CppGenerator::ErrorReturn::Default: + s << " {}"; + break; + case CppGenerator::ErrorReturn::Zero: + s << " 0"; + break; + case CppGenerator::ErrorReturn::MinusOne: + s << " -1"; + break; + case CppGenerator::ErrorReturn::Void: + break; + } + s << ";\n"; + return s; +} + +static constexpr auto converterVar = "converter"_L1; + +struct registerConverterName +{ + explicit registerConverterName(QAnyStringView typeName, + QAnyStringView varName = converterVar) : + m_typeName(typeName), m_varName(varName) {} + + QAnyStringView m_typeName; + QAnyStringView m_varName; +}; + +TextStream &operator<<(TextStream &s, const registerConverterName &r) +{ + s << "Shiboken::Conversions::registerConverterName(" << r.m_varName + << ", \"" << r.m_typeName << "\");\n"; return s; } @@ -147,15 +165,15 @@ static bool contains(const ProtocolEntries &l, const QString &needle) const ProtocolEntries &mappingProtocols() { static const ProtocolEntries result = { - {QLatin1String("__mlen__"), - QLatin1String("PyObject *self"), - QLatin1String("Py_ssize_t")}, - {QLatin1String("__mgetitem__"), - QLatin1String("PyObject *self, PyObject *_key"), - QLatin1String("PyObject*")}, - {QLatin1String("__msetitem__"), - QLatin1String("PyObject *self, PyObject *_key, PyObject *_value"), - intT()}}; + {u"__mlen__"_s, + u"PyObject *self"_s, + u"Py_ssize_t"_s}, + {u"__mgetitem__"_s, + u"PyObject *self, PyObject *_key"_s, + u"PyObject*"_s}, + {u"__msetitem__"_s, + u"PyObject *self, PyObject *_key, PyObject *_value"_s, + intT}}; return result; } @@ -164,153 +182,71 @@ const ProtocolEntries &mappingProtocols() const ProtocolEntries &sequenceProtocols() { static const ProtocolEntries result = { - {QLatin1String("__len__"), - QLatin1String("PyObject *self"), - QLatin1String("Py_ssize_t")}, - {QLatin1String("__getitem__"), - QLatin1String("PyObject *self, Py_ssize_t _i"), - QLatin1String("PyObject*")}, - {QLatin1String("__setitem__"), - QLatin1String("PyObject *self, Py_ssize_t _i, PyObject *_value"), - intT()}, - {QLatin1String("__getslice__"), - QLatin1String("PyObject *self, Py_ssize_t _i1, Py_ssize_t _i2"), - QLatin1String("PyObject*")}, - {QLatin1String("__setslice__"), - QLatin1String("PyObject *self, Py_ssize_t _i1, Py_ssize_t _i2, PyObject *_value"), - intT()}, - {QLatin1String("__contains__"), - QLatin1String("PyObject *self, PyObject *_value"), - intT()}, - {QLatin1String("__concat__"), - QLatin1String("PyObject *self, PyObject *_other"), - QLatin1String("PyObject*")} + {u"__len__"_s, + u"PyObject *self"_s, + u"Py_ssize_t"_s}, + {u"__getitem__"_s, + u"PyObject *self, Py_ssize_t _i"_s, + u"PyObject*"_s}, + {u"__setitem__"_s, + u"PyObject *self, Py_ssize_t _i, PyObject *_value"_s, + intT}, + {u"__getslice__"_s, + u"PyObject *self, Py_ssize_t _i1, Py_ssize_t _i2"_s, + u"PyObject*"_s}, + {u"__setslice__"_s, + u"PyObject *self, Py_ssize_t _i1, Py_ssize_t _i2, PyObject *_value"_s, + intT}, + {u"__contains__"_s, + u"PyObject *self, PyObject *_value"_s, + intT}, + {u"__concat__"_s, + u"PyObject *self, PyObject *_other"_s, + u"PyObject*"_s} }; return result; } -CppGenerator::CppGenerator() = default; - -QString CppGenerator::fileNameSuffix() const -{ - return QLatin1String("_wrapper.cpp"); -} - -QString CppGenerator::fileNameForContext(const GeneratorContext &context) const -{ - const AbstractMetaClass *metaClass = context.metaClass(); - if (!context.forSmartPointer()) { - QString fileNameBase = metaClass->qualifiedCppName().toLower(); - fileNameBase.replace(QLatin1String("::"), QLatin1String("_")); - return fileNameBase + fileNameSuffix(); - } - const AbstractMetaType &smartPointerType = context.preciseType(); - QString fileNameBase = getFileNameBaseForSmartPointer(smartPointerType, metaClass); - return fileNameBase + fileNameSuffix(); -} - -static bool isInplaceAdd(const AbstractMetaFunctionCPtr &func) -{ - return func->name() == u"operator+="; -} - -static bool isIncrementOperator(const AbstractMetaFunctionCPtr &func) -{ - return func->functionType() == AbstractMetaFunction::IncrementOperator; -} - -static bool isDecrementOperator(const AbstractMetaFunctionCPtr &func) +// Return name of function to create PyObject wrapping a container +static QString opaqueContainerCreationFunc(const AbstractMetaType &type) { - 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()); + const auto containerTypeEntry = + std::static_pointer_cast<const ContainerTypeEntry>(type.typeEntry()); + const auto instantiationTypeEntry = + type.instantiations().constFirst().typeEntry(); + QString result = u"create"_s; + if (type.isConstant()) + result += u"Const"_s; + result += containerTypeEntry->opaqueContainerName(type.instantiationCppSignatures()); return result; } -AbstractMetaFunctionCPtr CppGenerator::boolCast(const AbstractMetaClass *metaClass) const +// Write declaration of the function to create PyObject wrapping a container +static void writeOpaqueContainerCreationFuncDecl(TextStream &s, const QString &name, + AbstractMetaType type) { - if (!useIsNullAsNbNonZero()) - return {}; - // TODO: This could be configurable someday - const auto func = metaClass->findFunction(QLatin1String("isNull")); - if (func.isNull() || func->isVoid() || !func->type().typeEntry()->isPrimitive() - || !func->isPublic()) { - return {}; - } - auto pte = static_cast<const PrimitiveTypeEntry *>(func->type().typeEntry()); - while (pte->referencedTypeEntry()) - pte = pte->referencedTypeEntry(); - return func->isConstant() && pte->name() == QLatin1String("bool") - && func->arguments().isEmpty() ? func : AbstractMetaFunctionCPtr{}; + type.setReferenceType(NoReference); + // Maintain const + s << "PyObject *" << name << '(' << type.cppSignature() << "*);\n"; } -std::optional<AbstractMetaType> - CppGenerator::findSmartPointerInstantiation(const TypeEntry *entry) const +CppGenerator::CppGenerator() = default; + +QString CppGenerator::fileNameForContext(const GeneratorContext &context) const { - for (const auto &i : instantiatedSmartPointers()) { - if (i.instantiations().at(0).typeEntry() == entry) - return i; - } - return {}; + return fileNameForContextHelper(context, u"_wrapper.cpp"_s); } void CppGenerator::clearTpFuncs() { + // Functions that should not be registered under a name in PyMethodDef, + // but under a special constant under slots. m_tpFuncs = { - {QLatin1String("__str__"), {}}, {QLatin1String("__str__"), {}}, - {reprFunction(), {}}, {QLatin1String("__iter__"), {}}, - {QLatin1String("__next__"), {}} + {u"__str__"_s, {}}, {u"__str__"_s, {}}, + {REPR_FUNCTION, {}}, {u"__iter__"_s, {}}, + {u"__next__"_s, {}} }; + m_nbFuncs = { {u"__abs__"_s, {}}, {u"__pow__"_s, {} }}; } // Prevent ELF symbol qt_version_tag from being generated into the source @@ -318,13 +254,13 @@ static const char includeQDebug[] = "#ifndef QT_NO_VERSION_TAGGING\n" "# define QT_NO_VERSION_TAGGING\n" "#endif\n" -"#include <QDebug>\n"; +"#include <QtCore/QDebug>\n"; -static QString chopType(QString s) +QString CppGenerator::chopType(QString s) { - if (s.endsWith(QLatin1String("_Type"))) + if (s.endsWith(u"_Type")) s.chop(5); - else if (s.endsWith(QLatin1String("_TypeF()"))) + else if (s.endsWith(u"_TypeF()")) s.chop(8); return s; } @@ -332,133 +268,314 @@ static QString chopType(QString s) static bool isStdSetterName(QString setterName, QString propertyName) { return setterName.size() == propertyName.size() + 3 - && setterName.startsWith(QLatin1String("set")) + && setterName.startsWith(u"set") && setterName.endsWith(QStringView{propertyName}.right(propertyName.size() - 1)) && setterName.at(3) == propertyName.at(0).toUpper(); } static QString buildPropertyString(const QPropertySpec &spec) { - QString text; - text += QLatin1Char('"'); - text += spec.name(); - text += QLatin1Char(':'); + QString text = u'"' + spec.name() + u':'; if (spec.read() != spec.name()) text += spec.read(); if (!spec.write().isEmpty()) { - text += QLatin1Char(':'); + text += u':'; if (!isStdSetterName(spec.write(), spec.name())) text += spec.write(); } - text += QLatin1Char('"'); + text += u'"'; return text; } +static QString _plainName(const QString &s) +{ + auto cutPos = s.lastIndexOf(u"::"_s); + return cutPos < 0 ? s : s.right(s.length() - (cutPos + 2)); +} + +/********************************************************************** + * + * Decision whether to use an IntEnum/IntFlag + * ------------------------------------------ + * + * Unfortunately, all attempts to drive this decision automagically + * did not work out. We therefore compile a list in with known + * IntEnum and IntFlag. + */ + +/* + * This function is now unused and replaced by TypeSystem::PythonEnumType + */ +#if 0 +static QSet<QString> useIntSet() +{ + static const QSet<QString> result{ + /* IntEnum */ u"PySide6.QtCore.QDataStream.Version"_s, + /* IntEnum */ u"PySide6.QtCore.QEvent.Type"_s, + /* IntEnum */ u"PySide6.QtCore.QLocale.FloatingPointPrecisionOption"_s, + /* IntFlag */ u"PySide6.QtCore.QLocale.LanguageCodeType"_s, + /* IntFlag */ u"PySide6.QtCore.QUrl.ComponentFormattingOption"_s, + // note: "QUrl::UrlFormattingOption" is set as IntFlag without flags + /* IntFlag */ u"PySide6.QtCore.QUrl.UrlFormattingOption"_s, + /* IntFlag */ u"PySide6.QtCore.Qt.AlignmentFlag"_s, + /* IntFlag */ u"PySide6.QtCore.Qt.FocusPolicy"_s, + /* IntEnum */ u"PySide6.QtCore.Qt.GestureType"_s, + /* IntEnum */ u"PySide6.QtCore.Qt.ItemDataRole"_s, + /* IntEnum */ u"PySide6.QtCore.Qt.Key"_s, + /* Flag */ u"PySide6.QtCore.Qt.Modifier"_s, + // note: "Qt::TextFlag" is set as IntFlag without flags + /* IntFlag */ u"PySide6.QtCore.Qt.TextFlag"_s, + /* IntFlag */ u"PySide6.QtCore.Qt.WindowType"_s, + // This is found in QtWidgets but should be in QtGui. + /* IntEnum */ u"PySide6.QtGui.QFileSystemModel.Roles"_s, + /* IntEnum */ u"PySide6.QtGui.QFont.Stretch"_s, + /* IntEnum */ u"PySide6.QtGui.QFont.Weight"_s, + /* IntEnum */ u"PySide6.QtGui.QTextDocument.ResourceType"_s, + /* IntEnum */ u"PySide6.QtGui.QTextFormat.FormatType"_s, + /* IntEnum */ u"PySide6.QtGui.QTextFormat.ObjectTypes"_s, + /* IntEnum */ u"PySide6.QtGui.QTextFormat.Property"_s, + /* IntEnum */ u"PySide6.QtWidgets.QDialog.DialogCode"_s, + /* IntEnum */ u"PySide6.QtWidgets.QFrame.Shadow"_s, + /* IntEnum */ u"PySide6.QtWidgets.QFrame.Shape"_s, + /* IntEnum */ u"PySide6.QtWidgets.QListWidgetItem.ItemType"_s, + /* IntFlag */ u"PySide6.QtWidgets.QMessageBox.StandardButton"_s, + // note: "QSizePolicy::PolicyFlag" is set as IntFlag without flags + /* IntFlag */ u"PySide6.QtWidgets.QSizePolicy.PolicyFlag"_s, + /* IntEnum */ u"PySide6.QtWidgets.QStyle.ComplexControl"_s, + /* IntEnum */ u"PySide6.QtWidgets.QStyle.ContentsType"_s, + /* IntEnum */ u"PySide6.QtWidgets.QStyle.ControlElement"_s, + /* IntEnum */ u"PySide6.QtWidgets.QStyle.PixelMetric"_s, + /* IntEnum */ u"PySide6.QtWidgets.QStyle.PrimitiveElement"_s, + /* IntEnum */ u"PySide6.QtWidgets.QStyle.StandardPixmap"_s, + /* IntEnum */ u"PySide6.QtWidgets.QStyle.StyleHint"_s, + /* IntEnum */ u"PySide6.QtWidgets.QStyle.SubElement"_s, + /* IntEnum */ u"PySide6.QtWidgets.QTableWidgetItem.ItemType"_s, + /* IntEnum */ u"PySide6.QtWidgets.QTreeWidgetItem.ItemType"_s, + /* IntEnum */ u"PySide6.QtCharts.QBoxSet.ValuePositions"_s, + /* IntEnum */ u"PySide6.QtMultimedia.QMediaPlayer.Loops"_s, + /* IntEnum */ u"PySide6.QtQuick.QSGGeometry.DrawingMode"_s, + /* IntEnum */ u"PySide6.QtWebEngineCore.QWebEngineScript.ScriptWorldId"_s, + // Added because it should really be used as number + /* IntEnum */ u"PySide6.QtCore.QMetaType.Type"_s, + /* IntEnum */ u"PySide6.QtSerialPort.QSerialPort.BaudRate"_s, + }; + return result; +} +#endif + +static bool _shouldInheritInt(const AbstractMetaEnum &cppEnum) +{ + return !cppEnum.fullName().startsWith(u"PySide6."_s); +} + +static QString BuildEnumFlagInfo(const AbstractMetaEnum &cppEnum) +{ + auto enumType = cppEnum.typeEntry(); + QString result = _plainName(enumType->name()); + auto flags = enumType->flags(); + auto decision = enumType->pythonEnumType(); + bool _int = _shouldInheritInt(cppEnum); + bool _flag = bool(flags); + + if (decision != TypeSystem::PythonEnumType::Unspecified) { + _int = decision == TypeSystem::PythonEnumType::IntEnum || + decision == TypeSystem::PythonEnumType::IntFlag; + _flag = decision == TypeSystem::PythonEnumType::Flag || + decision == TypeSystem::PythonEnumType::IntFlag; + } + result += _flag ? (_int ? u":IntFlag"_s : u":Flag"_s) + : (_int ? u":IntEnum"_s : u":Enum"_s); + if (flags) + result += u':' + _plainName(flags->flagsName()); + return u'"' + result + u'"'; +} + static void writePyGetSetDefEntry(TextStream &s, const QString &name, const QString &getFunc, const QString &setFunc) { - s << "{const_cast<char *>(\"" << name << "\"), " << getFunc << ", " - << (setFunc.isEmpty() ? QLatin1String(NULL_PTR) : setFunc) << "},\n"; + s << "{const_cast<char *>(\"" << mangleName(name) << "\"), " << getFunc << ", " + << (setFunc.isEmpty() ? NULL_PTR : setFunc) << ", nullptr, nullptr},\n"; } -/*! - Function used to write the class generated binding code on the buffer - \param s the output buffer - \param metaClass the pointer to metaclass information -*/ -void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classContext) +static bool generateRichComparison(const GeneratorContext &c) { - s.setLanguage(TextStream::Language::Cpp); - const AbstractMetaClass *metaClass = classContext.metaClass(); + const auto metaClass = c.metaClass(); + if (c.forSmartPointer()) { + auto te = std::static_pointer_cast<const SmartPointerTypeEntry>(metaClass->typeEntry()); + return te->smartPointerType() == TypeSystem::SmartPointerType::Shared; + } + + return !metaClass->isNamespace() && metaClass->hasComparisonOperatorOverload(); +} + +void CppGenerator::generateIncludes(TextStream &s, const GeneratorContext &classContext, + const IncludeGroupList &includes, + const AbstractMetaClassCList &innerClasses) const +{ + const auto metaClass = classContext.metaClass(); // write license comment s << licenseComment() << '\n'; - if (!avoidProtectedHack() && !metaClass->isNamespace() && !metaClass->hasPrivateDestructor()) { - s << "//workaround to access protected functions\n"; - s << "#define protected public\n\n"; - } + const bool normalClass = !classContext.forSmartPointer(); + // Normally only required for classes for which we want to generate protected API, + // but it needs to be generated into all files to ensure ODR for Unity builds. + if (!avoidProtectedHack()) + s << HeaderGenerator::protectedHackDefine; + QByteArrayList cppIncludes{"typeinfo", "iterator", // for containers + "cctype", "cstring"}; // headers s << "// default includes\n"; s << "#include <shiboken.h>\n"; - if (usePySideExtensions()) { + if (wrapperDiagnostics()) { + s << "#include <helper.h>\n"; + cppIncludes << "iostream"; + } + + if (normalClass && usePySideExtensions()) { s << includeQDebug; - s << "#include <pysidesignal.h>\n" - << "#include <pysideproperty.h>\n" - << "#include <pyside.h>\n" - << "#include <pysideqenum.h>\n" + if (metaClass->hasToStringCapability()) + s << "#include <QtCore/QBuffer>\n"; + if (isQObject(metaClass)) { + s << "#include <pysideqobject.h>\n" + << "#include <pysidesignal.h>\n" + << "#include <pysideproperty.h>\n" + << "#include <signalmanager.h>\n" + << "#include <pysidemetafunction.h>\n"; + } + s << "#include <pysideqenum.h>\n" + << "#include <pysideqmetatype.h>\n" + << "#include <pysideutils.h>\n" << "#include <feature_select.h>\n" << "QT_WARNING_DISABLE_DEPRECATED\n\n"; - } - - s << "#include <typeinfo>\n"; - if (usePySideExtensions() && metaClass->isQObject()) { - s << "#include <signalmanager.h>\n"; - s << "#include <pysidemetafunction.h>\n"; } // The multiple inheritance initialization function // needs the 'set' class from C++ STL. - if (getMultipleInheritingClass(metaClass) != nullptr) - s << "#include <algorithm>\n#include <set>\n"; - if (metaClass->generateExceptionHandling()) - s << "#include <exception>\n"; - s << "#include <iterator>\n"; // For containers - - if (wrapperDiagnostics()) - s << "#include <helper.h>\n#include <iostream>\n"; + if (normalClass && getMultipleInheritingClass(metaClass) != nullptr) + cppIncludes << "algorithm" << "set"; + if (normalClass && metaClass->generateExceptionHandling()) + cppIncludes << "exception"; s << "\n// module include\n" << "#include \"" << getModuleHeaderFileName() << "\"\n"; if (hasPrivateClasses()) s << "#include \"" << getPrivateModuleHeaderFileName() << "\"\n"; - QString headerfile = fileNameForContext(classContext); - headerfile.replace(QLatin1String(".cpp"), QLatin1String(".h")); - s << "\n// main header\n" << "#include \"" << headerfile << "\"\n"; - - s << '\n' << "// inner classes\n"; - const AbstractMetaClassList &innerClasses = metaClass->innerClasses(); - for (AbstractMetaClass *innerClass : innerClasses) { - GeneratorContext innerClassContext = contextForClass(innerClass); - if (shouldGenerate(innerClass) && !innerClass->typeEntry()->isSmartPointer()) { - QString headerfile = fileNameForContext(innerClassContext); - headerfile.replace(QLatin1String(".cpp"), QLatin1String(".h")); - s << "#include \"" << headerfile << "\"\n"; + s << "\n// main header\n" << "#include \"" + << HeaderGenerator::headerFileNameForContext(classContext) << "\"\n"; + + if (!innerClasses.isEmpty()) { + s << "\n// inner classes\n"; + for (const auto &innerClass : innerClasses) { + GeneratorContext innerClassContext = contextForClass(innerClass); + s << "#include \"" + << HeaderGenerator::headerFileNameForContext(innerClassContext) << "\"\n"; } } - AbstractMetaEnumList classEnums = metaClass->enums(); - metaClass->getEnumsFromInvisibleNamespacesToBeGenerated(&classEnums); + if (avoidProtectedHack()) + s << baseWrapperIncludes(classContext); - //Extra includes - QList<Include> includes; - if (!classContext.useWrapper()) - includes += metaClass->typeEntry()->extraIncludes(); - for (const AbstractMetaEnum &cppEnum : qAsConst(classEnums)) - includes.append(cppEnum.typeEntry()->extraIncludes()); - if (!includes.isEmpty()) { - s << "\n// Extra includes\n"; - std::sort(includes.begin(), includes.end()); - for (const Include &inc : qAsConst(includes)) - s << inc.toString() << '\n'; - s << '\n'; + for (const auto &g : includes) + s << g; + + // C++ includes + std::sort(cppIncludes.begin(), cppIncludes.end()); + s << '\n'; + for (const auto &i : std::as_const(cppIncludes)) + s << "#include <" << i << ">\n"; +} + +// Write methods definition +void CppGenerator::writePyMethodDefs(TextStream &s, const QString &className, + const QString &methodsDefinitions) +{ + s << "static PyMethodDef " << className << "_methods[] = {\n" << indent + << methodsDefinitions << METHOD_DEF_SENTINEL << outdent << "};\n\n"; +} + +void CppGenerator::writeModuleCodeSnips(TextStream &s, const CodeSnipList &codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language) const +{ + if (!codeSnips.isEmpty()) { + try { + writeCodeSnips(s, codeSnips, position, language); + } catch (const std::exception &e) { + throw Exception(msgSnippetError("module source of "_L1 + moduleName(), e.what())); + } + } +} + +bool CppGenerator::hasHashFunction(const AbstractMetaClassCPtr &c) +{ + return !c->typeEntry()->hashFunction().isEmpty() + || c->hasHashFunction(); +} + +static bool needsTypeDiscoveryFunction(const AbstractMetaClassCPtr &c) +{ + return c->baseClass() != nullptr + && (c->isPolymorphic() || !c->typeEntry()->polymorphicIdValue().isEmpty()); +} + +static void writeAddedTypeSignatures(TextStream &s, const ComplexTypeEntryCPtr &te) +{ + for (const auto &e : te->addedPyMethodDefEntrys()) { + if (auto count = e.signatures.size()) { + for (qsizetype i = 0; i < count; ++i) { + if (count > 1) + s << i << ':'; + s << e.signatures.at(i) << '\n'; + } + } } +} - s << "\n#include <cctype>\n#include <cstring>\n"; +/// Function used to write the class generated binding code on the buffer +/// \param s the output buffer +/// \param classContext the pointer to metaclass information +void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classContext) +{ + if (classContext.forSmartPointer()) { + generateSmartPointerClass(s, classContext); + return; + } - if (metaClass->typeEntry()->typeFlags() & ComplexTypeEntry::Deprecated) + s.setLanguage(TextStream::Language::Cpp); + AbstractMetaClassCPtr metaClass = classContext.metaClass(); + const auto typeEntry = metaClass->typeEntry(); + + auto innerClasses = metaClass->innerClasses(); + for (auto it = innerClasses.begin(); it != innerClasses.end(); ) { + auto innerTypeEntry = (*it)->typeEntry(); + if (shouldGenerate(innerTypeEntry) && !innerTypeEntry->isSmartPointer()) + ++it; + else + it = innerClasses.erase(it); + } + + AbstractMetaEnumList classEnums = metaClass->enums(); + metaClass->getEnumsFromInvisibleNamespacesToBeGenerated(&classEnums); + + IncludeGroupList includeGroups; + if (!classContext.useWrapper() || !avoidProtectedHack()) + includeGroups.append(classIncludes(metaClass)); + generateIncludes(s, classContext, includeGroups, innerClasses); + + if (typeEntry->typeFlags().testFlag(ComplexTypeEntry::Deprecated)) s << "#Deprecated\n"; // Use class base namespace { - const AbstractMetaClass *context = metaClass->enclosingClass(); + AbstractMetaClassCPtr context = metaClass->enclosingClass(); while (context) { if (context->isNamespace() && !context->enclosingClass() - && static_cast<const NamespaceTypeEntry *>(context->typeEntry())->generateUsing()) { + && std::static_pointer_cast<const NamespaceTypeEntry>(context->typeEntry())->generateUsing()) { s << "\nusing namespace " << context->qualifiedCppName() << ";\n"; break; } @@ -466,177 +583,109 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon } } - s << "\n\n" << typeNameFunc << '\n'; - - // Create string literal for smart pointer getter method. - if (classContext.forSmartPointer()) { - const auto *typeEntry = - static_cast<const SmartPointerTypeEntry *>(classContext.preciseType() - .typeEntry()); - QString rawGetter = typeEntry->getter(); - s << "static const char * " << SMART_POINTER_GETTER << " = \"" << rawGetter << "\";"; - } + s << '\n'; // class inject-code native/beginning - if (!metaClass->typeEntry()->codeSnips().isEmpty()) { - writeClassCodeSnips(s, metaClass->typeEntry()->codeSnips(), + if (!typeEntry->codeSnips().isEmpty()) { + writeClassCodeSnips(s, typeEntry->codeSnips(), TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode, classContext); s << '\n'; } // python conversion rules - if (metaClass->typeEntry()->hasTargetConversionRule()) { - s << "// Python Conversion\n"; - s << metaClass->typeEntry()->targetConversionRule() << '\n'; + if (typeEntry->isValue()) { + auto vte = std::static_pointer_cast<const ValueTypeEntry>(typeEntry); + if (vte->hasTargetConversionRule()) { + s << "// Python Conversion\n"; + s << vte->targetConversionRule() << '\n'; + } } if (classContext.useWrapper()) { s << "// Native ---------------------------------------------------------\n\n"; if (avoidProtectedHack() && usePySideExtensions()) { - s << "void " << classContext.wrapperName() << "::pysideInitQtMetaTypes()\n{\n"; - Indentation indent(s); + s << "void " << classContext.wrapperName() << "::pysideInitQtMetaTypes()\n{\n" + << indent; writeInitQtMetaTypeFunctionBody(s, classContext); - s << "}\n\n"; + s << outdent << "}\n\n"; } - const auto &funcs = filterFunctions(metaClass); int maxOverrides = 0; writeCacheResetNative(s, classContext); - for (const auto &func : funcs) { - const bool notAbstract = !func->isAbstract(); - if ((func->isPrivate() && notAbstract && !func->isVisibilityModifiedToPrivate()) - || (func->isModifiedRemoved() && notAbstract)) - continue; - if (func->functionType() == AbstractMetaFunction::ConstructorFunction && !func->isUserAdded()) + for (const auto &func : metaClass->functions()) { + const auto generation = functionGeneration(func); + if (generation.testFlag(FunctionGenerationFlag::WrapperConstructor)) writeConstructorNative(s, classContext, func); - else if (shouldWriteVirtualMethodNative(func)) + else if (generation.testFlag(FunctionGenerationFlag::VirtualMethod)) writeVirtualMethodNative(s, func, maxOverrides++); } - if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) { - if (usePySideExtensions() && metaClass->isQObject()) - writeMetaObjectMethod(s, classContext); + if (shouldGenerateMetaObjectFunctions(metaClass)) + writeMetaObjectMethod(s, classContext); + if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) writeDestructorNative(s, classContext); - } } + for (const auto &f : metaClass->userAddedPythonOverrides()) + writeUserAddedPythonOverride(s, f); + StringStream smd(TextStream::Language::Cpp); StringStream md(TextStream::Language::Cpp); StringStream signatureStream(TextStream::Language::Cpp); - s << "\n// Target ---------------------------------------------------------\n\n" - << "extern \"C\" {\n"; + s << openTargetExternC; + const auto &functionGroups = getFunctionGroups(metaClass); for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) { - AbstractMetaFunctionCList overloads; - QSet<QString> seenSignatures; - bool staticEncountered = false; - for (const auto &func : it.value()) { - if (!func->isAssignmentOperator() - && !func->usesRValueReferences() - && !func->isConversionOperator() - && !func->isModifiedRemoved() - && (!func->isPrivate() || func->functionType() == AbstractMetaFunction::EmptyFunction) - && func->ownerClass() == func->implementingClass() - && (func->name() != QLatin1String("qt_metacall"))) { - // PYSIDE-331: Inheritance works correctly when there are disjoint functions. - // But when a function is both in a class and inherited in a subclass, - // then we need to search through all subclasses and collect the new signatures. - overloads << getFunctionAndInheritedOverloads(func, &seenSignatures); - if (func->isStatic()) - staticEncountered = true; - } - } - // PYSIDE-886: If the method does not have any static overloads declared - // in the class in question, remove all inherited static methods as setting - // METH_STATIC in that case can cause crashes for the instance methods. - // Manifested as crash when calling QPlainTextEdit::find() (clash with - // static QWidget::find(WId)). - if (!staticEncountered) { - for (qsizetype i = overloads.size() - 1; i >= 0; --i) { - if (overloads.at(i)->isStatic()) - overloads.removeAt(i); - } - } - + if (contains(sequenceProtocols(), it.key()) || contains(mappingProtocols(), it.key())) + continue; + const AbstractMetaFunctionCList &overloads = it.value(); if (overloads.isEmpty()) continue; const auto rfunc = overloads.constFirst(); - if (contains(sequenceProtocols(), rfunc->name()) - || contains(mappingProtocols(), rfunc->name())) { - continue; - } + OverloadData overloadData(overloads, api()); if (rfunc->isConstructor()) { - // @TODO: Implement constructor support for smart pointers, so that they can be - // instantiated in python code. - if (classContext.forSmartPointer()) - continue; - writeConstructorWrapper(s, overloads, classContext); - writeSignatureInfo(signatureStream, overloads); + writeConstructorWrapper(s, overloadData, classContext); + writeSignatureInfo(signatureStream, overloadData); } // call operators - else if (rfunc->name() == QLatin1String("operator()")) { - writeMethodWrapper(s, overloads, classContext); - writeSignatureInfo(signatureStream, overloads); + else if (rfunc->name() == u"operator()") { + writeMethodWrapper(s, overloadData, classContext); + writeSignatureInfo(signatureStream, overloadData); } else if (!rfunc->isOperatorOverload()) { - - if (classContext.forSmartPointer()) { - const auto *smartPointerTypeEntry = - static_cast<const SmartPointerTypeEntry *>( - classContext.preciseType().typeEntry()); - - if (smartPointerTypeEntry->getter() == rfunc->name()) { - // Replace the return type of the raw pointer getter method with the actual - // return type. - QString innerTypeName = - classContext.preciseType().getSmartPointerInnerType().cppSignature(); - QString pointerToInnerTypeName = innerTypeName + QLatin1Char('*'); - // @TODO: This possibly leaks, but there are a bunch of other places where this - // is done, so this will be fixed in bulk with all the other cases, because the - // ownership of the pointers is not clear at the moment. - auto pointerToInnerType = - buildAbstractMetaTypeFromString(pointerToInnerTypeName); - Q_ASSERT(pointerToInnerType.has_value()); - auto mutableRfunc = overloads.constFirst(); - qSharedPointerConstCast<AbstractMetaFunction>(mutableRfunc)->setType(pointerToInnerType.value()); - } else if (smartPointerTypeEntry->refCountMethodName().isEmpty() - || smartPointerTypeEntry->refCountMethodName() != rfunc->name()) { - // Skip all public methods of the smart pointer except for the raw getter and - // the ref count method. - continue; - } - } - - writeMethodWrapper(s, overloads, classContext); - writeSignatureInfo(signatureStream, overloads); + writeMethodWrapper(s, overloadData, classContext); + writeSignatureInfo(signatureStream, overloadData); // For a mixture of static and member function overloads, // a separate PyMethodDef entry is written which is referenced // in the PyMethodDef list and later in getattro() for handling // the non-static case. + const auto defEntries = methodDefinitionEntries(overloadData); if (OverloadData::hasStaticAndInstanceFunctions(overloads)) { QString methDefName = cpythonMethodDefinitionName(rfunc); - smd << "static PyMethodDef " << methDefName << " = " << indent; - writeMethodDefinitionEntries(smd, overloads, 1); - smd << outdent << ";\n\n"; + smd << "static PyMethodDef " << methDefName << " = " << indent + << defEntries.constFirst() << outdent << ";\n\n"; } - writeMethodDefinition(md, overloads); + const auto &fname = rfunc->name(); + if (!m_tpFuncs.contains(fname) && !m_nbFuncs.contains(fname)) + md << defEntries; } } + for (const auto &pyMethodDef : typeEntry->addedPyMethodDefEntrys()) + md << pyMethodDef << ",\n"; + + if (typeEntry->isValue()) + writeCopyFunction(s, md, signatureStream, classContext); + const QString methodsDefinitions = md.toString(); const QString singleMethodDefinitions = smd.toString(); const QString className = chopType(cpythonTypeName(metaClass)); - if (metaClass->typeEntry()->isValue() || metaClass->typeEntry()->isSmartPointer()) { - writeCopyFunction(s, classContext); - signatureStream << fullPythonClassName(metaClass) << ".__copy__()\n"; - } - // Write single method definitions s << singleMethodDefinitions; @@ -652,74 +701,44 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon s << '\n'; s << "static const char *" << className << "_PropertyStrings[] = {\n" << indent; - for (const auto &entry : qAsConst(sorter)) + for (const auto &entry : std::as_const(sorter)) + s << entry << ",\n"; + s << NULL_PTR << " // Sentinel\n" + << outdent << "};\n\n"; + + } + // PYSIDE-1735: Write an EnumFlagInfo structure + QStringList sorter; + for (const auto &entry : std::as_const(classEnums)) + sorter.append(BuildEnumFlagInfo(entry)); + sorter.sort(); + if (!sorter.empty()) { + s << "static const char *" << className << "_EnumFlagInfo[] = {\n" << indent; + for (const auto &entry : std::as_const(sorter)) s << entry << ",\n"; s << NULL_PTR << " // Sentinel\n" << outdent << "};\n\n"; } // Write methods definition - s << "static PyMethodDef " << className << "_methods[] = {\n" << indent - << methodsDefinitions << '\n'; - if (metaClass->typeEntry()->isValue() || metaClass->typeEntry()->isSmartPointer()) { - s << "{\"__copy__\", reinterpret_cast<PyCFunction>(" << className << "___copy__)" - << ", METH_NOARGS},\n"; - } - s << '{' << NULL_PTR << ", " << NULL_PTR << "} // Sentinel\n" << outdent - << "};\n\n"; + writePyMethodDefs(s, className, methodsDefinitions); // Write tp_s/getattro function const AttroCheck attroCheck = checkAttroFunctionNeeds(metaClass); - if (attroCheck.testFlag(AttroCheckFlag::GetattroSmartPointer)) { - writeSmartPointerGetattroFunction(s, classContext); - writeSmartPointerSetattroFunction(s, classContext); - } else { - if ((attroCheck & AttroCheckFlag::GetattroMask) != 0) - writeGetattroFunction(s, attroCheck, classContext); - if ((attroCheck & AttroCheckFlag::SetattroMask) != 0) - writeSetattroFunction(s, attroCheck, classContext); - } - - const auto f = boolCast(metaClass); - if (!f.isNull()) { - ErrorCode errorCode(-1); - s << "static int " << cpythonBaseName(metaClass) << "___nb_bool(PyObject *self)\n" - << "{\n" << indent; - writeCppSelfDefinition(s, classContext); - if (f->allowThread()) { - s << "int result;\n" - << BEGIN_ALLOW_THREADS << '\n' - << "result = !" << CPP_SELF_VAR << "->isNull();\n" - << END_ALLOW_THREADS << '\n' - << "return result;\n"; - } else { - s << "return !" << CPP_SELF_VAR << "->isNull();\n"; - } - s << outdent << "}\n\n"; - } + if ((attroCheck & AttroCheckFlag::GetattroMask) != 0) + writeGetattroFunction(s, attroCheck, classContext); + if ((attroCheck & AttroCheckFlag::SetattroMask) != 0) + writeSetattroFunction(s, attroCheck, classContext); - if (supportsNumberProtocol(metaClass) && !metaClass->typeEntry()->isSmartPointer()) { - const QList<AbstractMetaFunctionCList> opOverloads = filterGroupedOperatorFunctions( - metaClass, - OperatorQueryOption::ArithmeticOp - | OperatorQueryOption::IncDecrementOp - | OperatorQueryOption::LogicalOp - | OperatorQueryOption::BitwiseOp); - - for (const AbstractMetaFunctionCList &allOverloads : opOverloads) { - AbstractMetaFunctionCList overloads; - for (const auto &func : allOverloads) { - if (!func->isModifiedRemoved() - && !func->isPrivate() - && (func->ownerClass() == func->implementingClass() || func->isAbstract())) - overloads.append(func); - } - - if (overloads.isEmpty()) - continue; + if (const auto f = boolCast(metaClass) ; f.has_value()) + writeNbBoolFunction(classContext, f.value(), s); - writeMethodWrapper(s, overloads, classContext); - writeSignatureInfo(signatureStream, overloads); + if (supportsNumberProtocol(metaClass)) { + const auto numberProtocolOps = numberProtocolOperators(metaClass); + for (const auto &overloads : numberProtocolOps) { + OverloadData overloadData(overloads, api()); + writeMethodWrapper(s, overloadData, classContext); + writeSignatureInfo(signatureStream, overloadData); } } @@ -731,12 +750,12 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon writeMappingMethods(s, metaClass, classContext); } - if (!metaClass->isNamespace() && metaClass->hasComparisonOperatorOverload()) { + if (generateRichComparison(classContext)) { s << "// Rich comparison\n"; writeRichCompareFunction(s, classContext); } - if (shouldGenerateGetSetList(metaClass) && !classContext.forSmartPointer()) { + if (shouldGenerateGetSetList(metaClass)) { const AbstractMetaFieldList &fields = metaClass->fields(); for (const AbstractMetaField &metaField : fields) { if (metaField.canGenerateGetter()) @@ -779,13 +798,13 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon cpythonGetterFunctionName(property, metaClass), setter); } } - s << '{' << NULL_PTR << "} // Sentinel\n" + s << "{nullptr, nullptr, nullptr, nullptr, nullptr} // Sentinel\n" << outdent << "};\n\n"; } - s << "} // extern \"C\"\n\n"; + s << closeExternC; - if (!metaClass->typeEntry()->hashFunction().isEmpty()) + if (hasHashFunction(metaClass)) writeHashFunction(s, classContext); // Write tp_traverse and tp_clear functions. @@ -795,27 +814,38 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon writeClassDefinition(s, metaClass, classContext); s << '\n'; - if (metaClass->isPolymorphic() && metaClass->baseClass()) + if (needsTypeDiscoveryFunction(metaClass)) { writeTypeDiscoveryFunction(s, metaClass); - - writeFlagsNumberMethodsDefinitions(s, classEnums); - s << '\n'; + s << '\n'; + } writeConverterFunctions(s, metaClass, classContext); + writeAddedTypeSignatures(signatureStream, typeEntry); writeClassRegister(s, metaClass, classContext, signatureStream); if (metaClass->hasStaticFields()) writeStaticFieldInitialization(s, metaClass); // class inject-code native/end - if (!metaClass->typeEntry()->codeSnips().isEmpty()) { - writeClassCodeSnips(s, metaClass->typeEntry()->codeSnips(), + if (!typeEntry->codeSnips().isEmpty()) { + writeClassCodeSnips(s, typeEntry->codeSnips(), TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode, classContext); s << '\n'; } } +void CppGenerator::writeMethodWrapper(TextStream &s, TextStream &definitionStream, + TextStream &signatureStream, + const AbstractMetaFunctionCList &overloads, + const GeneratorContext &classContext) const +{ + OverloadData overloadData(overloads, api()); + writeMethodWrapper(s, overloadData, classContext); + writeSignatureInfo(signatureStream, overloadData); + definitionStream << methodDefinitionEntries(overloadData); +} + void CppGenerator::writeCacheResetNative(TextStream &s, const GeneratorContext &classContext) { s << "void " << classContext.wrapperName() @@ -827,7 +857,7 @@ void CppGenerator::writeCacheResetNative(TextStream &s, const GeneratorContext & void CppGenerator::writeConstructorNative(TextStream &s, const GeneratorContext &classContext, const AbstractMetaFunctionCPtr &func) const { - const QString qualifiedName = classContext.wrapperName() + QLatin1String("::"); + const QString qualifiedName = classContext.wrapperName() + u"::"_s; s << functionSignature(func, qualifiedName, QString(), OriginalTypeDescription | SkipDefaultValues); s << " : "; @@ -837,14 +867,16 @@ void CppGenerator::writeConstructorNative(TextStream &s, const GeneratorContext s << R"(std::cerr << __FUNCTION__ << ' ' << this << '\n';)" << '\n'; const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast(); s << "resetPyMethodCache();\n"; - writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode, func, lastArg); + writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionBeginning, + TypeSystem::NativeCode, func, false, lastArg); s << "// ... middle\n"; - writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode, func, lastArg); + writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionEnd, + TypeSystem::NativeCode, func, false, lastArg); s << outdent << "}\n\n"; } void CppGenerator::writeDestructorNative(TextStream &s, - const GeneratorContext &classContext) const + const GeneratorContext &classContext) { s << classContext.wrapperName() << "::~" << classContext.wrapperName() << "()\n{\n" << indent; @@ -856,49 +888,51 @@ Shiboken::Object::destroy(wrapper, this); )" << outdent << "}\n"; } -static bool allArgumentsRemoved(const AbstractMetaFunctionCPtr& func) -{ - if (func->arguments().isEmpty()) - return false; - const AbstractMetaArgumentList &arguments = func->arguments(); - for (const AbstractMetaArgument &arg : arguments) { - if (!func->argumentRemoved(arg.argumentIndex() + 1)) - return false; - } - return true; -} - +// Return type for error messages when getting invalid types from virtual +// methods implemented in Python in C++ wrappers QString CppGenerator::getVirtualFunctionReturnTypeName(const AbstractMetaFunctionCPtr &func) const { if (func->type().isVoid()) - return QLatin1String("\"\""); + return u"\"\""_s; - if (!func->typeReplaced(0).isEmpty()) - return QLatin1Char('"') + func->typeReplaced(0) + QLatin1Char('"'); + if (func->isTypeModified()) + return u'"' + func->modifiedTypeName() + u'"'; // SbkType would return null when the type is a container. auto typeEntry = func->type().typeEntry(); if (typeEntry->isContainer()) { - return QLatin1Char('"') - + reinterpret_cast<const ContainerTypeEntry *>(typeEntry)->typeName() - + QLatin1Char('"'); + const auto cte = std::static_pointer_cast<const ContainerTypeEntry>(typeEntry); + switch (cte->containerKind()) { + case ContainerTypeEntry::ListContainer: + case ContainerTypeEntry::SpanContainer: + break; + case ContainerTypeEntry::SetContainer: + return uR"("set")"_s; + break; + case ContainerTypeEntry::MapContainer: + case ContainerTypeEntry::MultiMapContainer: + return uR"("dict")"_s; + break; + case ContainerTypeEntry::PairContainer: + return uR"("tuple")"_s; + break; + } + return uR"("list")"_s; } if (typeEntry->isSmartPointer()) - return QLatin1Char('"') + typeEntry->qualifiedCppName() + QLatin1Char('"'); + return u'"' + typeEntry->qualifiedCppName() + u'"'; if (avoidProtectedHack()) { auto metaEnum = api().findAbstractMetaEnum(func->type().typeEntry()); - if (metaEnum.has_value() && metaEnum->isProtected()) { - return QLatin1Char('"') + protectedEnumSurrogateName(metaEnum.value()) - + QLatin1Char('"'); - } + if (metaEnum.has_value() && metaEnum->isProtected()) + return u'"' + protectedEnumSurrogateName(metaEnum.value()) + u'"'; } if (func->type().isPrimitive()) - return QLatin1Char('"') + func->type().name() + QLatin1Char('"'); + return u'"' + func->type().name() + u'"'; - return QLatin1String("reinterpret_cast<PyTypeObject *>(Shiboken::SbkType< ") - + typeEntry->qualifiedCppName() + QLatin1String(" >())->tp_name"); + return u"Shiboken::SbkType< "_s + + typeEntry->qualifiedCppName() + u" >()->tp_name"_s; } // When writing an overridden method of a wrapper class, write the part @@ -908,22 +942,26 @@ void CppGenerator::writeVirtualMethodCppCall(TextStream &s, const QString &funcName, const CodeSnipList &snips, const AbstractMetaArgument *lastArg, - const TypeEntry *retType, - const QString &returnStatement) const + const TypeEntryCPtr &retType, + const QString &returnStatement, bool hasGil) const { if (!snips.isEmpty()) { writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, - TypeSystem::ShellCode, func, lastArg); + TypeSystem::ShellCode, func, false, lastArg); } if (func->isAbstract()) { - s << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '" - << func->ownerClass()->name() << '.' << funcName - << "()' not implemented.\");\n" + if (!hasGil) + s << "Shiboken::GilState gil;\n"; + s << "Shiboken::Errors::setPureVirtualMethodError(\"" + << func->ownerClass()->name() << '.' << funcName << "\");\n" << returnStatement << '\n'; return; } + if (hasGil) + s << "gil.release();\n"; + if (retType) s << "return "; s << "this->::" << func->implementingClass()->qualifiedCppName() << "::"; @@ -933,23 +971,30 @@ void CppGenerator::writeVirtualMethodCppCall(TextStream &s, return; if (!snips.isEmpty()) { writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, - TypeSystem::ShellCode, func, lastArg); + TypeSystem::ShellCode, func, false, lastArg); } s << "return;\n"; } // Determine the return statement (void or a result value). -QString CppGenerator::virtualMethodReturn(TextStream &s, const ApiExtractorResult &api, - const AbstractMetaFunctionCPtr &func, - const FunctionModificationList &functionModifications) + +CppGenerator::VirtualMethodReturn + CppGenerator::virtualMethodReturn(const ApiExtractorResult &api, + const AbstractMetaFunctionCPtr &func, + const FunctionModificationList &functionModifications) { - if (func->isVoid()) - return QLatin1String("return;"); + VirtualMethodReturn result; + if (func->isVoid()) { + result.statement = "return;"_L1; + return result; + } + + result.statement = "return "_L1; const AbstractMetaType &returnType = func->type(); for (const FunctionModification &mod : functionModifications) { for (const ArgumentModification &argMod : mod.argument_mods()) { if (argMod.index() == 0 && !argMod.replacedDefaultExpression().isEmpty()) { - static const QRegularExpression regex(QStringLiteral("%(\\d+)")); + static const QRegularExpression regex("%(\\d+)"_L1); Q_ASSERT(regex.isValid()); QString expr = argMod.replacedDefaultExpression(); for (int offset = 0; ; ) { @@ -957,7 +1002,7 @@ QString CppGenerator::virtualMethodReturn(TextStream &s, const ApiExtractorResul if (!match.hasMatch()) break; const int argId = match.capturedView(1).toInt() - 1; - if (argId < 0 || argId > func->arguments().count()) { + if (argId < 0 || argId > func->arguments().size()) { qCWarning(lcShiboken, "The expression used in return value contains an invalid index."); break; } @@ -965,57 +1010,178 @@ QString CppGenerator::virtualMethodReturn(TextStream &s, const ApiExtractorResul offset = match.capturedStart(1); } DefaultValue defaultReturnExpr(DefaultValue::Custom, expr); - return QLatin1String("return ") + defaultReturnExpr.returnValue() - + QLatin1Char(';'); + result.statement += defaultReturnExpr.returnValue() + u';'; + return result; } } } QString errorMessage; const auto defaultReturnExpr = minimalConstructor(api, returnType, &errorMessage); if (!defaultReturnExpr.has_value()) { - QString errorMsg = QLatin1String(__FUNCTION__) + QLatin1String(": "); - if (const AbstractMetaClass *c = func->implementingClass()) - errorMsg += c->qualifiedCppName() + QLatin1String("::"); - errorMsg += func->signature(); + QString errorMsg = QLatin1StringView(__FUNCTION__) + u": "_s + + func->classQualifiedSignature(); errorMsg = msgCouldNotFindMinimalConstructor(errorMsg, func->type().cppSignature(), errorMessage); - qCWarning(lcShiboken).noquote().nospace() << errorMsg; - s << "\n#error " << errorMsg << '\n'; + throw Exception(errorMsg); + } + + result.needsReference = returnType.referenceType() == LValueReference; + result.statement += (result.needsReference + ? virtualMethodStaticReturnVar : defaultReturnExpr->returnValue()) + u';'; + return result; +} + +// Create an argument for Py_BuildValue() when writing virtual methods. +// Return a pair of (argument, format-char). +std::pair<QString, QChar> CppGenerator::virtualMethodNativeArg(const AbstractMetaFunctionCPtr &func, + const AbstractMetaArgument &arg) +{ + if (func->hasConversionRule(TypeSystem::TargetLangCode, arg.argumentIndex() + 1)) + return {arg.name() + CONV_RULE_OUT_VAR_SUFFIX, u'N'}; + + const auto &type = arg.type(); + auto argTypeEntry = type.typeEntry(); + // Check for primitive types convertible by Py_BuildValue() + if (argTypeEntry->isPrimitive() && !type.isCString()) { + const auto pte = basicReferencedTypeEntry(argTypeEntry); + auto it = formatUnits().constFind(pte->name()); + if (it != formatUnits().constEnd()) + return {arg.name(), it.value()}; + } + + // Rest: convert + StringStream ac(TextStream::Language::Cpp); + writeToPythonConversion(ac, type, func->ownerClass(), arg.name()); + return {ac.toString(), u'N'}; +} + +static const char PYTHON_ARGS_ARRAY[] = "pyArgArray"; + +void CppGenerator::writeVirtualMethodNativeVectorCallArgs(TextStream &s, + const AbstractMetaFunctionCPtr &func, + const AbstractMetaArgumentList &arguments, + const QList<int> &invalidateArgs) +{ + Q_ASSERT(!arguments.isEmpty()); + s << "PyObject *" << PYTHON_ARGS_ARRAY <<'[' << arguments.size() << "] = {\n" << indent; + const qsizetype last = arguments.size() - 1; + for (qsizetype i = 0; i <= last; ++i) { + const AbstractMetaArgument &arg = arguments.at(i); + if (func->hasConversionRule(TypeSystem::TargetLangCode, arg.argumentIndex() + 1)) { + s << arg.name() + CONV_RULE_OUT_VAR_SUFFIX; + } else { + writeToPythonConversion(s, arg.type(), func->ownerClass(), arg.name()); + } + if (i < last) + s << ','; + s << '\n'; + } + s << outdent << "};\n"; + + if (!invalidateArgs.isEmpty()) + s << '\n'; + for (int index : invalidateArgs) { + s << "const bool invalidateArg" << index << " = Py_REFCNT(" << PYTHON_ARGS_ARRAY << + '[' << index - 1 << "]) == 1;\n"; } - if (returnType.referenceType() == LValueReference) { - s << "static " << returnType.typeEntry()->qualifiedCppName() - << " result;\n"; - return QLatin1String("return result;"); +} + +void CppGenerator::writeVirtualMethodNativeArgs(TextStream &s, + const AbstractMetaFunctionCPtr &func, + const AbstractMetaArgumentList &arguments, + const QList<int> &invalidateArgs) +{ + s << "Shiboken::AutoDecRef " << PYTHON_ARGS << '('; + if (arguments.isEmpty()) { + s << "PyTuple_New(0));\n"; + return; } - return QLatin1String("return ") + defaultReturnExpr->returnValue() - + QLatin1Char(';'); + + QString format; + QStringList argConversions; + for (const AbstractMetaArgument &arg : arguments) { + auto argPair = virtualMethodNativeArg(func, arg); + argConversions.append(argPair.first); + format += argPair.second; + } + + s << "Py_BuildValue(\"(" << format << ")\",\n" + << indent << argConversions.join(u",\n"_s) << outdent << "\n));\n"; + + for (int index : std::as_const(invalidateArgs)) { + s << "bool invalidateArg" << index << " = Py_REFCNT(PyTuple_GET_ITEM(" << PYTHON_ARGS + << ", " << index - 1 << ")) == 1;\n"; + } +} + +static bool isArgumentNotRemoved(const AbstractMetaArgument &a) +{ + return !a.isModifiedRemoved(); +} + +// PyObject_Vectorcall(): since 3.9 +static const char vectorCallCondition[] = + "#if !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)\n"; + +// PyObject_CallNoArgs(): since 3.9, stable API since 3.10 +static const char noArgsCallCondition[] = + "#if !defined(PYPY_VERSION) && ((defined(Py_LIMITED_API) && Py_LIMITED_API >= 0x030A0000) || !defined(Py_LIMITED_API))\n"; +static const char inverseNoArgsCallCondition[] = + "#if defined(PYPY_VERSION) || (defined(Py_LIMITED_API) && Py_LIMITED_API < 0x030A0000)\n"; + +static inline void writeVirtualMethodStaticReturnVar(TextStream &s, const AbstractMetaFunctionCPtr &func) +{ + s << "static " << func->type().typeEntry()->qualifiedCppName() << ' ' + << virtualMethodStaticReturnVar << ";\n"; +} + +static void writeFuncNameVar(TextStream &s, const AbstractMetaFunctionCPtr &func, + const QString &funcName) +{ + // PYSIDE-1019: Add info about properties + int propFlag = 0; + if (func->isPropertyReader()) + propFlag |= 1; + if (func->isPropertyWriter()) + propFlag |= 2; + if (propFlag && func->isStatic()) + propFlag |= 4; + QString propStr; + if (propFlag != 90) + propStr = QString::number(propFlag) + u':'; + + if (propFlag != 0) + s << "// This method belongs to a property.\n"; + s << "static const char *funcName = \""; + if (propFlag != 0) + s << propFlag << ':'; + s << funcName << "\";\n"; } void CppGenerator::writeVirtualMethodNative(TextStream &s, const AbstractMetaFunctionCPtr &func, int cacheIndex) const { - //skip metaObject function, this will be written manually ahead - if (usePySideExtensions() && func->ownerClass() && func->ownerClass()->isQObject() && - ((func->name() == QLatin1String("metaObject")) || (func->name() == QLatin1String("qt_metacall")))) - return; - - const TypeEntry *retType = func->type().typeEntry(); + TypeEntryCPtr retType = func->type().typeEntry(); const QString funcName = func->isOperatorOverload() ? pythonOperatorFunctionName(func) : func->definitionNames().constFirst(); - QString prefix = wrapperName(func->ownerClass()) + QLatin1String("::"); - s << functionSignature(func, prefix, QString(), Generator::SkipDefaultValues|Generator::OriginalTypeDescription) + QString prefix = wrapperName(func->ownerClass()) + u"::"_s; + s << functionSignature(func, prefix, QString(), Generator::SkipDefaultValues | + Generator::OriginalTypeDescription) << "\n{\n" << indent; - const FunctionModificationList &functionModifications = func->modifications(); + const auto returnStatement = virtualMethodReturn(api(), func, + func->modifications()); - const QString returnStatement = virtualMethodReturn(s, api(), func, functionModifications); + if (returnStatement.needsReference) + writeVirtualMethodStaticReturnVar(s, func); - if (func->isAbstract() && func->isModifiedRemoved()) { - qCWarning(lcShiboken, "%s", qPrintable(msgPureVirtualFunctionRemoved(func.data()))); - s << returnStatement << '\n' << outdent << "}\n\n"; + const bool isAbstract = func->isAbstract(); + if (isAbstract && func->isModifiedRemoved()) { + qCWarning(lcShiboken, "%s", qPrintable(msgPureVirtualFunctionRemoved(func.get()))); + s << returnStatement.statement << '\n' << outdent << "}\n\n"; return; } @@ -1024,10 +1190,10 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast(); - //Write declaration/native injected code + // Write declaration/native injected code. if (!snips.isEmpty()) { writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionDeclaration, - TypeSystem::ShellCode, func, lastArg); + TypeSystem::ShellCode, func, false, lastArg); } if (wrapperDiagnostics()) { @@ -1039,240 +1205,270 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, << cacheIndex << R"( << "]=" << m_PyMethodCache[)" << cacheIndex << R"(] << '\n';)" << '\n'; } - // PYSIDE-803: Build a boolean cache for unused overrides. - const bool multi_line = func->isVoid() || !snips.isEmpty() || func->isAbstract(); - s << "if (m_PyMethodCache[" << cacheIndex << "])" << (multi_line ? " {\n" : "\n"); - { - Indentation indentation(s); - writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType, - returnStatement); - } + // PYSIDE-803: Build a boolean cache for unused overrides + const bool multi_line = func->isVoid() || !snips.isEmpty() || isAbstract; + s << "if (m_PyMethodCache[" << cacheIndex << "])" << (multi_line ? " {\n" : "\n") + << indent; + writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType, + returnStatement.statement, false); + s << outdent; if (multi_line) s << "}\n"; s << "Shiboken::GilState gil;\n"; // Get out of virtual method call if someone already threw an error. - s << "if (PyErr_Occurred())\n" << indent - << returnStatement << '\n' << outdent; - - //PYSIDE-1019: Add info about properties. - int propFlag = 0; - if (func->isPropertyReader()) - propFlag |= 1; - if (func->isPropertyWriter()) - propFlag |= 2; - if (propFlag && func->isStatic()) - propFlag |= 4; - QString propStr; - if (propFlag) - propStr = QString::number(propFlag) + QLatin1Char(':'); + s << "if (" << shibokenErrorsOccurred << ")\n" << indent + << returnStatement.statement << '\n' << outdent; s << "static PyObject *nameCache[2] = {};\n"; - if (propFlag) - s << "// This method belongs to a property.\n"; - s << "static const char *funcName = \"" << propStr << funcName << "\";\n" - << "Shiboken::AutoDecRef " << PYTHON_OVERRIDE_VAR + writeFuncNameVar(s, func, funcName); + s << "Shiboken::AutoDecRef " << PYTHON_OVERRIDE_VAR << "(Shiboken::BindingManager::instance().getOverride(this, nameCache, funcName));\n" - << "if (" << PYTHON_OVERRIDE_VAR << ".isNull()) {\n" - << indent << "gil.release();\n"; + << "if (" << PYTHON_OVERRIDE_VAR << ".isNull()) {\n" << indent; if (useOverrideCaching(func->ownerClass())) s << "m_PyMethodCache[" << cacheIndex << "] = true;\n"; writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType, - returnStatement); + returnStatement.statement, true); s << outdent << "}\n\n"; //WS - writeConversionRule(s, func, TypeSystem::TargetLangCode); - - s << "Shiboken::AutoDecRef " << PYTHON_ARGS << "("; + if (!snips.isEmpty()) { + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionPyOverride, + TypeSystem::ShellCode, func, false, lastArg); + } - if (func->arguments().isEmpty() || allArgumentsRemoved(func)) { - s << "PyTuple_New(0));\n"; - } else { - QStringList argConversions; - const AbstractMetaArgumentList &arguments = func->arguments(); - for (const AbstractMetaArgument &arg : arguments) { - if (func->argumentRemoved(arg.argumentIndex() + 1)) - continue; + writeVirtualMethodPythonOverride(s, func, snips, returnStatement); +} - const auto &argType = arg.type(); - auto argTypeEntry = static_cast<const PrimitiveTypeEntry *>(argType.typeEntry()); - bool convert = argTypeEntry->isObject() - || argTypeEntry->isValue() - || argType.isValuePointer() - || argType.isNativePointer() - || argTypeEntry->isFlags() - || argTypeEntry->isEnum() - || argTypeEntry->isContainer() - || argType.referenceType() == LValueReference; - - if (!convert && argTypeEntry->isPrimitive()) { - if (argTypeEntry->basicReferencedTypeEntry()) - argTypeEntry = argTypeEntry->basicReferencedTypeEntry(); - convert = !formatUnits().contains(argTypeEntry->name()); - } +void CppGenerator::writeVirtualMethodPythonOverride(TextStream &s, + const AbstractMetaFunctionCPtr &func, + const CodeSnipList &snips, + const VirtualMethodReturn &returnStatement) const +{ + writeConversionRule(s, func, TypeSystem::TargetLangCode, false); - StringStream ac(TextStream::Language::Cpp); - if (!func->conversionRule(TypeSystem::TargetLangCode, arg.argumentIndex() + 1).isEmpty()) { - // Has conversion rule. - ac << arg.name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX); + bool invalidateReturn = false; + QList<int> invalidateArgs; + for (const FunctionModification &funcMod : func->modifications()) { + for (const ArgumentModification &argMod : funcMod.argument_mods()) { + const int index = argMod.index(); + if (index == 0) { + if (argMod.targetOwnerShip() == TypeSystem::CppOwnership) + invalidateReturn = true; } else { - QString argName = arg.name(); - if (convert) - writeToPythonConversion(ac, arg.type(), func->ownerClass(), argName); - else - ac << argName; + const int actualIndex = func->actualArgumentIndex(index - 1) + 1; + if (argMod.resetAfterUse() && !invalidateArgs.contains(actualIndex)) + invalidateArgs.append(actualIndex); } - - argConversions << ac.toString(); } + } + std::sort(invalidateArgs.begin(), invalidateArgs.end()); - s << "Py_BuildValue(\"(" << getFormatUnitString(func, false) << ")\",\n" - << argConversions.join(QLatin1String(",\n")) << "\n));\n"; + auto arguments = func->arguments(); + auto removedEnd = std::stable_partition(arguments.begin(), arguments.end(), + isArgumentNotRemoved); + if (func->isAbstract()) { // Base function is not called, indicate unused arguments. + for (auto it = removedEnd; it != arguments.end(); ++it) + s << sbkUnusedVariableCast(it->name()); } + arguments.erase(removedEnd, arguments.end()); - bool invalidateReturn = false; - QSet<int> invalidateArgs; - for (const FunctionModification &funcMod : functionModifications) { - for (const ArgumentModification &argMod : funcMod.argument_mods()) { - const int index = argMod.index(); - if (argMod.resetAfterUse() && !invalidateArgs.contains(index)) { - invalidateArgs.insert(index); - s << "bool invalidateArg" << index - << " = PyTuple_GET_ITEM(" << PYTHON_ARGS << ", " - << index - 1 << ")->ob_refcnt == 1;\n"; - } else if (index == 0 && - argMod.targetOwnerShip() == TypeSystem::CppOwnership) { - invalidateReturn = true; - } + // FIXME PYSIDE-7: new functions PyObject_Vectorcall() (since 3.9) and + // PyObject_CallNoArgs() (since 3.9, stable API since 3.10) might have + // become part of the stable API? + + // Code snips might expect the args tuple, don't generate new code + const bool generateNewCall = snips.isEmpty(); + const qsizetype argCount = arguments.size(); + const char *newCallCondition = argCount == 0 ? noArgsCallCondition : vectorCallCondition; + if (generateNewCall) { + if (argCount > 0) { + s << newCallCondition; + writeVirtualMethodNativeVectorCallArgs(s, func, arguments, invalidateArgs); + s << "#else\n"; + } else { + s << inverseNoArgsCallCondition; } } + writeVirtualMethodNativeArgs(s, func, arguments, invalidateArgs); + if (generateNewCall) + s << "#endif\n"; s << '\n'; if (!snips.isEmpty()) { if (func->injectedCodeUsesPySelf()) s << "PyObject *pySelf = BindingManager::instance().retrieveWrapper(this);\n"; - const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast(); - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode, func, lastArg); + const AbstractMetaArgument *lastArg = func->arguments().isEmpty() + ? nullptr : &func->arguments().constLast(); + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, + TypeSystem::NativeCode, func, false, lastArg); } + qsizetype returnIndirections = 0; + if (!func->injectedCodeCallsPythonOverride()) { + if (generateNewCall) { + s << newCallCondition << "Shiboken::AutoDecRef " << PYTHON_RETURN_VAR << '('; + if (argCount > 0) { + s << "PyObject_Vectorcall(" << PYTHON_OVERRIDE_VAR << ", " + << PYTHON_ARGS_ARRAY << ", " << argCount << ", nullptr));\n"; + for (int argIndex : std::as_const(invalidateArgs)) { + s << "if (invalidateArg" << argIndex << ")\n" << indent + << "Shiboken::Object::invalidate(" << PYTHON_ARGS_ARRAY + << '[' << (argIndex - 1) << "]);\n" << outdent; + } + for (qsizetype i = 0, size = arguments.size(); i < size; ++i) + s << "Py_DECREF(" << PYTHON_ARGS_ARRAY << '[' << i << "]);\n"; + } else { + s << "PyObject_CallNoArgs(" << PYTHON_OVERRIDE_VAR << "));\n"; + } + s << "#else\n"; + } s << "Shiboken::AutoDecRef " << PYTHON_RETURN_VAR << "(PyObject_Call(" - << PYTHON_OVERRIDE_VAR << ", " << PYTHON_ARGS << ", nullptr));\n" + << PYTHON_OVERRIDE_VAR << ", " << PYTHON_ARGS << ", nullptr));\n"; + + for (int argIndex : std::as_const(invalidateArgs)) { + s << "if (invalidateArg" << argIndex << ")\n" << indent + << "Shiboken::Object::invalidate(PyTuple_GET_ITEM(" << PYTHON_ARGS + << ", " << (argIndex - 1) << "));\n" << outdent; + } + if (generateNewCall) + s << "#endif\n"; + + s << "if (" << PYTHON_RETURN_VAR << ".isNull()) {\n" << indent << "// An error happened in python code!\n" - << "if (" << PYTHON_RETURN_VAR << ".isNull()) {\n" << indent - << "PyErr_Print();\n" << returnStatement << '\n' << outdent - << "}\n"; + << "Shiboken::Errors::storePythonOverrideErrorOrPrint(\"" + << func->ownerClass()->name() << "\", funcName);\n" + << returnStatement.statement << "\n" << outdent + << "}\n"; + + if (invalidateReturn) { + s << "bool invalidateArg0 = Py_REFCNT(" << PYTHON_RETURN_VAR << ") == 1;\n" + << "if (invalidateArg0)\n" << indent + << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR + << ".object());\n" << outdent; + } if (!func->isVoid()) { - if (invalidateReturn) - s << "bool invalidateArg0 = " << PYTHON_RETURN_VAR << "->ob_refcnt == 1;\n"; - if (func->typeReplaced(0) != cPyObjectT()) { + if (func->modifiedTypeName() != cPyObjectT) { s << "// Check return type\n"; - if (func->typeReplaced(0).isEmpty()) { - s << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << " = " + + if (!func->isTypeModified()) { + + s << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' + << PYTHON_TO_CPP_VAR << " =\n" << indent << cpythonIsConvertibleFunction(func->type()) - << PYTHON_RETURN_VAR << ");\n" - << "if (!" << PYTHON_TO_CPP_VAR << ") {\n"; - { - Indentation indent(s); - s << "Shiboken::warning(PyExc_RuntimeWarning, 2, "\ - "\"Invalid return value in function %s, expected %s, got %s.\", \"" - << func->ownerClass()->name() << '.' << funcName << "\", " - << getVirtualFunctionReturnTypeName(func) - << ", Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);\n" - << returnStatement << '\n'; - } - s << "}\n"; + << PYTHON_RETURN_VAR << ");\n" << outdent + << "if (!" << PYTHON_TO_CPP_VAR << ") {\n" << indent + << "Shiboken::Warnings::warnInvalidReturnValue(\"" + << func->ownerClass()->name() << "\", funcName, " + << getVirtualFunctionReturnTypeName(func) << ", " + << "Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);\n" + << returnStatement.statement << '\n' << outdent + << "}\n"; } else { - s << "// Check return type\n" - << "bool typeIsValid = "; - writeTypeCheck(s, func->type(), QLatin1String(PYTHON_RETURN_VAR), - isNumber(func->type().typeEntry()), func->typeReplaced(0)); + s << "bool typeIsValid = "; + if (func->isTypeModified()) { + writeTypeCheck(s, func->modifiedTypeName(), PYTHON_RETURN_VAR); + } else { + const bool numberType = isNumber(func->type().typeEntry()); + writeTypeCheck(s, func->type(), PYTHON_RETURN_VAR, numberType); + } + s << ";\n"; s << "if (!typeIsValid"; if (func->type().isPointerToWrapperType()) s << " && " << PYTHON_RETURN_VAR << " != Py_None"; - s << ") {\n"; - { - Indentation indent(s); - s << "Shiboken::warning(PyExc_RuntimeWarning, 2, "\ - "\"Invalid return value in function %s, expected %s, got %s.\", \"" - << func->ownerClass()->name() << '.' << funcName << "\", " - << getVirtualFunctionReturnTypeName(func) - << ", Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);\n" - << returnStatement << '\n'; - } - s << "}\n"; + s << ") {\n" << indent + << "Shiboken::Warnings::warnInvalidReturnValue(\"" + << func->ownerClass()->name() << "\", funcName, " + << getVirtualFunctionReturnTypeName(func) << ", " + << "Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);\n" + << returnStatement.statement << '\n' << outdent + << "}\n"; } } - if (!func->conversionRule(TypeSystem::NativeCode, 0).isEmpty()) { - // Has conversion rule. - writeConversionRule(s, func, TypeSystem::NativeCode, QLatin1String(CPP_RETURN_VAR)); + if (func->hasConversionRule(TypeSystem::NativeCode, 0)) { + writeConversionRule(s, func, TypeSystem::NativeCode, CPP_RETURN_VAR); } else if (!func->injectedCodeHasReturnValueAttribution(TypeSystem::NativeCode)) { - writePythonToCppTypeConversion(s, func->type(), QLatin1String(PYTHON_RETURN_VAR), - QLatin1String(CPP_RETURN_VAR), func->implementingClass()); + returnIndirections = writePythonToCppTypeConversion( + s, func->type(), PYTHON_RETURN_VAR, + CPP_RETURN_VAR, func->implementingClass(), {}); } } } - if (invalidateReturn) { - s << "if (invalidateArg0)\n" << indent - << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR - << ".object());\n" << outdent; - } - for (int argIndex : qAsConst(invalidateArgs)) { - s << "if (invalidateArg" << argIndex << ")\n" << indent - << "Shiboken::Object::invalidate(PyTuple_GET_ITEM(" << PYTHON_ARGS - << ", " << (argIndex - 1) << "));\n" << outdent; - } - - - for (const FunctionModification &funcMod : functionModifications) { + for (const FunctionModification &funcMod : func->modifications()) { for (const ArgumentModification &argMod : funcMod.argument_mods()) { if (argMod.index() == 0 && argMod.nativeOwnership() == TypeSystem::CppOwnership) { - s << "if (Shiboken::Object::checkType(" << PYTHON_RETURN_VAR << "))\n"; - Indentation indent(s); - s << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR << ");\n"; + s << "if (Shiboken::Object::checkType(" << PYTHON_RETURN_VAR << "))\n" << indent + << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR << ");\n" + << outdent; } } } if (func->hasInjectedCode()) { s << '\n'; - const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast(); - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode, func, lastArg); + const AbstractMetaArgument *lastArg = func->arguments().isEmpty() + ? nullptr : &func->arguments().constLast(); + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, + TypeSystem::NativeCode, func, false, lastArg); } if (!func->isVoid()) { s << "return "; + TypeEntryCPtr retType = func->type().typeEntry(); if (avoidProtectedHack() && retType->isEnum()) { auto metaEnum = api().findAbstractMetaEnum(retType); bool isProtectedEnum = metaEnum.has_value() && metaEnum->isProtected(); if (isProtectedEnum) { QString typeCast; if (metaEnum->enclosingClass()) - typeCast += QLatin1String("::") + metaEnum->enclosingClass()->qualifiedCppName(); - typeCast += QLatin1String("::") + metaEnum->name(); + typeCast += getFullTypeName(metaEnum->enclosingClass()); + typeCast += u"::"_s + metaEnum->name(); s << '(' << typeCast << ')'; } } - if (func->type().referenceType() == LValueReference && !func->type().isPointer()) - s << " *"; + + if (returnIndirections > 0) + s << QByteArray(returnIndirections, '*'); s << CPP_RETURN_VAR << ";\n"; } s << outdent << "}\n\n"; } +void CppGenerator::writeUserAddedPythonOverride(TextStream &s, + const AbstractMetaFunctionCPtr &func) const +{ + TypeEntryCPtr retType = func->type().typeEntry(); + const QString funcName = func->isOperatorOverload() + ? pythonOperatorFunctionName(func) : func->definitionNames().constFirst(); + + const CodeSnipList snips = func->hasInjectedCode() + ? func->injectedCodeSnips() : CodeSnipList(); + + QString prefix = wrapperName(func->ownerClass()) + u"::"_s; + s << '\n' << functionSignature(func, prefix, QString(), Generator::SkipDefaultValues | + Generator::OriginalTypeDescription) + << "\n{\n" << indent << sbkUnusedVariableCast("gil"); + + writeFuncNameVar(s, func, funcName); + + const auto returnStatement = virtualMethodReturn(api(), func, + func->modifications()); + writeVirtualMethodPythonOverride(s, func, snips, returnStatement); +} + void CppGenerator::writeMetaObjectMethod(TextStream &s, const GeneratorContext &classContext) const { @@ -1280,19 +1476,21 @@ void CppGenerator::writeMetaObjectMethod(TextStream &s, const QString wrapperClassName = classContext.wrapperName(); const QString qualifiedCppName = classContext.metaClass()->qualifiedCppName(); s << "const QMetaObject *" << wrapperClassName << "::metaObject() const\n{\n"; - s << indent << "if (QObject::d_ptr->metaObject)\n" + s << indent << "if (QObject::d_ptr->metaObject != nullptr)\n" << indent << "return QObject::d_ptr->dynamicMetaObject();\n" << outdent << "SbkObject *pySelf = Shiboken::BindingManager::instance().retrieveWrapper(this);\n" << "if (pySelf == nullptr)\n" << indent << "return " << qualifiedCppName << "::metaObject();\n" << outdent - << "return PySide::SignalManager::retrieveMetaObject(reinterpret_cast<PyObject *>(pySelf));\n" + << "return PySide::SignalManager::retrieveMetaObject(" + "reinterpret_cast<PyObject *>(pySelf));\n" << outdent << "}\n\n"; // qt_metacall function - s << "int " << wrapperClassName << "::qt_metacall(QMetaObject::Call call, int id, void **args)\n"; + s << "int " << wrapperClassName + << "::qt_metacall(QMetaObject::Call call, int id, void **args)\n"; s << "{\n" << indent; - const auto list = classContext.metaClass()->queryFunctionsByName(QLatin1String("qt_metacall")); + const auto list = classContext.metaClass()->queryFunctionsByName(u"qt_metacall"_s); CodeSnipList snips; if (list.size() == 1) { @@ -1300,12 +1498,15 @@ void CppGenerator::writeMetaObjectMethod(TextStream &s, snips = func->injectedCodeSnips(); if (func->isUserAdded()) { CodeSnipList snips = func->injectedCodeSnips(); - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionAny, TypeSystem::NativeCode, func); + const bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(func); + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionAny, + TypeSystem::NativeCode, func, usePyArgs, nullptr); } } s << "int result = " << qualifiedCppName << "::qt_metacall(call, id, args);\n" - << "return result < 0 ? result : PySide::SignalManager::qt_metacall(this, call, id, args);\n" + << "return result < 0 ? result : PySide::SignalManager::qt_metacall(" + "this, call, id, args);\n" << outdent << "}\n\n"; // qt_metacast function @@ -1318,108 +1519,117 @@ void CppGenerator::writeMetaCast(TextStream &s, const QString wrapperClassName = classContext.wrapperName(); const QString qualifiedCppName = classContext.metaClass()->qualifiedCppName(); s << "void *" << wrapperClassName << "::qt_metacast(const char *_clname)\n{\n" - << indent << "if (!_clname)\n" << indent << "return {};\n" << outdent + << indent << "if (_clname == nullptr)\n" << indent << "return {};\n" << outdent << "SbkObject *pySelf = Shiboken::BindingManager::instance().retrieveWrapper(this);\n" - << "if (pySelf && PySide::inherits(Py_TYPE(pySelf), _clname))\n" + << "if (pySelf != nullptr && PySide::inherits(Py_TYPE(pySelf), _clname))\n" << indent << "return static_cast<void *>(const_cast< " << wrapperClassName << " *>(this));\n" << outdent << "return " << qualifiedCppName << "::qt_metacast(_clname);\n" << outdent << "}\n\n"; } -void CppGenerator::writeEnumConverterFunctions(TextStream &s, const AbstractMetaEnum &metaEnum) const +static void generateDeprecatedValueWarnings(TextStream &c, + const AbstractMetaEnum &metaEnum, + bool useSurrogateName) { - if (metaEnum.isPrivate() || metaEnum.isAnonymous()) - return; - writeEnumConverterFunctions(s, metaEnum.typeEntry()); + EnumTypeEntryCPtr enumType = metaEnum.typeEntry(); + const QString prefix = enumType->qualifiedCppName() + u"::"_s; + c << "switch (value) {\n"; + const auto &deprecatedValues = metaEnum.deprecatedValues(); + for (const auto &v : deprecatedValues) { + c << "case "; + if (useSurrogateName) + c << v.value().toString(); // Protected, use int representation + else + c << prefix << v.name(); + c << ":\n" << indent + << "Shiboken::Warnings::warnDeprecatedEnumValue(\"" << enumType->name() + << "\", \"" << v.name() << "\");\nbreak;\n" << outdent; + } + if (deprecatedValues.size() < metaEnum.values().size()) + c << "default:\n" << indent << "break;\n" << outdent; + c << "}\n"; } -void CppGenerator::writeEnumConverterFunctions(TextStream &s, const TypeEntry *enumType) const +void CppGenerator::writeEnumConverterFunctions(TextStream &s, const AbstractMetaEnum &metaEnum) const { - if (!enumType) + if (metaEnum.isPrivate() || metaEnum.isAnonymous()) return; + EnumTypeEntryCPtr enumType = metaEnum.typeEntry(); + Q_ASSERT(enumType); QString typeName = fixedCppTypeName(enumType); QString enumPythonType = cpythonTypeNameExt(enumType); - QString cppTypeName = getFullTypeName(enumType).trimmed(); - if (avoidProtectedHack()) { - auto metaEnum = api().findAbstractMetaEnum(enumType); - if (metaEnum.has_value() && metaEnum->isProtected()) - cppTypeName = protectedEnumSurrogateName(metaEnum.value()); - } + const bool useSurrogateName = avoidProtectedHack() && metaEnum.isProtected(); + QString cppTypeName = useSurrogateName + ? protectedEnumSurrogateName(metaEnum) : getFullTypeName(enumType).trimmed(); + StringStream c(TextStream::Language::Cpp); - c << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n" - << " "; - if (enumType->isFlags()) - c << cppTypeName << "(QFlag(int(PySide::QFlags::getValue(reinterpret_cast<PySideQFlagsObject *>(pyIn)))))"; - else - c << "static_cast<" << cppTypeName << ">(Shiboken::Enum::getValue(pyIn))"; - c << ";\n"; + if (metaEnum.isDeprecated()) + c << "Shiboken::Warnings::warnDeprecatedEnum(\"" << enumType->name() << "\");\n"; + + c << "const auto value = static_cast<" << cppTypeName + << ">(Shiboken::Enum::getValue(pyIn));\n"; + + // Warn about deprecated values unless it is protected+scoped (inaccessible values) + const bool valuesAcccessible = !useSurrogateName || metaEnum.enumKind() != EnumClass; + if (valuesAcccessible && metaEnum.hasDeprecatedValues()) + generateDeprecatedValueWarnings(c, metaEnum, useSurrogateName); + + c << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) = value;\n"; + + ConfigurableScope configScope(s, enumType); writePythonToCppFunction(s, c.toString(), typeName, typeName); - QString pyTypeCheck = QStringLiteral("PyObject_TypeCheck(pyIn, %1)").arg(enumPythonType); + QString pyTypeCheck = u"PyObject_TypeCheck(pyIn, "_s + enumPythonType + u')'; writeIsPythonConvertibleToCppFunction(s, typeName, typeName, pyTypeCheck); c.clear(); c << "const int castCppIn = int(*reinterpret_cast<const " - << cppTypeName << " *>(cppIn));\n" << "return "; - if (enumType->isFlags()) { - c << "reinterpret_cast<PyObject *>(PySide::QFlags::newObject(castCppIn, " - << enumPythonType << "))"; - } else { - c << "Shiboken::Enum::newItem(" << enumPythonType << ", castCppIn)"; - } - c << ";\n"; + << cppTypeName << " *>(cppIn));\n" << "return " + << "Shiboken::Enum::newItem(" << enumPythonType << ", castCppIn);\n"; writeCppToPythonFunction(s, c.toString(), typeName, typeName); s << '\n'; +} - if (enumType->isFlags()) - return; +static void writePointerToPythonConverter(TextStream &c, + const AbstractMetaClassCPtr &metaClass, + const QString &typeName, + const QString &cpythonType) +{ + c << "auto *pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));\n" + << "if (pyOut) {\n" << indent + << "Py_INCREF(pyOut);\nreturn pyOut;\n" << outdent + << "}\n"; - auto flags = reinterpret_cast<const EnumTypeEntry *>(enumType)->flags(); - if (!flags) + const QString nameFunc = metaClass->typeEntry()->polymorphicNameFunction(); + if (nameFunc.isEmpty() && !metaClass->hasVirtualDestructor()) { + c << "return Shiboken::Object::newObjectWithHeuristics(" + << cpythonType << ", const_cast<void *>(cppIn), false);\n"; return; + } - // QFlags part. - - writeEnumConverterFunctions(s, flags); - - c.clear(); - cppTypeName = getFullTypeName(flags).trimmed(); - c << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n" - << " " << cppTypeName - << "(QFlag(int(Shiboken::Enum::getValue(pyIn))));\n"; - - QString flagsTypeName = fixedCppTypeName(flags); - writePythonToCppFunction(s, c.toString(), typeName, flagsTypeName); - writeIsPythonConvertibleToCppFunction(s, typeName, flagsTypeName, pyTypeCheck); + c << "auto *tCppIn = reinterpret_cast<const " << typeName << R"( *>(cppIn); +const char *typeName = )"; + if (nameFunc.isEmpty()) + c << "typeid(*tCppIn).name();\n"; + else + c << nameFunc << "(tCppIn);\n"; + c << "return Shiboken::Object::newObjectForPointer(" + << cpythonType << ", const_cast<void *>(cppIn), false, typeName);\n"; +} - c.clear(); - c << "Shiboken::AutoDecRef pyLong(PyNumber_Long(pyIn));\n" - << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) =\n" - << " " << cppTypeName - << "(QFlag(int(PyLong_AsLong(pyLong.object()))));\n"; - // PYSIDE-898: Include an additional condition to detect if the type of the - // enum corresponds to the object that is being evaluated. - // Using only `PyNumber_Check(...)` is too permissive, - // then we would have been unable to detect the difference between - // a PolarOrientation and Qt::AlignmentFlag, which was the main - // issue of the bug. - const QString numberCondition = QStringLiteral("PyNumber_Check(pyIn) && ") + pyTypeCheck; - writePythonToCppFunction(s, c.toString(), QLatin1String("number"), flagsTypeName); - writeIsPythonConvertibleToCppFunction(s, QLatin1String("number"), flagsTypeName, numberCondition); -} - -void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClass *metaClass, +void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext) const { s << "// Type conversion functions.\n\n"; AbstractMetaEnumList classEnums = metaClass->enums(); + auto typeEntry = metaClass->typeEntry(); metaClass->getEnumsFromInvisibleNamespacesToBeGenerated(&classEnums); if (!classEnums.isEmpty()) s << "// Python to C++ enum conversion.\n"; - for (const AbstractMetaEnum &metaEnum : qAsConst(classEnums)) + for (const AbstractMetaEnum &metaEnum : std::as_const(classEnums)) writeEnumConverterFunctions(s, metaEnum); if (metaClass->isNamespace()) @@ -1437,123 +1647,102 @@ void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClas s << "// Python to C++ pointer conversion - returns the C++ object of the Python wrapper (keeps object identity).\n"; QString sourceTypeName = metaClass->name(); - QString targetTypeName = metaClass->name() + QLatin1String("_PTR"); + QString targetTypeName = metaClass->name() + u"_PTR"_s; StringStream c(TextStream::Language::Cpp); c << "Shiboken::Conversions::pythonToCppPointer(" << cpythonType << ", pyIn, cppOut);"; writePythonToCppFunction(s, c.toString(), sourceTypeName, targetTypeName); // "Is convertible" function for the Python object to C++ pointer conversion. - const QString pyTypeCheck = QLatin1String("PyObject_TypeCheck(pyIn, reinterpret_cast<PyTypeObject *>(") - + cpythonType + QLatin1String("))"); + const QString pyTypeCheck = u"PyObject_TypeCheck(pyIn, "_s + cpythonType + u")"_s; writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, pyTypeCheck, QString(), true); s << '\n'; // C++ pointer to a Python wrapper, keeping identity. s << "// C++ to Python pointer conversion - tries to find the Python wrapper for the C++ object (keeps object identity).\n"; c.clear(); - if (usePySideExtensions() && metaClass->isQObject()) { + if (usePySideExtensions() && isQObject(metaClass)) { c << "return PySide::getWrapperForQObject(reinterpret_cast<" << typeName << " *>(const_cast<void *>(cppIn)), " << cpythonType << ");\n"; } else { - c << "auto pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));\n" - << "if (pyOut) {\n"; - { - Indentation indent(c); - c << "Py_INCREF(pyOut);\nreturn pyOut;\n"; - } - c << "}\n" - << "bool changedTypeName = false;\n" - << "auto tCppIn = reinterpret_cast<const " << typeName << R"( *>(cppIn); -const char *typeName = typeid(*tCppIn).name(); -auto sbkType = Shiboken::ObjectType::typeForTypeName(typeName); -if (sbkType && Shiboken::ObjectType::hasSpecialCastFunction(sbkType)) { - typeName = typeNameOf(tCppIn); - changedTypeName = true; -} -)" - << "PyObject *result = Shiboken::Object::newObject(" << cpythonType - << R"(, const_cast<void *>(cppIn), false, /* exactType */ changedTypeName, typeName); -if (changedTypeName) - delete [] typeName; -return result;)"; + writePointerToPythonConverter(c, metaClass, typeName, cpythonType); } std::swap(targetTypeName, sourceTypeName); writeCppToPythonFunction(s, c.toString(), sourceTypeName, targetTypeName); // The conversions for an Object Type end here. - if (!metaClass->typeEntry()->isValue() && !metaClass->typeEntry()->isSmartPointer()) { + if (!typeEntry->isValue() && !typeEntry->isSmartPointer()) { s << '\n'; return; } // Always copies C++ value (not pointer, and not reference) to a new Python wrapper. s << '\n' << "// C++ to Python copy conversion.\n"; - if (!classContext.forSmartPointer()) - targetTypeName = metaClass->name(); - else - targetTypeName = classContext.preciseType().name(); + targetTypeName = metaClass->name(); - sourceTypeName = targetTypeName + QLatin1String("_COPY"); + sourceTypeName = targetTypeName + u"_COPY"_s; c.clear(); - QString computedWrapperName; - if (!classContext.forSmartPointer()) { - computedWrapperName = classContext.useWrapper() - ? classContext.wrapperName() : metaClass->qualifiedCppName(); + const bool isUniquePointer = classContext.forSmartPointer() + && typeEntry->isUniquePointer(); + + if (isUniquePointer) { + c << "auto *source = reinterpret_cast<" << typeName + << " *>(const_cast<void *>(cppIn));\n"; } else { - computedWrapperName = classContext.smartPointerWrapperName(); + c << "auto *source = reinterpret_cast<const " << typeName << " *>(cppIn);\n"; } - c << "return Shiboken::Object::newObject(" << cpythonType - << ", new ::" << computedWrapperName << "(*reinterpret_cast<const " - << typeName << " *>(cppIn)), true, true);"; + << ", new " << globalScopePrefix(classContext) << classContext.effectiveClassName() << '(' + << (isUniquePointer ? "std::move(*source)" : "*source") + << "), true, true);"; writeCppToPythonFunction(s, c.toString(), sourceTypeName, targetTypeName); s << '\n'; // Python to C++ copy conversion. s << "// Python to C++ copy conversion.\n"; - if (!classContext.forSmartPointer()) - sourceTypeName = metaClass->name(); - else - sourceTypeName = classContext.preciseType().name(); + sourceTypeName = metaClass->name(); - targetTypeName = sourceTypeName + QStringLiteral("_COPY"); + targetTypeName = sourceTypeName + "_COPY"_L1; c.clear(); - QString pyInVariable = QLatin1String("pyIn"); - QString wrappedCPtrExpression; - if (!classContext.forSmartPointer()) - wrappedCPtrExpression = cpythonWrapperCPtr(metaClass->typeEntry(), pyInVariable); - else - wrappedCPtrExpression = cpythonWrapperCPtr(classContext.preciseType(), pyInVariable); + QString pyInVariable = u"pyIn"_s; + const QString outPtr = u"reinterpret_cast<"_s + typeName + u" *>(cppOut)"_s; + if (!classContext.forSmartPointer()) { + c << '*' << outPtr << " = *" + << cpythonWrapperCPtr(typeEntry, pyInVariable) << ';'; + } else { + auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(typeEntry); + const QString resetMethod = ste->resetMethod(); + c << "auto *ptr = " << outPtr << ";\n"; + c << "if (" << pyInVariable << " == Py_None)\n" << indent; + if (resetMethod.isEmpty()) + c << "*ptr = {};\n"; + else + c << "ptr->" << resetMethod << "();\n"; + const QString value = u'*' + cpythonWrapperCPtr(classContext.preciseType(), pyInVariable); + c << outdent << "else\n" << indent + << "*ptr = " << (isUniquePointer ? stdMove(value) : value) << ';'; + } - c << "*reinterpret_cast<" << typeName << " *>(cppOut) = *" - << wrappedCPtrExpression << ';'; writePythonToCppFunction(s, c.toString(), sourceTypeName, targetTypeName); // "Is convertible" function for the Python object to C++ value copy conversion. - writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, pyTypeCheck); + QString copyTypeCheck = pyTypeCheck; + if (classContext.forSmartPointer()) + copyTypeCheck.prepend(pyInVariable + u" == Py_None || "_s); + writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, copyTypeCheck); s << '\n'; // User provided implicit conversions. - CustomConversion *customConversion = metaClass->typeEntry()->customConversion(); - // Implicit conversions. - AbstractMetaFunctionCList implicitConvs; - if (!customConversion || !customConversion->replaceOriginalTargetToNativeConversions()) { - const auto &allImplicitConvs = api().implicitConversions(metaClass->typeEntry()); - for (const auto &func : allImplicitConvs) { - if (!func->isUserAdded()) - implicitConvs << func; - } - } + const AbstractMetaFunctionCList implicitConvs = implicitConversions(typeEntry); if (!implicitConvs.isEmpty()) s << "// Implicit conversions.\n"; - AbstractMetaType targetType = buildAbstractMetaTypeFromAbstractMetaClass(metaClass); - for (const auto &conv : qAsConst(implicitConvs)) { + AbstractMetaType targetType = AbstractMetaType::fromAbstractMetaClass(metaClass); + for (const auto &conv : std::as_const(implicitConvs)) { if (conv->isModifiedRemoved()) continue; @@ -1561,35 +1750,30 @@ return result;)"; QString toCppConv; QString toCppPreConv; if (conv->isConversionOperator()) { - const AbstractMetaClass *sourceClass = conv->ownerClass(); - typeCheck = QStringLiteral("PyObject_TypeCheck(pyIn, %1)").arg(cpythonTypeNameExt(sourceClass->typeEntry())); - toCppConv = QLatin1Char('*') + cpythonWrapperCPtr(sourceClass->typeEntry(), QLatin1String("pyIn")); + const auto sourceClass = conv->ownerClass(); + typeCheck = u"PyObject_TypeCheck(pyIn, "_s + + cpythonTypeNameExt(sourceClass->typeEntry()) + u')'; + toCppConv = u'*' + cpythonWrapperCPtr(sourceClass->typeEntry(), + pyInVariable); } else { // Constructor that does implicit conversion. - if (!conv->typeReplaced(1).isEmpty() || conv->isModifiedToArray(1)) + const auto &firstArg = conv->arguments().constFirst(); + if (firstArg.isTypeModified() || conv->isModifiedToArray(1)) continue; - const AbstractMetaType sourceType = conv->arguments().constFirst().type(); - typeCheck = cpythonCheckFunction(sourceType); - bool isUserPrimitiveWithoutTargetLangName = sourceType.isUserPrimitive() - && sourceType.typeEntry()->targetLangApiName() == sourceType.typeEntry()->name(); - if (!sourceType.isWrapperType() - && !isUserPrimitiveWithoutTargetLangName - && !sourceType.typeEntry()->isEnum() - && !sourceType.typeEntry()->isFlags() - && !sourceType.typeEntry()->isContainer()) { - typeCheck += QLatin1Char('('); - } + const AbstractMetaType &sourceType = firstArg.type(); if (sourceType.isWrapperType()) { - typeCheck += QLatin1String("pyIn)"); - toCppConv = (sourceType.referenceType() == LValueReference - || !sourceType.isPointerToWrapperType()) - ? QLatin1String(" *") : QString(); - toCppConv += cpythonWrapperCPtr(sourceType.typeEntry(), QLatin1String("pyIn")); - } else if (typeCheck.contains(QLatin1String("%in"))) { - typeCheck.replace(QLatin1String("%in"), QLatin1String("pyIn")); - typeCheck.append(QLatin1Char(')')); - } else { - typeCheck += QLatin1String("pyIn)"); + if (sourceType.referenceType() == LValueReference + || !sourceType.isPointerToWrapperType()) { + toCppConv = u" *"_s; + } + toCppConv += cpythonWrapperCPtr(sourceType.typeEntry(), pyInVariable); + } + + typeCheck = cpythonCheckFunction(sourceType); + if (typeCheck.endsWith(u", ")) { + typeCheck += pyInVariable + u')'; + } else if (typeCheck != u"true" && typeCheck != u"false") { + typeCheck += u'(' + pyInVariable + u')'; } if (sourceType.isUserPrimitive() @@ -1598,81 +1782,81 @@ return result;)"; || sourceType.typeEntry()->isEnum() || sourceType.typeEntry()->isFlags()) { StringStream pc(TextStream::Language::Cpp); - pc << getFullTypeNameWithoutModifiers(sourceType) << " cppIn"; - writeMinimalConstructorExpression(pc, api(), sourceType); - pc << ";\n"; - writeToCppConversion(pc, sourceType, nullptr, QLatin1String("pyIn"), QLatin1String("cppIn")); + pc << getFullTypeNameWithoutModifiers(sourceType) << " cppIn" + << minimalConstructorExpression(api(), sourceType) << ";\n"; + writeToCppConversion(pc, sourceType, pyInVariable, + u"cppIn"_s); pc << ';'; toCppPreConv = pc.toString(); - toCppConv.append(QLatin1String("cppIn")); + toCppConv.append(u"cppIn"_s); } else if (!sourceType.isWrapperType()) { StringStream tcc(TextStream::Language::Cpp); - writeToCppConversion(tcc, sourceType, metaClass, QLatin1String("pyIn"), QLatin1String("/*BOZO-1061*/")); + writeToCppConversion(tcc, sourceType, pyInVariable, + u"/*BOZO-1061*/"_s); toCppConv = tcc.toString(); } } const AbstractMetaType sourceType = conv->isConversionOperator() - ? buildAbstractMetaTypeFromAbstractMetaClass(conv->ownerClass()) + ? AbstractMetaType::fromAbstractMetaClass(conv->ownerClass()) : conv->arguments().constFirst().type(); writePythonToCppConversionFunctions(s, sourceType, targetType, typeCheck, toCppConv, toCppPreConv); } - writeCustomConverterFunctions(s, customConversion); + if (typeEntry->isValue()) { + auto vte = std::static_pointer_cast<const ValueTypeEntry>(typeEntry); + writeCustomConverterFunctions(s, vte->customConversion()); + } } void CppGenerator::writeCustomConverterFunctions(TextStream &s, - const CustomConversion *customConversion) const + const CustomConversionPtr &customConversion) const { if (!customConversion) return; - const CustomConversion::TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions(); + const TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions(); if (toCppConversions.isEmpty()) return; - s << "// Python to C++ conversions for type '" << customConversion->ownerType()->qualifiedCppName() << "'.\n"; - for (CustomConversion::TargetToNativeConversion *toNative : toCppConversions) - writePythonToCppConversionFunctions(s, toNative, customConversion->ownerType()); + auto ownerType = customConversion->ownerType(); + s << "// Python to C++ conversions for type '" << ownerType->qualifiedCppName() << "'.\n"; + for (const auto &toNative : toCppConversions) + writePythonToCppConversionFunctions(s, toNative, ownerType); s << '\n'; } -void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClass *metaClass, +void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext) const { - if (metaClass->isNamespace()) + const auto typeEntry = metaClass->typeEntry(); + if (typeEntry->isNamespace()) return; s << "// Register Converter\n" - << "SbkConverter *converter = Shiboken::Conversions::createConverter(" - << cpythonTypeName(metaClass) << ',' << '\n'; - { - Indentation indent(s); - QString sourceTypeName = metaClass->name(); - QString targetTypeName = sourceTypeName + QLatin1String("_PTR"); - s << pythonToCppFunctionName(sourceTypeName, targetTypeName) << ',' << '\n' - << convertibleToCppFunctionName(sourceTypeName, targetTypeName) << ',' << '\n'; - std::swap(targetTypeName, sourceTypeName); + << "SbkConverter *converter = Shiboken::Conversions::createConverter(pyType,\n" + << indent; + QString sourceTypeName = metaClass->name(); + QString targetTypeName = sourceTypeName + u"_PTR"_s; + s << pythonToCppFunctionName(sourceTypeName, targetTypeName) << ',' << '\n' + << convertibleToCppFunctionName(sourceTypeName, targetTypeName) << ',' << '\n'; + std::swap(targetTypeName, sourceTypeName); + s << cppToPythonFunctionName(sourceTypeName, targetTypeName); + if (typeEntry->isValue() || typeEntry->isSmartPointer()) { + s << ',' << '\n'; + sourceTypeName = metaClass->name() + u"_COPY"_s; s << cppToPythonFunctionName(sourceTypeName, targetTypeName); - if (metaClass->typeEntry()->isValue() || metaClass->typeEntry()->isSmartPointer()) { - s << ',' << '\n'; - sourceTypeName = metaClass->name() + QLatin1String("_COPY"); - s << cppToPythonFunctionName(sourceTypeName, targetTypeName); - } } - s << ");\n"; - - s << '\n'; + s << outdent << ");\n\n"; auto writeConversions = [&s](const QString &signature) { - s << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "\");\n" - << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "*\");\n" - << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "&\");\n"; + s << registerConverterName(signature) << registerConverterName(signature + u'*') + << registerConverterName(signature + u'&'); }; auto writeConversionsForType = [writeConversions](const QString &fullTypeName) { - QStringList lst = fullTypeName.split(QLatin1String("::"), + QStringList lst = fullTypeName.split(u"::"_s, Qt::SkipEmptyParts); while (!lst.isEmpty()) { - QString signature = lst.join(QLatin1String("::")); + QString signature = lst.join(u"::"_s); writeConversions(signature); lst.removeFirst(); } @@ -1685,18 +1869,18 @@ void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClass const QString &smartPointerType = classContext.preciseType().instantiations().at(0).cppSignature(); const QString &smartPointerName = classContext.preciseType().typeEntry()->name(); - QStringList lst = smartPointerType.split(QLatin1String("::"), + QStringList lst = smartPointerType.split(u"::"_s, Qt::SkipEmptyParts); while (!lst.isEmpty()) { - QString signature = lst.join(QLatin1String("::")); - writeConversions(QStringLiteral("%1<%2 >").arg(smartPointerName, signature)); + QString signature = lst.join(u"::"_s); + writeConversions(smartPointerName + u'<' + signature + u'>'); lst.removeFirst(); } writeConversionsForType(smartPointerType); } - s << "Shiboken::Conversions::registerConverterName(converter, typeid(::"; + s << "Shiboken::Conversions::registerConverterName(converter, typeid(" << m_gsp; QString qualifiedCppNameInvocation; if (!classContext.forSmartPointer()) qualifiedCppNameInvocation = metaClass->qualifiedCppName(); @@ -1706,70 +1890,66 @@ void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClass s << qualifiedCppNameInvocation << ").name());\n"; if (classContext.useWrapper()) { - s << "Shiboken::Conversions::registerConverterName(converter, typeid(::" + s << "Shiboken::Conversions::registerConverterName(converter, typeid(" << classContext.wrapperName() << ").name());\n"; } - s << '\n'; - - if (!metaClass->typeEntry()->isValue() && !metaClass->typeEntry()->isSmartPointer()) + if (!typeEntry->isValue() && !typeEntry->isSmartPointer()) return; // Python to C++ copy (value, not pointer neither reference) conversion. - s << "// Add Python to C++ copy (value, not pointer neither reference) conversion to type converter.\n"; - QString sourceTypeName = metaClass->name(); - QString targetTypeName = sourceTypeName + QLatin1String("_COPY"); + s << "\n// Add Python to C++ copy (value, not pointer neither reference) conversion to type converter.\n"; + sourceTypeName = metaClass->name(); + targetTypeName = sourceTypeName + u"_COPY"_s; QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName); - writeAddPythonToCppConversion(s, QLatin1String("converter"), toCpp, isConv); + writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv); // User provided implicit conversions. - CustomConversion *customConversion = metaClass->typeEntry()->customConversion(); // Add implicit conversions. - AbstractMetaFunctionCList implicitConvs; - if (!customConversion || !customConversion->replaceOriginalTargetToNativeConversions()) { - const auto &allImplicitConvs = api().implicitConversions(metaClass->typeEntry()); - for (const auto &func : allImplicitConvs) { - if (!func->isUserAdded()) - implicitConvs << func; - } - } + const AbstractMetaFunctionCList implicitConvs = implicitConversions(typeEntry); if (!implicitConvs.isEmpty()) s << "// Add implicit conversions to type converter.\n"; - AbstractMetaType targetType = buildAbstractMetaTypeFromAbstractMetaClass(metaClass); - for (const auto &conv : qAsConst(implicitConvs)) { + AbstractMetaType targetType = AbstractMetaType::fromAbstractMetaClass(metaClass); + for (const auto &conv : std::as_const(implicitConvs)) { if (conv->isModifiedRemoved()) continue; AbstractMetaType sourceType; if (conv->isConversionOperator()) { - sourceType = buildAbstractMetaTypeFromAbstractMetaClass(conv->ownerClass()); + sourceType = AbstractMetaType::fromAbstractMetaClass(conv->ownerClass()); } else { // Constructor that does implicit conversion. - if (!conv->typeReplaced(1).isEmpty() || conv->isModifiedToArray(1)) + const auto &firstArg = conv->arguments().constFirst(); + if (firstArg.isTypeModified() || conv->isModifiedToArray(1)) continue; - sourceType = conv->arguments().constFirst().type(); + sourceType = firstArg.type(); } QString toCpp = pythonToCppFunctionName(sourceType, targetType); QString isConv = convertibleToCppFunctionName(sourceType, targetType); - writeAddPythonToCppConversion(s, QLatin1String("converter"), toCpp, isConv); + writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv); } - writeCustomConverterRegister(s, customConversion, QLatin1String("converter")); + if (typeEntry->isValue()) { + auto vte = std::static_pointer_cast<const ValueTypeEntry>(typeEntry); + writeCustomConverterRegister(s, vte->customConversion(), u"converter"_s); + } } -void CppGenerator::writeCustomConverterRegister(TextStream &s, const CustomConversion *customConversion, +void CppGenerator::writeCustomConverterRegister(TextStream &s, + const CustomConversionPtr &customConversion, const QString &converterVar) { if (!customConversion) return; - const CustomConversion::TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions(); + const TargetToNativeConversions &toCppConversions = + customConversion->targetToNativeConversions(); if (toCppConversions.isEmpty()) return; s << "// Add user defined implicit conversions to type converter.\n"; - for (CustomConversion::TargetToNativeConversion *toNative : toCppConversions) { + for (const auto &toNative : toCppConversions) { QString toCpp = pythonToCppFunctionName(toNative, customConversion->ownerType()); QString isConv = convertibleToCppFunctionName(toNative, customConversion->ownerType()); writeAddPythonToCppConversion(s, converterVar, toCpp, isConv); @@ -1783,38 +1963,25 @@ void CppGenerator::writeContainerConverterFunctions(TextStream &s, writePythonToCppConversionFunctions(s, containerType); } -void CppGenerator::writeSmartPointerConverterFunctions(TextStream &s, - const AbstractMetaType &smartPointerType) const +bool CppGenerator::needsArgumentErrorHandling(const OverloadData &overloadData) { - auto targetClass = AbstractMetaClass::findClass(api().classes(), - smartPointerType.instantiations().at(0).typeEntry()); - - if (targetClass) { - const auto *smartPointerTypeEntry = - static_cast<const SmartPointerTypeEntry *>( - smartPointerType.typeEntry()); - - // TODO: Missing conversion to smart pointer pointer type: - - s << "// Register smartpointer conversion for all derived classes\n"; - const auto classes = targetClass->typeSystemBaseClasses(); - for (auto k : classes) { - if (smartPointerTypeEntry->matchesInstantiation(k->typeEntry())) { - if (auto smartTargetType = findSmartPointerInstantiation(k->typeEntry())) { - s << "// SmartPointer derived class: " - << smartTargetType->cppSignature() << "\n"; - writePythonToCppConversionFunctions(s, smartPointerType, smartTargetType.value(), {}, {}, {}); - } - } - } - } + if (overloadData.maxArgs() > 0) + return true; + // QObject constructors need error handling when passing properties as kwarg. + if (!usePySideExtensions()) + return false; + auto rfunc = overloadData.referenceFunction(); + return rfunc->functionType() == AbstractMetaFunction::ConstructorFunction + && isQObject(rfunc->ownerClass()); } -void CppGenerator::writeMethodWrapperPreamble(TextStream &s, OverloadData &overloadData, - const GeneratorContext &context) const +void CppGenerator::writeMethodWrapperPreamble(TextStream &s, + const OverloadData &overloadData, + const GeneratorContext &context, + ErrorReturn errorReturn) { const auto rfunc = overloadData.referenceFunction(); - const AbstractMetaClass *ownerClass = rfunc->targetLangOwner(); + const auto ownerClass = rfunc->targetLangOwner(); Q_ASSERT(ownerClass == context.metaClass()); int minArgs = overloadData.minArgs(); int maxArgs = overloadData.maxArgs(); @@ -1824,32 +1991,31 @@ void CppGenerator::writeMethodWrapperPreamble(TextStream &s, OverloadData &overl if (rfunc->isConstructor()) { // Check if the right constructor was called. if (!ownerClass->hasPrivateDestructor()) { - s << "if (Shiboken::Object::isUserType(self) && !Shiboken::ObjectType::canCallConstructor(self->ob_type, Shiboken::SbkType< ::"; + s << "if (Shiboken::Object::isUserType(self) && " + << "!Shiboken::ObjectType::canCallConstructor(self->ob_type, Shiboken::SbkType< " + << m_gsp; QString qualifiedCppName; if (!context.forSmartPointer()) qualifiedCppName = ownerClass->qualifiedCppName(); else qualifiedCppName = context.preciseType().cppSignature(); - s << qualifiedCppName << " >()))\n"; - Indentation indent(s); - s << returnStatement(m_currentErrorCode) << '\n' << '\n'; + s << qualifiedCppName << " >()))\n" << indent << errorReturn << outdent << '\n'; } // Declare pointer for the underlying C++ object. - s << "::"; - if (!context.forSmartPointer()) { - s << (context.useWrapper() ? context.wrapperName() : ownerClass->qualifiedCppName()); - } else { - s << context.smartPointerWrapperName(); - } - s << " *cptr{};\n"; + s << globalScopePrefix(context) << context.effectiveClassName() << " *cptr{};\n"; initPythonArguments = maxArgs > 0; } else { if (rfunc->implementingClass() && (!rfunc->implementingClass()->isNamespace() && overloadData.hasInstanceFunction())) { - writeCppSelfDefinition(s, rfunc, context, overloadData.hasStaticFunction()); + CppSelfDefinitionFlags flags; + if (overloadData.hasStaticFunction()) + flags.setFlag(CppSelfDefinitionFlag::HasStaticOverload); + if (overloadData.hasClassMethod()) + flags.setFlag(CppSelfDefinitionFlag::HasClassMethodOverload); + writeCppSelfDefinition(s, rfunc, context, errorReturn, flags); } if (!rfunc->isInplaceOperator() && overloadData.hasNonVoidReturnType()) s << "PyObject *" << PYTHON_RETURN_VAR << "{};\n"; @@ -1857,80 +2023,83 @@ void CppGenerator::writeMethodWrapperPreamble(TextStream &s, OverloadData &overl initPythonArguments = minArgs != maxArgs || maxArgs > 1; } - s << R"(Shiboken::AutoDecRef errInfo{}; -static const char *fullName = ")" << fullPythonFunctionName(rfunc, true) - << "\";\nSBK_UNUSED(fullName)\n"; + if (needsArgumentErrorHandling(overloadData)) + s << "Shiboken::AutoDecRef errInfo{};\n"; + + s << "static const char fullName[] = \"" << fullPythonFunctionName(rfunc, true) + << "\";\nSBK_UNUSED(fullName)\n" + << "Shiboken::PythonContextMarker pcm;\n"; + // PYSIDE-2335: Mark blocking calls like `exec` or `run` as such. + bool isBlockingFunction = rfunc->name() == u"exec"_s || rfunc->name() == u"exec_"_s + || rfunc->name() == u"run"_s; + if (isBlockingFunction) + s << "pcm.setBlocking();\n"; + if (maxArgs > 0) { s << "int overloadId = -1;\n" - << "PythonToCppFunc " << PYTHON_TO_CPP_VAR; - if (pythonFunctionWrapperUsesListOfArguments(overloadData)) { - s << "[] = { " << NULL_PTR; - for (int i = 1; i < maxArgs; ++i) - s << ", " << NULL_PTR; - s << " };\n"; - } else { - s << "{};\n"; - } - writeUnusedVariableCast(s, QLatin1String(PYTHON_TO_CPP_VAR)); + << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR; + if (overloadData.pythonFunctionWrapperUsesListOfArguments()) + s << '[' << maxArgs << ']'; + s << ";\n" << sbkUnusedVariableCast(PYTHON_TO_CPP_VAR); } if (initPythonArguments) { s << "const Py_ssize_t numArgs = "; - if (minArgs == 0 && maxArgs == 1 && !rfunc->isConstructor() && !pythonFunctionWrapperUsesListOfArguments(overloadData)) + if (minArgs == 0 && maxArgs == 1 && !rfunc->isConstructor() + && !overloadData.pythonFunctionWrapperUsesListOfArguments()) { s << "(" << PYTHON_ARG << " == 0 ? 0 : 1);\n"; - else - writeArgumentsInitializer(s, overloadData); + } else { + writeArgumentsInitializer(s, overloadData, errorReturn); + } } } -void CppGenerator::writeConstructorWrapper(TextStream &s, const AbstractMetaFunctionCList &overloads, +void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &overloadData, const GeneratorContext &classContext) const { - ErrorCode errorCode(-1); - OverloadData overloadData(overloads, api()); + const ErrorReturn errorReturn = ErrorReturn::MinusOne; const auto rfunc = overloadData.referenceFunction(); - const AbstractMetaClass *metaClass = rfunc->ownerClass(); + const auto metaClass = rfunc->ownerClass(); s << "static int\n"; s << cpythonFunctionName(rfunc) << "(PyObject *self, PyObject *args, PyObject *kwds)\n{\n" << indent; + if (overloadData.maxArgs() == 0 || metaClass->isAbstract()) + s << sbkUnusedVariableCast("args"); + s << sbkUnusedVariableCast("kwds"); - const bool needsMetaObject = usePySideExtensions() && metaClass->isQObject(); + const bool needsMetaObject = usePySideExtensions() && isQObject(metaClass); if (needsMetaObject) s << "const QMetaObject *metaObject;\n"; - s << "SbkObject *sbkSelf = reinterpret_cast<SbkObject *>(self);\n"; + s << "auto *sbkSelf = reinterpret_cast<SbkObject *>(self);\n"; if (metaClass->isAbstract() || metaClass->baseClassNames().size() > 1) { - s << "SbkObjectType *type = reinterpret_cast<SbkObjectType *>(self->ob_type);\n" - << "SbkObjectType *myType = reinterpret_cast<SbkObjectType *>(" - << cpythonTypeNameExt(metaClass->typeEntry()) << ");\n"; + s << "PyTypeObject *type = self->ob_type;\n" + << "PyTypeObject *myType = " + << cpythonTypeNameExt(metaClass->typeEntry()) << ";\n"; } if (metaClass->isAbstract()) { // C++ Wrapper disabled: Abstract C++ class cannot be instantiated. if (metaClass->typeEntry()->typeFlags().testFlag(ComplexTypeEntry::DisableWrapper)) { - writeUnusedVariableCast(s, QStringLiteral("sbkSelf")); - writeUnusedVariableCast(s, QStringLiteral("type")); - writeUnusedVariableCast(s, QStringLiteral("myType")); + s << sbkUnusedVariableCast("sbkSelf") + << sbkUnusedVariableCast("type") + << sbkUnusedVariableCast("myType"); if (needsMetaObject) - writeUnusedVariableCast(s, QStringLiteral("metaObject")); - s << "PyErr_SetString(PyExc_NotImplementedError,\n" << indent - << "\"Abstract class '" << metaClass->qualifiedCppName() - << "' cannot be instantiated since the wrapper has been disabled.\");\n" << outdent - << returnStatement(m_currentErrorCode) << outdent - << "\n}\n\n"; + s << sbkUnusedVariableCast("metaObject"); + s << "Shiboken::Errors::setInstantiateAbstractClassDisabledWrapper(\"" + << metaClass->qualifiedCppName() << "\");\n" << errorReturn << outdent + << "}\n\n"; return; } // Refuse to instantiate Abstract C++ class (via C++ Wrapper) unless it is // a Python-derived class for which type != myType. s << "if (type == myType) {\n" << indent - << "PyErr_SetString(PyExc_NotImplementedError,\n" << indent - << "\"'" << metaClass->qualifiedCppName() - << "' represents a C++ abstract class and cannot be instantiated\");\n" << outdent - << returnStatement(m_currentErrorCode) << '\n' << outdent + << "Shiboken::Errors::setInstantiateAbstractClass(\"" << metaClass->qualifiedCppName() + << "\");\n" << errorReturn << outdent << "}\n\n"; } @@ -1943,63 +2112,67 @@ void CppGenerator::writeConstructorWrapper(TextStream &s, const AbstractMetaFunc } // PYSIDE-1478: Switching must also happen at object creation time. - if (usePySideExtensions()) + if (usePySideExtensions() && !classContext.forSmartPointer()) s << "PySide::Feature::Select(self);\n"; - writeMethodWrapperPreamble(s, overloadData, classContext); + writeMethodWrapperPreamble(s, overloadData, classContext, errorReturn); s << '\n'; if (overloadData.maxArgs() > 0) - writeOverloadedFunctionDecisor(s, overloadData); + writeOverloadedFunctionDecisor(s, overloadData, errorReturn); + + // 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); + writeFunctionCalls(s, overloadData, classContext, errorReturn); s << '\n'; - s << "if (PyErr_Occurred() || !Shiboken::Object::setCppPointer(sbkSelf, Shiboken::SbkType< ::" - << metaClass->qualifiedCppName() << " >(), cptr)) {\n"; - { - Indentation indent(s); - s << "delete cptr;\n"; - s << returnStatement(m_currentErrorCode) << '\n'; - } - s << "}\n"; + const QString typeName = classContext.forSmartPointer() + ? classContext.preciseType().cppSignature() : metaClass->qualifiedCppName(); + s << "if (" << shibokenErrorsOccurred + << " || !Shiboken::Object::setCppPointer(sbkSelf, Shiboken::SbkType< " + << globalScopePrefix(classContext) << typeName << " >(), cptr)) {\n" + << indent << "delete cptr;\n" << errorReturn << outdent + << "}\n"; if (overloadData.maxArgs() > 0) - s << "if (!cptr) goto " << cpythonFunctionName(rfunc) << "_TypeError;\n\n"; + s << "if (cptr == nullptr)\n" << indent + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n\n" + << outdent; s << "Shiboken::Object::setValidCpp(sbkSelf, true);\n"; // If the created C++ object has a C++ wrapper the ownership is assigned to Python // (first "1") and the flag indicating that the Python wrapper holds an C++ wrapper // is marked as true (the second "1"). Otherwise the default values apply: // Python owns it and C++ wrapper is false. - if (shouldGenerateCppWrapper(overloads.constFirst()->ownerClass())) + if (shouldGenerateCppWrapper(overloadData.referenceFunction()->ownerClass())) s << "Shiboken::Object::setHasCppWrapper(sbkSelf, true);\n"; // Need to check if a wrapper for same pointer is already registered // Caused by bug PYSIDE-217, where deleted objects' wrappers are not released - s << "if (Shiboken::BindingManager::instance().hasWrapper(cptr)) {\n"; - { - Indentation indent(s); - s << "Shiboken::BindingManager::instance().releaseWrapper(" - "Shiboken::BindingManager::instance().retrieveWrapper(cptr));\n"; - } - s << "}\nShiboken::BindingManager::instance().registerWrapper(sbkSelf, cptr);\n"; + s << "if (Shiboken::BindingManager::instance().hasWrapper(cptr)) {\n" << indent + << "Shiboken::BindingManager::instance().releaseWrapper(" + "Shiboken::BindingManager::instance().retrieveWrapper(cptr));\n" << outdent + << "}\nShiboken::BindingManager::instance().registerWrapper(sbkSelf, cptr);\n"; // Create metaObject and register signal/slot - bool errHandlerNeeded = overloadData.maxArgs() > 0; if (needsMetaObject) { - errHandlerNeeded = true; s << "\n// QObject setup\n" << "PySide::Signal::updateSourceObject(self);\n" << "metaObject = cptr->metaObject(); // <- init python qt properties\n" << "if (!errInfo.isNull() && PyDict_Check(errInfo.object())) {\n" << indent - << "if (!PySide::fillQtProperties(self, metaObject, errInfo))\n" << indent - << "goto " << cpythonFunctionName(rfunc) << "_TypeError;\n" << outdent << outdent + << "if (!PySide::fillQtProperties(self, metaObject, errInfo, usesPyMI))\n" << indent + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" + << outdent << outdent << "};\n"; } // Constructor code injections, position=end bool hasCodeInjectionsAtEnd = false; - for (const auto &func : overloads) { + for (const auto &func : overloadData.overloads()) { const CodeSnipList &injectedCodeSnips = func->injectedCodeSnips(); for (const CodeSnip &cs : injectedCodeSnips) { if (cs.position == TypeSystem::CodeSnipPositionEnd) { @@ -2010,125 +2183,104 @@ void CppGenerator::writeConstructorWrapper(TextStream &s, const AbstractMetaFunc } if (hasCodeInjectionsAtEnd) { // FIXME: C++ arguments are not available in code injection on constructor when position = end. - s <<"switch (overloadId) {\n"; - for (const auto &func : overloads) { - Indentation indent(s); + s << "switch (overloadId) {\n"; + for (const auto &func : overloadData.overloads()) { + s << indent; const CodeSnipList &injectedCodeSnips = func->injectedCodeSnips(); for (const CodeSnip &cs : injectedCodeSnips) { if (cs.position == TypeSystem::CodeSnipPositionEnd) { s << "case " << metaClass->functions().indexOf(func) << ':' << '\n' - << "{\n"; - { - Indentation indent(s); - writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode, func); - } - s << "}\nbreak;\n"; + << "{\n" << indent; + writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionEnd, + TypeSystem::TargetLangCode, func, + true /* usesPyArgs */, nullptr); + s << outdent << "}\nbreak;\n"; break; } } + s << outdent; } s << "}\n"; } s << "\n\nreturn 1;\n"; - if (errHandlerNeeded) - writeErrorSection(s, overloadData); s<< outdent << "}\n\n"; } -void CppGenerator::writeMethodWrapper(TextStream &s, const AbstractMetaFunctionCList &overloads, +void CppGenerator::writeMethodWrapper(TextStream &s, const OverloadData &overloadData, const GeneratorContext &classContext) const { - OverloadData overloadData(overloads, api()); const auto rfunc = overloadData.referenceFunction(); int maxArgs = overloadData.maxArgs(); s << "static PyObject *"; s << cpythonFunctionName(rfunc) << "(PyObject *self"; + bool hasKwdArgs = false; if (maxArgs > 0) { - s << ", PyObject *" << (pythonFunctionWrapperUsesListOfArguments(overloadData) ? "args" : PYTHON_ARG); - if (overloadData.hasArgumentWithDefaultValue() || rfunc->isCallOperator()) + s << ", PyObject *" + << (overloadData.pythonFunctionWrapperUsesListOfArguments() ? u"args"_s : PYTHON_ARG); + hasKwdArgs = overloadData.hasArgumentWithDefaultValue() || rfunc->isCallOperator(); + if (hasKwdArgs) s << ", PyObject *kwds"; } s << ")\n{\n" << indent; + if (rfunc->ownerClass() == nullptr || overloadData.hasStaticFunction()) + s << sbkUnusedVariableCast(PYTHON_SELF_VAR); + if (hasKwdArgs) + s << sbkUnusedVariableCast("kwds"); writeMethodWrapperPreamble(s, overloadData, classContext); s << '\n'; - /* - * This code is intended for shift operations only: - * Make sure reverse <</>> operators defined in other classes (specially from other modules) - * are called. A proper and generic solution would require an reengineering in the operator - * system like the extended converters. - * - * Solves #119 - QDataStream <</>> operators not working for QPixmap - * http://bugs.openbossa.org/show_bug.cgi?id=119 - */ - bool hasReturnValue = overloadData.hasNonVoidReturnType(); - bool callExtendedReverseOperator = hasReturnValue - && !rfunc->isInplaceOperator() - && !rfunc->isCallOperator() - && rfunc->isOperatorOverload(); - - QScopedPointer<Indentation> reverseIndent; - - if (callExtendedReverseOperator) { - QString revOpName = ShibokenGenerator::pythonOperatorFunctionName(rfunc).insert(2, QLatin1Char('r')); + // This code is intended for shift operations only: Make sure reverse <</>> + // operators defined in other classes (specially from other modules) + // are called. A proper and generic solution would require an reengineering + // in the operator system like the extended converters. + // Solves #119 - QDataStream <</>> operators not working for QPixmap. + const bool hasReturnValue = overloadData.hasNonVoidReturnType(); + + if (hasReturnValue && rfunc->functionType() == AbstractMetaFunction::ShiftOperator + && rfunc->isBinaryOperator()) { // For custom classes, operations like __radd__ and __rmul__ // will enter an infinite loop. - if (rfunc->isBinaryOperator() && revOpName.contains(QLatin1String("shift"))) { - s << "Shiboken::AutoDecRef attrName(Py_BuildValue(\"s\", \"" << revOpName << "\"));\n"; - s << "if (!isReverse\n"; - { - Indentation indent(s); - s << "&& Shiboken::Object::checkType(" << PYTHON_ARG << ")\n" - << "&& !PyObject_TypeCheck(" << PYTHON_ARG << ", self->ob_type)\n" - << "&& PyObject_HasAttr(" << PYTHON_ARG << ", attrName)) {\n"; - - // This PyObject_CallMethod call will emit lots of warnings like - // "deprecated conversion from string constant to char *" during compilation - // due to the method name argument being declared as "char *" instead of "const char *" - // issue 6952 http://bugs.python.org/issue6952 - s << "PyObject *revOpMethod = PyObject_GetAttr(" << PYTHON_ARG << ", attrName);\n"; - s << "if (revOpMethod && PyCallable_Check(revOpMethod)) {\n"; - { - Indentation indent(s); - s << PYTHON_RETURN_VAR << " = PyObject_CallFunction(revOpMethod, \"O\", self);\n" - << "if (PyErr_Occurred() && (PyErr_ExceptionMatches(PyExc_NotImplementedError)" - << " || PyErr_ExceptionMatches(PyExc_AttributeError))) {\n"; - { - Indentation indent(s); - s << "PyErr_Clear();\n" - << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");\n" - << PYTHON_RETURN_VAR << " = " << NULL_PTR << ";\n"; - } - s << "}\n"; - } - s << "}\n" - << "Py_XDECREF(revOpMethod);\n\n"; - } // - s << "}\n\n" - << "// Do not enter here if other object has implemented a reverse operator.\n" - << "if (!" << PYTHON_RETURN_VAR << ") {\n"; - reverseIndent.reset(new Indentation(s)); - } // binary shift operator - } - - if (maxArgs > 0) - writeOverloadedFunctionDecisor(s, overloadData); - - writeFunctionCalls(s, overloadData, classContext); - - if (!reverseIndent.isNull()) { // binary shift operator - reverseIndent.reset(); - s << '\n' << "} // End of \"if (!" << PYTHON_RETURN_VAR << ")\"\n"; + const QString pythonOp = ShibokenGenerator::pythonOperatorFunctionName(rfunc); + s << "static PyObject *attrName = Shiboken::PyMagicName::r" + << pythonOp.mid(2, pythonOp.size() -4) << "();\n" // Strip __ + << "if (!isReverse\n" << indent + << "&& Shiboken::Object::checkType(" << PYTHON_ARG << ")\n" + << "&& !PyObject_TypeCheck(" << PYTHON_ARG << ", self->ob_type)\n" + << "&& PyObject_HasAttr(" << PYTHON_ARG << ", attrName)) {\n" + << "PyObject *revOpMethod = PyObject_GetAttr(" << PYTHON_ARG << ", attrName);\n" + << "if (revOpMethod && PyCallable_Check(revOpMethod)) {\n" << indent + << PYTHON_RETURN_VAR << " = PyObject_CallFunction(revOpMethod, \"O\", self);\n" + << "if (" << shibokenErrorsOccurred + << " && (PyErr_ExceptionMatches(PyExc_NotImplementedError)" + << " || PyErr_ExceptionMatches(PyExc_AttributeError))) {\n" << indent + << "PyErr_Clear();\n" + << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");\n" + << PYTHON_RETURN_VAR << " = " << NULL_PTR << ";\n" + << outdent << "}\n" + << outdent << "}\n" + << "Py_XDECREF(revOpMethod);\n\n" + << outdent << "}\n\n" + << "// Do not enter here if other object has implemented a reverse operator.\n" + << "if (" << PYTHON_RETURN_VAR << " == nullptr) {\n" << indent; + if (maxArgs > 0) + writeOverloadedFunctionDecisor(s, overloadData, ErrorReturn::Default); + writeFunctionCalls(s, overloadData, classContext, ErrorReturn::Default); + s << outdent << '\n' << "} // End of \"if (!" << PYTHON_RETURN_VAR << ")\"\n"; + } else { // binary shift operator + if (maxArgs > 0) + writeOverloadedFunctionDecisor(s, overloadData, ErrorReturn::Default); + writeFunctionCalls(s, overloadData, classContext, ErrorReturn::Default); } s << '\n'; - writeFunctionReturnErrorCheckSection(s, hasReturnValue && !rfunc->isInplaceOperator()); + writeFunctionReturnErrorCheckSection(s, ErrorReturn::Default, + hasReturnValue && !rfunc->isInplaceOperator()); if (hasReturnValue) { if (rfunc->isInplaceOperator()) { @@ -2140,24 +2292,21 @@ void CppGenerator::writeMethodWrapper(TextStream &s, const AbstractMetaFunctionC s << "Py_RETURN_NONE;\n"; } - if (maxArgs > 0) - writeErrorSection(s, overloadData); - s<< outdent << "}\n\n"; } -void CppGenerator::writeArgumentsInitializer(TextStream &s, OverloadData &overloadData) +void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData &overloadData, + ErrorReturn errorReturn) { const auto rfunc = overloadData.referenceFunction(); - s << "PyTuple_GET_SIZE(args);\n"; - writeUnusedVariableCast(s, QLatin1String("numArgs")); + s << "PyTuple_GET_SIZE(args);\n" << sbkUnusedVariableCast("numArgs"); int minArgs = overloadData.minArgs(); int maxArgs = overloadData.maxArgs(); s << "PyObject *"; s << PYTHON_ARGS << "[] = {" - << QString(maxArgs, QLatin1Char('0')).split(QLatin1String(""), Qt::SkipEmptyParts).join(QLatin1String(", ")) + << QByteArrayList(maxArgs, "nullptr").join(", ") << "};\n\n"; if (overloadData.hasVarargs()) { @@ -2176,47 +2325,34 @@ void CppGenerator::writeArgumentsInitializer(TextStream &s, OverloadData &overlo bool usesNamedArguments = overloadData.hasArgumentWithDefaultValue(); s << "// invalid argument lengths\n"; - bool ownerClassIsQObject = rfunc->ownerClass() && rfunc->ownerClass()->isQObject() && rfunc->isConstructor(); - if (usesNamedArguments) { - if (!ownerClassIsQObject) { - s << "if (numArgs > " << maxArgs << ") {\n"; - { - Indentation indent(s); - s << "static PyObject *const too_many = " - "Shiboken::String::createStaticString(\">\");\n" - << "errInfo.reset(too_many);\n" - << "Py_INCREF(errInfo.object());\n" - << "goto " << cpythonFunctionName(rfunc) << "_TypeError;\n"; - } - s << '}'; - } - if (minArgs > 0) { - if (!ownerClassIsQObject) - s << " else "; - s << "if (numArgs < " << minArgs << ") {\n"; - { - Indentation indent(s); - s << "static PyObject *const too_few = " - "Shiboken::String::createStaticString(\"<\");\n" - << "errInfo.reset(too_few);\n" - << "Py_INCREF(errInfo.object());\n" - << "goto " << cpythonFunctionName(rfunc) << "_TypeError;\n"; - } - s << '}'; - } + + // Disable argument count checks for QObject constructors to allow for + // passing properties as KW args. + const auto owner = rfunc->ownerClass(); + bool isQObjectConstructor = owner && isQObject(owner) + && rfunc->functionType() == AbstractMetaFunction::ConstructorFunction; + + if (usesNamedArguments && !isQObjectConstructor) { + s << "errInfo.reset(Shiboken::checkInvalidArgumentCount(numArgs, " + << minArgs << ", " << maxArgs << "));\n" + << "if (!errInfo.isNull())\n" << indent + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" + << outdent; } + const QList<int> invalidArgsLength = overloadData.invalidArgumentLengths(); if (!invalidArgsLength.isEmpty()) { - QStringList invArgsLen; - for (int i : qAsConst(invalidArgsLength)) - invArgsLen << QStringLiteral("numArgs == %1").arg(i); - if (usesNamedArguments && (!ownerClassIsQObject || minArgs > 0)) - s << " else "; - s << "if (" << invArgsLen.join(QLatin1String(" || ")) << ")\n"; - Indentation indent(s); - s << "goto " << cpythonFunctionName(rfunc) << "_TypeError;"; + s << "if ("; + for (qsizetype i = 0, size = invalidArgsLength.size(); i < size; ++i) { + if (i) + s << " || "; + s << "numArgs == " << invalidArgsLength.at(i); + } + s << ")\n" << indent + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" + << outdent; } - s << "\n\n"; + s << '\n'; QString funcName; if (rfunc->isOperatorOverload()) @@ -2224,8 +2360,8 @@ void CppGenerator::writeArgumentsInitializer(TextStream &s, OverloadData &overlo else funcName = rfunc->name(); - QString argsVar = overloadData.hasVarargs() ? QLatin1String("nonvarargs") : QLatin1String("args"); - s << "if (!"; + QString argsVar = overloadData.hasVarargs() ? u"nonvarargs"_s : u"args"_s; + s << "if ("; if (usesNamedArguments) { s << "PyArg_ParseTuple(" << argsVar << ", \"|" << QByteArray(maxArgs, 'O') << ':' << funcName << '"'; @@ -2235,182 +2371,187 @@ void CppGenerator::writeArgumentsInitializer(TextStream &s, OverloadData &overlo } for (int i = 0; i < maxArgs; i++) s << ", &(" << PYTHON_ARGS << '[' << i << "])"; - s << "))\n"; - { - Indentation indent(s); - s << returnStatement(m_currentErrorCode) << '\n'; - } - s << '\n'; + s << ") == 0)\n" << indent << errorReturn << outdent << '\n'; } void CppGenerator::writeCppSelfConversion(TextStream &s, const GeneratorContext &context, const QString &className, bool useWrapperClass) { - static const QString pythonSelfVar = QLatin1String("self"); + if (context.forSmartPointer()) { + writeSmartPointerCppSelfConversion(s, context); + return; + } + if (useWrapperClass) s << "static_cast<" << className << " *>("; - if (!context.forSmartPointer()) - s << cpythonWrapperCPtr(context.metaClass(), pythonSelfVar); - else - s << cpythonWrapperCPtr(context.preciseType(), pythonSelfVar); + s << cpythonWrapperCPtr(context.metaClass(), PYTHON_SELF_VAR); if (useWrapperClass) s << ')'; } +void CppGenerator::writeCppSelfVarDef(TextStream &s, + CppSelfDefinitionFlags flags) +{ + if (flags.testFlag(CppGenerator::CppSelfAsReference)) + s << "auto &" << CPP_SELF_VAR << " = *"; + else + s << "auto *" << CPP_SELF_VAR << " = "; +} + void CppGenerator::writeCppSelfDefinition(TextStream &s, const GeneratorContext &context, - bool hasStaticOverload, - bool cppSelfAsReference) const + ErrorReturn errorReturn, + CppSelfDefinitionFlags flags) { - Q_ASSERT(!(cppSelfAsReference && hasStaticOverload)); + Q_ASSERT(!(flags.testFlag(CppSelfAsReference) && flags.testFlag(HasStaticOverload))); + if (context.forSmartPointer()) { + writeSmartPointerCppSelfDefinition(s, context, errorReturn, flags); + return; + } - const AbstractMetaClass *metaClass = context.metaClass(); + AbstractMetaClassCPtr metaClass = context.metaClass(); const auto cppWrapper = context.metaClass()->cppWrapper(); // In the Python method, use the wrapper to access the protected // functions. const bool useWrapperClass = avoidProtectedHack() && cppWrapper.testFlag(AbstractMetaClass::CppProtectedHackWrapper); Q_ASSERT(!useWrapperClass || context.useWrapper()); - QString className; - if (!context.forSmartPointer()) { - className = useWrapperClass - ? context.wrapperName() - : (QLatin1String("::") + metaClass->qualifiedCppName()); - } else { - className = context.smartPointerWrapperName(); - } + const QString className = useWrapperClass + ? context.wrapperName() : getFullTypeName(metaClass); - writeInvalidPyObjectCheck(s, QLatin1String("self")); + writeInvalidPyObjectCheck(s, PYTHON_SELF_VAR, errorReturn); - if (cppSelfAsReference) { - s << "auto &" << CPP_SELF_VAR << " = *"; + if (flags.testFlag(CppSelfAsReference)) { + writeCppSelfVarDef(s, flags); writeCppSelfConversion(s, context, className, useWrapperClass); s << ";\n"; return; } - if (!hasStaticOverload) { - s << "auto " << CPP_SELF_VAR << " = "; - writeCppSelfConversion(s, context, className, useWrapperClass); - s << ";\n"; - writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR)); + if (!flags.testFlag(HasStaticOverload)) { + if (!flags.testFlag(HasClassMethodOverload)) { + // PYSIDE-131: The single case of a class method for now: tr(). + writeCppSelfVarDef(s, flags); + writeCppSelfConversion(s, context, className, useWrapperClass); + s << ";\n" << sbkUnusedVariableCast(CPP_SELF_VAR); + } return; } - s << className << " *" << CPP_SELF_VAR << " = nullptr;\n"; - writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR)); + s << className << " *" << CPP_SELF_VAR << " = nullptr;\n" + << sbkUnusedVariableCast(CPP_SELF_VAR); // Checks if the underlying C++ object is valid. - s << "if (self)\n"; - { - Indentation indent(s); - s << CPP_SELF_VAR << " = "; - writeCppSelfConversion(s, context, className, useWrapperClass); - s << ";\n"; - } + s << "if (self)\n" << indent + << CPP_SELF_VAR << " = "; + writeCppSelfConversion(s, context, className, useWrapperClass); + s << ";\n"<< outdent; } void CppGenerator::writeCppSelfDefinition(TextStream &s, const AbstractMetaFunctionCPtr &func, const GeneratorContext &context, - bool hasStaticOverload) const + ErrorReturn errorReturn, + CppSelfDefinitionFlags flags) { if (!func->ownerClass() || func->isConstructor()) return; if (func->isOperatorOverload() && func->isBinaryOperator()) { QString checkFunc = cpythonCheckFunction(func->ownerClass()->typeEntry()); - s << "bool isReverse = " << checkFunc << PYTHON_ARG << ")\n"; - { - Indentation indent1(s, 4); - s << "&& !" << checkFunc << "self);\n"; - } - s << "if (isReverse)\n"; - Indentation indent(s); - s << "std::swap(self, " << PYTHON_ARG << ");\n"; + s << "bool isReverse = " << checkFunc << PYTHON_ARG << ")\n" + << " && !" << checkFunc << "self);\n" + << "if (isReverse)\n" << indent + << "std::swap(self, " << PYTHON_ARG << ");\n" << outdent; } - writeCppSelfDefinition(s, context, hasStaticOverload); + writeCppSelfDefinition(s, context, errorReturn, flags); } -void CppGenerator::writeErrorSection(TextStream &s, OverloadData &overloadData) +QString CppGenerator::returnErrorWrongArguments(const OverloadData &overloadData, + ErrorReturn errorReturn) { const auto rfunc = overloadData.referenceFunction(); - s << '\n' << cpythonFunctionName(rfunc) << "_TypeError:\n"; - Indentation indentation(s); - QString argsVar = pythonFunctionWrapperUsesListOfArguments(overloadData) - ? QLatin1String("args") : QLatin1String(PYTHON_ARG); - s << "Shiboken::setErrorAboutWrongArguments(" << argsVar << ", fullName, errInfo);\n" - << "return " << m_currentErrorCode << ";\n"; + QString argsVar = overloadData.pythonFunctionWrapperUsesListOfArguments() + ? u"args"_s : PYTHON_ARG; + switch (errorReturn) { + case ErrorReturn::Default: + return u"Shiboken::returnWrongArguments("_s + argsVar + u", fullName, errInfo)"_s; + case ErrorReturn::Zero: + return u"Shiboken::returnWrongArguments_Zero("_s + argsVar + u", fullName, errInfo)"_s; + case ErrorReturn::MinusOne: + return u"Shiboken::returnWrongArguments_MinusOne("_s + argsVar + u", fullName, errInfo)"_s; + case ErrorReturn::Void: + Q_ASSERT(false); + } + return {}; } -void CppGenerator::writeFunctionReturnErrorCheckSection(TextStream &s, bool hasReturnValue) +void CppGenerator::writeFunctionReturnErrorCheckSection(TextStream &s, + ErrorReturn errorReturn, + bool hasReturnValue) { - s << "if (PyErr_Occurred()"; + s << "if (" << shibokenErrorsOccurred; if (hasReturnValue) - s << " || !" << PYTHON_RETURN_VAR; - s << ") {\n"; - { - Indentation indent(s); - if (hasReturnValue) - s << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");\n"; - s << returnStatement(m_currentErrorCode) << '\n'; - } - s << "}\n"; + s << " || " << PYTHON_RETURN_VAR << " == nullptr"; + s << ") {\n" << indent; + if (hasReturnValue) + s << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");\n"; + s << errorReturn << outdent << "}\n"; } -void CppGenerator::writeInvalidPyObjectCheck(TextStream &s, const QString &pyObj) +void CppGenerator::writeInvalidPyObjectCheck(TextStream &s, const QString &pyObj, + ErrorReturn errorReturn) { - s << "if (!Shiboken::Object::isValid(" << pyObj << "))\n"; - Indentation indent(s); - s << returnStatement(m_currentErrorCode) << '\n'; + s << "if (!Shiboken::Object::isValid(" << pyObj << "))\n" + << indent << errorReturn << outdent; } static QString pythonToCppConverterForArgumentName(const QString &argumentName) { - static const QRegularExpression pyArgsRegex(QLatin1String(PYTHON_ARGS) - + QLatin1String(R"((\[\d+[-]?\d*\]))")); + static const QRegularExpression pyArgsRegex(PYTHON_ARGS + + uR"((\[\d+[-]?\d*\]))"_s); Q_ASSERT(pyArgsRegex.isValid()); const QRegularExpressionMatch match = pyArgsRegex.match(argumentName); - QString result = QLatin1String(PYTHON_TO_CPP_VAR); + QString result = PYTHON_TO_CPP_VAR; if (match.hasMatch()) result += match.captured(1); return result; } -void CppGenerator::writeTypeCheck(TextStream &s, AbstractMetaType argType, - const QString &argumentName, bool isNumber, - const QString &customType, bool rejectNull) const +void CppGenerator::writeTypeCheck(TextStream &s, const QString &customType, + const QString &argumentName) { - QString customCheck; - if (!customType.isEmpty()) { - AbstractMetaType metaType; - // PYSIDE-795: Note: XML-Overrides are handled in this shibokengenerator function! - // This enables iterables for QMatrix4x4 for instance. - auto customCheckResult = guessCPythonCheckFunction(customType); - customCheck = customCheckResult.checkFunction; - if (customCheckResult.type.has_value()) - argType = customCheckResult.type.value(); - } + QString errorMessage; + const auto metaTypeOpt = AbstractMetaType::fromString(customType, &errorMessage); + if (!metaTypeOpt.has_value()) + throw Exception(errorMessage); + writeTypeCheck(s, metaTypeOpt.value(), argumentName, + ShibokenGenerator::isNumber(metaTypeOpt.value())); +} +void CppGenerator::writeTypeCheck(TextStream &s, const AbstractMetaType &argType, + const QString &argumentName, bool isNumber, + bool rejectNull) +{ // TODO-CONVERTER: merge this with the code below. - QString typeCheck; - if (customCheck.isEmpty()) - typeCheck = cpythonIsConvertibleFunction(argType, argType.isEnum() ? false : isNumber); - else - typeCheck = customCheck; - typeCheck.append(QString::fromLatin1("(%1)").arg(argumentName)); + QString typeCheck = cpythonIsConvertibleFunction(argType); + if (typeCheck != u"true") // For PyObject, which is always true + typeCheck.append(u'(' +argumentName + u')'); // TODO-CONVERTER ----------------------------------------------------------------------- - if (customCheck.isEmpty() && !argType.typeEntry()->isCustom()) { - typeCheck = QString::fromLatin1("(%1 = %2))").arg(pythonToCppConverterForArgumentName(argumentName), typeCheck); - if (!isNumber && argType.typeEntry()->isCppPrimitive()) - typeCheck.prepend(QString::fromLatin1("%1(%2) && ").arg(cpythonCheckFunction(argType), argumentName)); + if (!argType.typeEntry()->isCustom()) { + typeCheck = u'(' + pythonToCppConverterForArgumentName(argumentName) + + u" = "_s + typeCheck + u"))"_s; + if (!isNumber && isCppPrimitive(argType.typeEntry())) { + typeCheck.prepend(cpythonCheckFunction(argType) + u'(' + + argumentName + u") && "_s); + } } // TODO-CONVERTER ----------------------------------------------------------------------- if (rejectNull) - typeCheck = QString::fromLatin1("(%1 != Py_None && %2)").arg(argumentName, typeCheck); + typeCheck = u'(' + argumentName + u" != Py_None && "_s + typeCheck + u')'; s << typeCheck; } @@ -2418,14 +2559,20 @@ void CppGenerator::writeTypeCheck(TextStream &s, AbstractMetaType argType, static void checkTypeViability(const AbstractMetaFunctionCPtr &func, const AbstractMetaType &type, int argIdx) { + const bool modified = argIdx == 0 + ? func->isTypeModified() + : func->arguments().at(argIdx -1).isTypeModified(); + const bool isRemoved = argIdx == 0 + ? func->argumentRemoved(0) + : func->arguments().at(argIdx -1).isModifiedRemoved(); if (type.isVoid() || !type.typeEntry()->isPrimitive() || type.indirections() == 0 || (type.indirections() == 1 && type.typeUsagePattern() == AbstractMetaType::NativePointerAsArrayPattern) || type.isCString() - || func->argumentRemoved(argIdx) - || !func->typeReplaced(argIdx).isEmpty() - || !func->conversionRule(TypeSystem::All, argIdx).isEmpty() + || isRemoved + || modified + || func->hasConversionRule(TypeSystem::All, argIdx) || func->hasInjectedCode()) return; QString message; @@ -2449,19 +2596,20 @@ static void checkTypeViability(const AbstractMetaFunctionCPtr &func) if (func->isUserAdded()) return; checkTypeViability(func, func->type(), 0); - for (int i = 0; i < func->arguments().count(); ++i) - checkTypeViability(func, func->arguments().at(i).type(), i + 1); + for (qsizetype i = 0; i < func->arguments().size(); ++i) + checkTypeViability(func, func->arguments().at(i).type(), int(i + 1)); } -void CppGenerator::writeTypeCheck(TextStream &s, const OverloadData *overloadData, - const QString &argumentName) const +void CppGenerator::writeTypeCheck(TextStream &s, + const std::shared_ptr<OverloadDataNode> &overloadData, + const QString &argumentName) { - QSet<const TypeEntry *> numericTypes; - const OverloadDataList &overloads = overloadData->previousOverloadData()->nextOverloadData(); - for (OverloadData *od : overloads) { - for (const auto &func : od->overloads()) { + QSet<TypeEntryCPtr> numericTypes; + const OverloadDataList &siblings = overloadData->parent()->children(); + for (const auto &sibling : siblings) { + for (const auto &func : sibling->overloads()) { checkTypeViability(func); - const AbstractMetaType &argType = od->argument(func)->type(); + const AbstractMetaType &argType = sibling->overloadArgument(func)->type(); if (!argType.isPrimitive()) continue; if (ShibokenGenerator::isNumber(argType.typeEntry())) @@ -2470,140 +2618,139 @@ void CppGenerator::writeTypeCheck(TextStream &s, const OverloadData *overloadDat } // This condition trusts that the OverloadData object will arrange for - // PyInt type to come after the more precise numeric types (e.g. float and bool) - AbstractMetaType argType = overloadData->argType(); + // PyLong type to come after the more precise numeric types (e.g. float and bool) + AbstractMetaType argType = overloadData->modifiedArgType(); if (auto viewOn = argType.viewOn()) argType = *viewOn; - bool numberType = numericTypes.count() == 1 || ShibokenGenerator::isPyInt(argType); - QString customType = (overloadData->hasArgumentTypeReplace() ? overloadData->argumentTypeReplaced() : QString()); + const bool numberType = numericTypes.size() == 1 || ShibokenGenerator::isPyInt(argType); bool rejectNull = - shouldRejectNullPointerArgument(api(), overloadData->referenceFunction(), overloadData->argPos()); - writeTypeCheck(s, argType, argumentName, numberType, customType, rejectNull); + shouldRejectNullPointerArgument(overloadData->referenceFunction(), overloadData->argPos()); + writeTypeCheck(s, argType, argumentName, numberType, rejectNull); } -void CppGenerator::writeArgumentConversion(TextStream &s, - const AbstractMetaType &argType, - const QString &argName, const QString &pyArgName, - const AbstractMetaClass *context, - const QString &defaultValue, - bool castArgumentAsUnused) const +qsizetype CppGenerator::writeArgumentConversion(TextStream &s, + const AbstractMetaType &argType, + const QString &argName, + const QString &pyArgName, + ErrorReturn errorReturn, + const AbstractMetaClassCPtr &context, + const QString &defaultValue, + bool castArgumentAsUnused) const { + qsizetype result = 0; if (argType.typeEntry()->isCustom() || argType.typeEntry()->isVarargs()) - return; + return result; if (argType.isWrapperType()) - writeInvalidPyObjectCheck(s, pyArgName); - writePythonToCppTypeConversion(s, argType, pyArgName, argName, context, defaultValue); + writeInvalidPyObjectCheck(s, pyArgName, errorReturn); + result = writePythonToCppTypeConversion(s, argType, pyArgName, argName, context, defaultValue); if (castArgumentAsUnused) - writeUnusedVariableCast(s, argName); -} - -static const QStringList &knownPythonTypes() -{ - static const QStringList result = { - pyBoolT(), pyIntT(), pyFloatT(), pyLongT(), - cPyObjectT(), QLatin1String("PyString"), - cPyBufferT(), cPySequenceT(), - QLatin1String("PyTuple"), cPyListT(), - QLatin1String("PyDict"), QLatin1String("PyObject*"), - QLatin1String("PyObject *"), QLatin1String("PyTupleObject*")}; + s << sbkUnusedVariableCast(argName); return result; } -std::optional<AbstractMetaType> - CppGenerator::getArgumentType(const AbstractMetaFunctionCPtr &func, int argPos) +AbstractMetaType + CppGenerator::getArgumentType(const AbstractMetaFunctionCPtr &func, int index) { - if (argPos < 0 || argPos > func->arguments().size()) { + if (index < 0 || index >= func->arguments().size()) { qCWarning(lcShiboken).noquote().nospace() - << QStringLiteral("Argument index for function '%1' out of range.").arg(func->signature()); + << "Argument index for function '" << func->signature() << "' out of range."; return {}; } - QString typeReplaced = func->typeReplaced(argPos); - if (typeReplaced.isEmpty()) { - if (argPos == 0) - return func->type(); - auto argType = func->arguments().at(argPos - 1).type(); - return argType.viewOn() ? *argType.viewOn() : argType; - } - - auto argType = buildAbstractMetaTypeFromString(typeReplaced); - if (!argType.has_value() && !knownPythonTypes().contains(typeReplaced)) { - qCWarning(lcShiboken, "%s", - qPrintable(msgUnknownTypeInArgumentTypeReplacement(typeReplaced, func.data()))); - } - return argType; + auto argType = func->arguments().at(index).modifiedType(); + return argType.viewOn() ? *argType.viewOn() : argType; } static inline QString arrayHandleType(const AbstractMetaTypeList &nestedArrayTypes) { switch (nestedArrayTypes.size()) { case 1: - return QStringLiteral("Shiboken::Conversions::ArrayHandle<") - + nestedArrayTypes.constLast().minimalSignature() - + QLatin1Char('>'); + return "Shiboken::Conversions::ArrayHandle<"_L1 + + nestedArrayTypes.constLast().minimalSignature() + u'>'; case 2: - return QStringLiteral("Shiboken::Conversions::Array2Handle<") + return "Shiboken::Conversions::Array2Handle<"_L1 + nestedArrayTypes.constLast().minimalSignature() - + QStringLiteral(", ") + + ", "_L1 + QString::number(nestedArrayTypes.constFirst().arrayElementCount()) - + QLatin1Char('>'); + + u'>'; } return QString(); } -void CppGenerator::writePythonToCppTypeConversion(TextStream &s, +// Helper to write argument initialization code for a function argument +// in case it has a default value. +template <class Type> // AbstractMetaType/TypeEntry +static void writeMinimalConstructorExpression(TextStream &s, + const ApiExtractorResult &api, + Type type, + bool isPrimitive, + const QString &defaultValue) +{ + if (defaultValue.isEmpty()) { + s << ShibokenGenerator::minimalConstructorExpression(api, type); + return; + } + // Use assignment to avoid "Most vexing parse" if it looks like + // a function call, or for primitives/pointers + const bool isDefault = defaultValue == u"{}"; + if ((isPrimitive && !isDefault) + || defaultValue == u"nullptr" || defaultValue.contains(u'(')) { + s << " = " << defaultValue; + return; + } + if (isDefault) { + s << defaultValue; + return; + } + s << '(' << defaultValue << ')'; +} + +qsizetype CppGenerator::writePythonToCppTypeConversion(TextStream &s, const AbstractMetaType &type, const QString &pyIn, const QString &cppOut, - const AbstractMetaClass *context, + const AbstractMetaClassCPtr &context, const QString &defaultValue) const { - const TypeEntry *typeEntry = type.typeEntry(); + TypeEntryCPtr typeEntry = type.typeEntry(); if (typeEntry->isCustom() || typeEntry->isVarargs()) - return; + return 0; - QString cppOutAux = cppOut + QLatin1String("_local"); - - const bool isEnum = typeEntry->isEnum(); - const bool isFlags = typeEntry->isFlags(); - bool treatAsPointer = valueTypeWithCopyConstructorOnlyPassed(api(), type); - bool isPointerOrObjectType = (type.isObjectType() || type.isPointer()) - && !type.isUserPrimitive() && !type.isExtendedCppPrimitive() - && !isEnum && !isFlags; - const bool isNotContainerEnumOrFlags = !typeEntry->isContainer() - && !isEnum && !isFlags; - bool mayHaveImplicitConversion = type.referenceType() == LValueReference - && !type.isUserPrimitive() - && !type.isExtendedCppPrimitive() - && isNotContainerEnumOrFlags - && !(treatAsPointer || isPointerOrObjectType); - - const AbstractMetaTypeList &nestedArrayTypes = type.nestedArrayTypes(); - const bool isCppPrimitiveArray = !nestedArrayTypes.isEmpty() - && nestedArrayTypes.constLast().isCppPrimitive(); - QString typeName = isCppPrimitiveArray - ? arrayHandleType(nestedArrayTypes) - : getFullTypeNameWithoutModifiers(type); + const auto arg = GeneratorArgument::fromMetaType(type); + const bool isPrimitive = arg.type == GeneratorArgument::Type::Primitive; - bool isProtectedEnum = false; + QString cppOutAux = cppOut + u"_local"_s; - if (mayHaveImplicitConversion) { - s << typeName << ' ' << cppOutAux; - writeMinimalConstructorExpression(s, api(), type, defaultValue); - s << ";\n"; - } else if (avoidProtectedHack() && isEnum) { + QString typeName = arg.type == GeneratorArgument::Type::CppPrimitiveArray + ? arrayHandleType(type.nestedArrayTypes()) + : getFullTypeNameWithoutModifiers(type); + + bool isProtectedEnum = false; + if (arg.type == GeneratorArgument::Type::Enum && avoidProtectedHack()) { auto metaEnum = api().findAbstractMetaEnum(type.typeEntry()); if (metaEnum.has_value() && metaEnum->isProtected()) { - typeName = wrapperName(context) + QLatin1String("::") + typeName = wrapperName(context) + u"::"_s + metaEnum.value().name(); isProtectedEnum = true; } } s << typeName; - if (isCppPrimitiveArray) { + switch (arg.conversion) { + case GeneratorArgument::Conversion::CppPrimitiveArray: s << ' ' << cppOut; - } else if (treatAsPointer || isPointerOrObjectType) { + break; + case GeneratorArgument::Conversion::ValueOrPointer: { + // Generate either value conversion for &cppOutAux or pointer + // conversion for &cppOut + s << ' ' << cppOutAux; + // No default value for containers which can also be passed by pointer. + if (arg.type != GeneratorArgument::Type::Container || type.indirections() == 0) + writeMinimalConstructorExpression(s, api(), type, isPrimitive, defaultValue); + s << ";\n" << typeName << " *" << cppOut << " = &" << cppOutAux; + } + break; + case GeneratorArgument::Conversion::Pointer: { s << " *" << cppOut; if (!defaultValue.isEmpty()) { const bool needsConstCast = !isNullPtr(defaultValue) @@ -2616,9 +2763,9 @@ void CppGenerator::writePythonToCppTypeConversion(TextStream &s, if (needsConstCast) s << ')'; } - } else if (type.referenceType() == LValueReference && !typeEntry->isPrimitive() && isNotContainerEnumOrFlags) { - s << " *" << cppOut << " = &" << cppOutAux; - } else { + } + break; + case GeneratorArgument::Conversion::Default: s << ' ' << cppOut; if (isProtectedEnum && avoidProtectedHack()) { s << " = "; @@ -2626,43 +2773,48 @@ void CppGenerator::writePythonToCppTypeConversion(TextStream &s, s << "{}"; else s << defaultValue; - } else if (type.isUserPrimitive() || isEnum || isFlags) { - writeMinimalConstructorExpression(s, api(), typeEntry, defaultValue); - } else if (!type.isContainer() && !type.isSmartPointer()) { - writeMinimalConstructorExpression(s, api(), type, defaultValue); + } else if (type.isUserPrimitive() + || arg.type == GeneratorArgument::Type::Enum + || arg.type == GeneratorArgument::Type::Flags) { + writeMinimalConstructorExpression(s, api(), typeEntry, isPrimitive, defaultValue); + } else if ((!type.isContainer() || type.indirections() == 0) && !type.isSmartPointer()) { + writeMinimalConstructorExpression(s, api(), type, isPrimitive, defaultValue); } + break; } s << ";\n"; QString pythonToCppFunc = pythonToCppConverterForArgumentName(pyIn); - if (!defaultValue.isEmpty()) - s << "if (" << pythonToCppFunc << ") "; - - QString pythonToCppCall = QString::fromLatin1("%1(%2, &%3)").arg(pythonToCppFunc, pyIn, cppOut); - if (!mayHaveImplicitConversion) { + QString pythonToCppCall = pythonToCppFunc + u'(' + pyIn + u", &"_s + + cppOut + u')'; + if (arg.conversion != GeneratorArgument::Conversion::ValueOrPointer) { + // pythonToCppFunc may be 0 when less parameters are passed and + // the defaultValue takes effect. + if (!defaultValue.isEmpty()) + s << "if (" << pythonToCppFunc << ")\n" << indent; s << pythonToCppCall << ";\n"; - return; + if (!defaultValue.isEmpty()) + s << outdent; + return arg.indirections; } + // pythonToCppFunc may be 0 when less parameters are passed and + // the defaultValue takes effect. if (!defaultValue.isEmpty()) - s << "{\n"; + s << "if (" << pythonToCppFunc << ") {\n" << indent; - s << "if (Shiboken::Conversions::isImplicitConversion(reinterpret_cast<SbkObjectType *>(" - << cpythonTypeNameExt(type) << "), " << pythonToCppFunc << "))\n"; - { - Indentation indent(s); - s << pythonToCppFunc << '(' << pyIn << ", &" << cppOutAux << ");\n"; - } - s << "else\n"; - { - Indentation indent(s); - s << pythonToCppCall << ";\n"; - } + s << "if (" << pythonToCppFunc << ".isValue())\n" + << indent << pythonToCppFunc << '(' << pyIn << ", &" << cppOutAux << ");\n" + << outdent << "else\n" << indent + << pythonToCppCall << ";\n" << outdent; - if (!defaultValue.isEmpty()) - s << '}'; - s << '\n'; + if (defaultValue.isEmpty()) + s << '\n'; + else + s << "}\n" << outdent; + + return arg.indirections; } static void addConversionRuleCodeSnippet(CodeSnipList &snippetList, QString &rule, @@ -2674,10 +2826,10 @@ static void addConversionRuleCodeSnippet(CodeSnipList &snippetList, QString &rul if (rule.isEmpty()) return; if (snippetLanguage == TypeSystem::TargetLangCode) { - rule.replace(QLatin1String("%in"), inputName); - rule.replace(QLatin1String("%out"), outputName + QLatin1String("_out")); + rule.replace(u"%in"_s, inputName); + rule.replace(u"%out"_s, outputName + u"_out"_s); } else { - rule.replace(QLatin1String("%out"), outputName); + rule.replace(u"%out"_s, outputName); } CodeSnip snip(snippetLanguage); snip.position = (snippetLanguage == TypeSystem::NativeCode) ? TypeSystem::CodeSnipPositionAny : TypeSystem::CodeSnipPositionBeginning; @@ -2686,7 +2838,7 @@ static void addConversionRuleCodeSnippet(CodeSnipList &snippetList, QString &rul } void CppGenerator::writeConversionRule(TextStream &s, const AbstractMetaFunctionCPtr &func, - TypeSystem::Language language) const + TypeSystem::Language language, bool usesPyArgs) const { CodeSnipList snippets; @@ -2696,7 +2848,8 @@ void CppGenerator::writeConversionRule(TextStream &s, const AbstractMetaFunction addConversionRuleCodeSnippet(snippets, rule, language, TypeSystem::TargetLangCode, arg.name(), arg.name()); } - writeCodeSnips(s, snippets, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode, func); + writeCodeSnips(s, snippets, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode, + func, usesPyArgs, nullptr); } void CppGenerator::writeConversionRule(TextStream &s, const AbstractMetaFunctionCPtr &func, @@ -2705,7 +2858,8 @@ void CppGenerator::writeConversionRule(TextStream &s, const AbstractMetaFunction CodeSnipList snippets; QString rule = func->conversionRule(language, 0); addConversionRuleCodeSnippet(snippets, rule, language, language, outputVar); - writeCodeSnips(s, snippets, TypeSystem::CodeSnipPositionAny, language, func); + writeCodeSnips(s, snippets, TypeSystem::CodeSnipPositionAny, language, + func, false /* uses PyArgs */, nullptr); } void CppGenerator::writeNoneReturn(TextStream &s, const AbstractMetaFunctionCPtr &func, @@ -2718,45 +2872,46 @@ void CppGenerator::writeNoneReturn(TextStream &s, const AbstractMetaFunctionCPtr } } -void CppGenerator::writeOverloadedFunctionDecisor(TextStream &s, const OverloadData &overloadData) const +void CppGenerator::writeOverloadedFunctionDecisor(TextStream &s, + const OverloadData &overloadData, + ErrorReturn errorReturn) const { s << "// Overloaded function decisor\n"; const auto rfunc = overloadData.referenceFunction(); - const AbstractMetaFunctionCList &functionOverloads = overloadData.overloadsWithoutRepetition(); - for (int i = 0; i < functionOverloads.count(); i++) { + const AbstractMetaFunctionCList &functionOverloads = overloadData.overloads(); + for (qsizetype i = 0; i < functionOverloads.size(); ++i) { const auto func = functionOverloads.at(i); s << "// " << i << ": "; if (func->isStatic()) s << "static "; - if (const auto *decl = func->declaringClass()) + if (const auto &decl = func->declaringClass()) s << decl->name() << "::"; s << func->signatureComment() << '\n'; } - writeOverloadedFunctionDecisorEngine(s, &overloadData); + writeOverloadedFunctionDecisorEngine(s, overloadData, &overloadData); s << '\n'; // Ensure that the direct overload that called this reverse // is called. if (rfunc->isOperatorOverload() && !rfunc->isCallOperator()) { - s << "if (isReverse && overloadId == -1) {\n"; - { - Indentation indent(s); - s << "PyErr_SetString(PyExc_NotImplementedError, \"reverse operator not implemented.\");\n" - << "return {};\n"; - } - s << "}\n\n"; + s << "if (isReverse && overloadId == -1) {\n" << indent + << "Shiboken::Errors::setReverseOperatorNotImplemented();\n" + << "return {};\n" << outdent + << "}\n\n"; } s << "// Function signature not found.\n" - << "if (overloadId == -1) goto " - << cpythonFunctionName(overloadData.referenceFunction()) << "_TypeError;\n\n"; + << "if (overloadId == -1)\n" << indent + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n\n" + << outdent; } void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, - const OverloadData *parentOverloadData) const + const OverloadData &overloadData, + const OverloadDataRootNode *node) const { - bool hasDefaultCall = parentOverloadData->nextArgumentHasDefaultValue(); - auto referenceFunction = parentOverloadData->referenceFunction(); + bool hasDefaultCall = node->nextArgumentHasDefaultValue(); + auto referenceFunction = node->referenceFunction(); // If the next argument has not an argument with a default value, it is still possible // that one of the overloads for the current overload data has its final occurrence here. @@ -2764,8 +2919,8 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, // variable to be used further on this method on the conditional that identifies default // method calls. if (!hasDefaultCall) { - for (const auto &func : parentOverloadData->overloads()) { - if (parentOverloadData->isFinalOccurrence(func)) { + for (const auto &func : node->overloads()) { + if (node->isFinalOccurrence(func)) { referenceFunction = func; hasDefaultCall = true; break; @@ -2773,13 +2928,13 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, } } - int maxArgs = parentOverloadData->maxArgs(); + const int maxArgs = overloadData.maxArgs(); // Python constructors always receive multiple arguments. - bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(*parentOverloadData); + const bool usePyArgs = overloadData.pythonFunctionWrapperUsesListOfArguments(); // Functions without arguments are identified right away. if (maxArgs == 0) { - s << "overloadId = " << parentOverloadData->headOverloadData()->overloads().indexOf(referenceFunction) + s << "overloadId = " << overloadData.functionNumber(referenceFunction) << "; // " << referenceFunction->minimalSignature() << '\n'; return; @@ -2787,15 +2942,15 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, // To decide if a method call is possible at this point the current overload // data object cannot be the head, since it is just an entry point, or a root, // for the tree of arguments and it does not represent a valid method call. - if (!parentOverloadData->isHeadOverloadData()) { - bool isLastArgument = parentOverloadData->nextOverloadData().isEmpty(); - bool signatureFound = parentOverloadData->overloads().size() == 1; + if (!node->isRoot()) { + const bool isLastArgument = node->children().isEmpty(); + const bool signatureFound = node->overloads().size() == 1; // The current overload data describes the last argument of a signature, // so the method can be identified right now. if (isLastArgument || (signatureFound && !hasDefaultCall)) { - const auto func = parentOverloadData->referenceFunction(); - s << "overloadId = " << parentOverloadData->headOverloadData()->overloads().indexOf(func) + const auto func = node->referenceFunction(); + s << "overloadId = " << overloadData.functionNumber(func) << "; // " << func->minimalSignature() << '\n'; return; } @@ -2806,58 +2961,54 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, // If the next argument has a default value the decisor can perform a method call; // it just need to check if the number of arguments received from Python are equal // to the number of parameters preceding the argument with the default value. - const OverloadDataList &overloads = parentOverloadData->nextOverloadData(); + const OverloadDataList &children = node->children(); if (hasDefaultCall) { isFirst = false; - int numArgs = parentOverloadData->argPos() + 1; - s << "if (numArgs == " << numArgs << ") {\n"; - { - Indentation indent(s); - auto func = referenceFunction; - for (OverloadData *overloadData : overloads) { - const auto defValFunc = overloadData->getFunctionWithDefaultValue(); - if (!defValFunc.isNull()) { - func = defValFunc; - break; - } + int numArgs = node->argPos() + 1; + s << "if (numArgs == " << numArgs << ") {\n" << indent; + auto func = referenceFunction; + for (const auto &child : children) { + const auto defValFunc = child->getFunctionWithDefaultValue(); + if (defValFunc) { + func = defValFunc; + break; } - s << "overloadId = " << parentOverloadData->headOverloadData()->overloads().indexOf(func) - << "; // " << func->minimalSignature() << '\n'; } - s << '}'; + s << "overloadId = " << overloadData.functionNumber(func) + << "; // " << func->minimalSignature() << '\n' << outdent << '}'; } - for (OverloadData *overloadData : overloads) { - bool signatureFound = overloadData->overloads().size() == 1 - && !overloadData->getFunctionWithDefaultValue() - && !overloadData->findNextArgWithDefault(); + for (auto child : children) { + bool signatureFound = child->overloads().size() == 1 + && !child->getFunctionWithDefaultValue() + && !child->findNextArgWithDefault(); - const auto refFunc = overloadData->referenceFunction(); + const auto refFunc = child->referenceFunction(); QStringList typeChecks; QString pyArgName = (usePyArgs && maxArgs > 1) - ? pythonArgsAt(overloadData->argPos()) - : QLatin1String(PYTHON_ARG); - OverloadData *od = overloadData; + ? pythonArgsAt(child->argPos()) + : PYTHON_ARG; + auto od = child; int startArg = od->argPos(); int sequenceArgCount = 0; while (od && !od->argType().isVarargs()) { - bool typeReplacedByPyObject = od->argumentTypeReplaced() == cPyObjectT(); + const bool typeReplacedByPyObject = od->isTypeModified() + && od->modifiedArgType().name() == cPyObjectT; if (!typeReplacedByPyObject) { if (usePyArgs) pyArgName = pythonArgsAt(od->argPos()); StringStream tck(TextStream::Language::Cpp); auto func = od->referenceFunction(); - if (func->isConstructor() && func->arguments().count() == 1) { - const AbstractMetaClass *ownerClass = func->ownerClass(); - const ComplexTypeEntry *baseContainerType = ownerClass->typeEntry()->baseContainerType(); + if (func->isConstructor() && func->arguments().size() == 1) { + AbstractMetaClassCPtr ownerClass = func->ownerClass(); + ComplexTypeEntryCPtr baseContainerType = ownerClass->typeEntry()->baseContainerType(); if (baseContainerType && baseContainerType == func->arguments().constFirst().type().typeEntry() && ownerClass->isCopyable()) { - tck << '!' << cpythonCheckFunction(ownerClass->typeEntry()) << pyArgName << ")\n"; - Indentation indent(s); - tck << "&& "; + tck << '!' << cpythonCheckFunction(ownerClass->typeEntry()) + << pyArgName << ")\n" << indent << "&& " << outdent; } } writeTypeCheck(tck, od, pyArgName); @@ -2866,14 +3017,14 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, sequenceArgCount++; - if (od->nextOverloadData().isEmpty() + if (od->children().isEmpty() || od->nextArgumentHasDefaultValue() - || od->nextOverloadData().size() != 1 - || od->overloads().size() != od->nextOverloadData().constFirst()->overloads().size()) { - overloadData = od; + || od->children().size() != 1 + || od->overloads().size() != od->children().constFirst()->overloads().size()) { + child = od; od = nullptr; } else { - od = od->nextOverloadData().constFirst(); + od = od->children().constFirst(); } } @@ -2883,14 +3034,16 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, int numArgs = args.size() - OverloadData::numberOfRemovedArguments(refFunc); if (isVarargs) --numArgs; - typeChecks.prepend(QString::fromLatin1("numArgs %1 %2").arg(isVarargs ? QLatin1String(">=") : QLatin1String("==")).arg(numArgs)); - } else if (sequenceArgCount > 1) { - typeChecks.prepend(QString::fromLatin1("numArgs >= %1").arg(startArg + sequenceArgCount)); + QString check = (isVarargs ? u"numArgs >= "_s : u"numArgs == "_s) + + QString::number(numArgs); + typeChecks.prepend(check); + } else if (usePyArgs && sequenceArgCount > 0) { + typeChecks.prepend(u"numArgs >= "_s + QString::number(startArg + sequenceArgCount)); } else if (refFunc->isOperatorOverload() && !refFunc->isCallOperator()) { QString check; if (!refFunc->isReverseOperator()) - check.append(QLatin1Char('!')); - check.append(QLatin1String("isReverse")); + check.append(u'!'); + check.append(u"isReverse"_s); typeChecks.prepend(check); } @@ -2903,88 +3056,87 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s, if (typeChecks.isEmpty()) { s << "true"; } else { - Indentation indent(s); - s << typeChecks.join(QLatin1String("\n&& ")); - } - s << ") {\n"; - { - Indentation indent(s); - writeOverloadedFunctionDecisorEngine(s, overloadData); + s << indent << typeChecks.join(u"\n&& "_s) << outdent; } - s << "}"; + s << ") {\n" << indent; + writeOverloadedFunctionDecisorEngine(s, overloadData, child.get()); + s << outdent << '}'; } s << '\n'; } void CppGenerator::writeFunctionCalls(TextStream &s, const OverloadData &overloadData, - const GeneratorContext &context) const + const GeneratorContext &context, + ErrorReturn errorReturn) const { - const AbstractMetaFunctionCList &overloads = overloadData.overloadsWithoutRepetition(); + const AbstractMetaFunctionCList &overloads = overloadData.overloads(); s << "// Call function/method\n" - << (overloads.count() > 1 ? "switch (overloadId) " : "") << "{\n"; - { - Indentation indent(s); - if (overloads.count() == 1) { - writeSingleFunctionCall(s, overloadData, overloads.constFirst(), context); - } else { - for (int i = 0; i < overloads.count(); i++) { - const auto func = overloads.at(i); - s << "case " << i << ": // " << func->signature() << "\n{\n"; - { - Indentation indent(s); - writeSingleFunctionCall(s, overloadData, func, context); - if (func->attributes().testFlag(AbstractMetaFunction::Deprecated)) { - s << "PyErr_WarnEx(PyExc_DeprecationWarning, \""; - if (auto cls = context.metaClass()) - s << cls->name() << '.'; - s << func->signature() << " is deprecated\", 1);\n"; - } - s << "break;\n"; - } - s << "}\n"; - } + << (overloads.size() > 1 ? "switch (overloadId) " : "") << "{\n" << indent; + if (overloads.size() == 1) { + writeSingleFunctionCall(s, overloadData, overloads.constFirst(), context, + errorReturn); + } else { + for (qsizetype i = 0; i < overloads.size(); ++i) { + const auto func = overloads.at(i); + s << "case " << i << ": // " << func->signature() << "\n{\n" << indent; + writeSingleFunctionCall(s, overloadData, func, context, errorReturn); + s << "break;\n" << outdent << "}\n"; } } - s << "}\n"; + s << outdent << "}\n"; +} + +static void writeDeprecationWarning(TextStream &s, + const GeneratorContext &context, + const AbstractMetaFunctionCPtr &func, + CppGenerator::ErrorReturn errorReturn) +{ + s << "Shiboken::Warnings::warnDeprecated(\""; + if (const auto cls = context.metaClass()) + s << cls->name() << "\", "; + // Check error in case "warning-as-error" is set. + s << '"' << func->signature().replace(u"::"_s, u"."_s) << "\");\n" + << "if (" << shibokenErrorsOccurred << ")\n" + << indent << errorReturn << outdent; } void CppGenerator::writeSingleFunctionCall(TextStream &s, const OverloadData &overloadData, const AbstractMetaFunctionCPtr &func, - const GeneratorContext &context) const + const GeneratorContext &context, + ErrorReturn errorReturn) const { - if (func->isDeprecated()) { - s << "Shiboken::warning(PyExc_DeprecationWarning, 1, \"Function: '" - << func->signature().replace(QLatin1String("::"), QLatin1String(".")) - << "' is marked as deprecated, please check the documentation for more information.\");\n"; - } + if (func->isDeprecated()) + writeDeprecationWarning(s, context, func, errorReturn); if (func->functionType() == AbstractMetaFunction::EmptyFunction) { - s << "PyErr_Format(PyExc_TypeError, \"%s is a private method.\", \"" - << func->signature().replace(QLatin1String("::"), QLatin1String(".")) - << "\");\n" - << returnStatement(m_currentErrorCode) << '\n'; + s << "Shiboken::Errors::setPrivateMethod(\"" + << func->signature().replace(u"::"_s, u"."_s) << "\");\n" + << errorReturn; return; } - bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(overloadData); + const bool usePyArgs = overloadData.pythonFunctionWrapperUsesListOfArguments(); // Handle named arguments. - writeNamedArgumentResolution(s, func, usePyArgs, overloadData); + writeNamedArgumentResolution(s, func, usePyArgs, overloadData, errorReturn); bool injectCodeCallsFunc = injectedCodeCallsCppFunction(context, func); bool mayHaveUnunsedArguments = !func->isUserAdded() && func->hasInjectedCode() && injectCodeCallsFunc; int removedArgs = 0; - for (int argIdx = 0; argIdx < func->arguments().count(); ++argIdx) { - bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode, argIdx + 1).isEmpty(); + + const auto argCount = func->arguments().size(); + QList<qsizetype> indirections(argCount, 0); + for (qsizetype argIdx = 0; argIdx < argCount; ++argIdx) { + const bool hasConversionRule = + func->hasConversionRule(TypeSystem::NativeCode, int(argIdx + 1)); const AbstractMetaArgument &arg = func->arguments().at(argIdx); - if (func->argumentRemoved(argIdx + 1)) { + if (arg.isModifiedRemoved()) { if (!arg.defaultValueExpression().isEmpty()) { - const QString cppArgRemoved = QLatin1String(CPP_ARG_REMOVED) - + QString::number(argIdx); + const QString cppArgRemoved = CPP_ARG_REMOVED(argIdx); s << getFullTypeName(arg.type()) << ' ' << cppArgRemoved; - s << " = " << guessScopeForDefaultValue(func, arg) << ";\n"; - writeUnusedVariableCast(s, cppArgRemoved); + s << " = " << arg.defaultValueExpression() << ";\n" + << sbkUnusedVariableCast(cppArgRemoved); } else if (!injectCodeCallsFunc && !func->isUserAdded() && !hasConversionRule) { // When an argument is removed from a method signature and no other means of calling // the method are provided (as with code injection) the generator must abort. @@ -2999,24 +3151,26 @@ void CppGenerator::writeSingleFunctionCall(TextStream &s, } if (hasConversionRule) continue; - auto argType = getArgumentType(func, argIdx + 1); - if (!argType.has_value() || (mayHaveUnunsedArguments && !func->injectedCodeUsesArgument(argIdx))) + if (mayHaveUnunsedArguments && !func->injectedCodeUsesArgument(argIdx)) continue; + auto argType = getArgumentType(func, argIdx); int argPos = argIdx - removedArgs; - QString argName = QLatin1String(CPP_ARG) + QString::number(argPos); - QString pyArgName = usePyArgs ? pythonArgsAt(argPos) : QLatin1String(PYTHON_ARG); - QString defaultValue = guessScopeForDefaultValue(func, arg); - writeArgumentConversion(s, argType.value(), argName, pyArgName, - func->implementingClass(), defaultValue, - func->isUserAdded()); + QString pyArgName = usePyArgs ? pythonArgsAt(argPos) : PYTHON_ARG; + indirections[argIdx] = + writeArgumentConversion(s, argType, CPP_ARG_N(argPos), pyArgName, errorReturn, + func->implementingClass(), arg.defaultValueExpression(), + func->isUserAdded()); } s << '\n'; int numRemovedArgs = OverloadData::numberOfRemovedArguments(func); - s << "if (!PyErr_Occurred()) {\n" << indent; - writeMethodCall(s, func, context, func->arguments().size() - numRemovedArgs); + s << "if (Shiboken::Errors::occurred() == nullptr) {\n" << indent; + writeMethodCall(s, func, context, + overloadData.pythonFunctionWrapperUsesListOfArguments(), + func->arguments().size() - numRemovedArgs, indirections, errorReturn); + if (!func->isConstructor()) writeNoneReturn(s, func, overloadData.hasNonVoidReturnType()); s << outdent << "}\n"; @@ -3026,34 +3180,34 @@ QString CppGenerator::cppToPythonFunctionName(const QString &sourceTypeName, QSt { if (targetTypeName.isEmpty()) targetTypeName = sourceTypeName; - return sourceTypeName + QLatin1String("_CppToPython_") + targetTypeName; + return sourceTypeName + u"_CppToPython_"_s + targetTypeName; } QString CppGenerator::pythonToCppFunctionName(const QString &sourceTypeName, const QString &targetTypeName) { - return sourceTypeName + QLatin1String("_PythonToCpp_") + targetTypeName; + return sourceTypeName + u"_PythonToCpp_"_s + targetTypeName; } QString CppGenerator::pythonToCppFunctionName(const AbstractMetaType &sourceType, const AbstractMetaType &targetType) { return pythonToCppFunctionName(fixedCppTypeName(sourceType), fixedCppTypeName(targetType)); } -QString CppGenerator::pythonToCppFunctionName(const CustomConversion::TargetToNativeConversion *toNative, - const TypeEntry *targetType) +QString CppGenerator::pythonToCppFunctionName(const TargetToNativeConversion &toNative, + const TypeEntryCPtr &targetType) { return pythonToCppFunctionName(fixedCppTypeName(toNative), fixedCppTypeName(targetType)); } QString CppGenerator::convertibleToCppFunctionName(const QString &sourceTypeName, const QString &targetTypeName) { - return QLatin1String("is_") + sourceTypeName + QLatin1String("_PythonToCpp_") - + targetTypeName + QLatin1String("_Convertible"); + return u"is_"_s + sourceTypeName + u"_PythonToCpp_"_s + + targetTypeName + u"_Convertible"_s; } QString CppGenerator::convertibleToCppFunctionName(const AbstractMetaType &sourceType, const AbstractMetaType &targetType) { return convertibleToCppFunctionName(fixedCppTypeName(sourceType), fixedCppTypeName(targetType)); } -QString CppGenerator::convertibleToCppFunctionName(const CustomConversion::TargetToNativeConversion *toNative, - const TypeEntry *targetType) +QString CppGenerator::convertibleToCppFunctionName(const TargetToNativeConversion &toNative, + const TypeEntryCPtr &targetType) { return convertibleToCppFunctionName(fixedCppTypeName(toNative), fixedCppTypeName(targetType)); } @@ -3063,63 +3217,92 @@ 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"; } -static void replaceCppToPythonVariables(QString &code, const QString &typeName) +static QString writeCppInRef(const QString &typeName, bool constRef) { - const QString line = QLatin1String("auto &cppInRef = *reinterpret_cast<") - + typeName + QLatin1String(" *>(const_cast<void *>(cppIn));"); - CodeSnipAbstract::prependCode(&code, line); - code.replace(QLatin1String("%INTYPE"), typeName); - code.replace(QLatin1String("%OUTTYPE"), QLatin1String("PyObject *")); - code.replace(QLatin1String("%in"), QLatin1String("cppInRef")); - code.replace(QLatin1String("%out"), QLatin1String("pyOut")); + QString result; + QTextStream str(&result); + if (constRef) + str << "const "; + str << "auto &cppInRef = *reinterpret_cast<"; + if (constRef) + str << "const "; + str << typeName << " *>(" + << (constRef ? "cppIn" : "const_cast<void *>(cppIn)") << ");"; + return result; } -void CppGenerator::writeCppToPythonFunction(TextStream &s, const CustomConversion *customConversion) const +static void replaceCppToPythonVariables(QString &code, const QString &typeName, + bool constRef = false) +{ + CodeSnipAbstract::prependCode(&code, writeCppInRef(typeName, constRef)); + code.replace(u"%INTYPE"_s, typeName); + code.replace(u"%OUTTYPE"_s, u"PyObject *"_s); + code.replace(u"%in"_s, u"cppInRef"_s); + code.replace(u"%out"_s, u"pyOut"_s); +} + +void CppGenerator::writeCppToPythonFunction(TextStream &s, + const CustomConversionPtr &customConversion) const { QString code = customConversion->nativeToTargetConversion(); - replaceCppToPythonVariables(code, getFullTypeName(customConversion->ownerType())); + auto ownerType = customConversion->ownerType(); + const bool constRef = !ownerType->isPrimitive(); // PyCapsule needs a non-const ref + replaceCppToPythonVariables(code, getFullTypeName(ownerType), constRef); writeCppToPythonFunction(s, code, fixedCppTypeName(customConversion->ownerType())); } + +QString CppGenerator::containerNativeToTargetTypeName(const ContainerTypeEntryCPtr &type) +{ + QString result = type->targetLangApiName(); + if (result != cPyObjectT) { + result = containerCpythonBaseName(type); + if (result == cPySequenceT) + result = cPyListT; + } + return result; +} + void CppGenerator::writeCppToPythonFunction(TextStream &s, const AbstractMetaType &containerType) const { - const CustomConversion *customConversion = containerType.typeEntry()->customConversion(); - if (!customConversion) { + Q_ASSERT(containerType.typeEntry()->isContainer()); + auto cte = std::static_pointer_cast<const ContainerTypeEntry>(containerType.typeEntry()); + if (!cte->hasCustomConversion()) { QString m; QTextStream(&m) << "Can't write the C++ to Python conversion function for container type '" << containerType.typeEntry()->qualifiedCppName() << "' - no conversion rule was defined for it in the type system."; throw Exception(m); } - if (!containerType.typeEntry()->isContainer()) { - writeCppToPythonFunction(s, customConversion); - return; - } + const auto customConversion = cte->customConversion(); QString code = customConversion->nativeToTargetConversion(); - for (int i = 0; i < containerType.instantiations().count(); ++i) { + for (qsizetype i = 0; i < containerType.instantiations().size(); ++i) { const AbstractMetaType &type = containerType.instantiations().at(i); QString typeName = getFullTypeName(type); if (type.isConstant()) - typeName = QLatin1String("const ") + typeName; - code.replace(QString::fromLatin1("%INTYPE_%1").arg(i), typeName); + typeName = u"const "_s + typeName; + code.replace(u"%INTYPE_"_s + QString::number(i), typeName); } - replaceCppToPythonVariables(code, getFullTypeNameWithoutModifiers(containerType)); - processCodeSnip(code); - writeCppToPythonFunction(s, code, fixedCppTypeName(containerType)); + replaceCppToPythonVariables(code, getFullTypeNameWithoutModifiers(containerType), true); + processCodeSnip(code, containerType.typeEntry()->qualifiedCppName()); + writeCppToPythonFunction(s, code, fixedCppTypeName(containerType), + containerNativeToTargetTypeName(cte)); } void CppGenerator::writePythonToCppFunction(TextStream &s, const QString &code, const QString &sourceTypeName, const QString &targetTypeName) const { QString prettyCode = code; - 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"; } @@ -3137,16 +3320,15 @@ void CppGenerator::writeIsPythonConvertibleToCppFunction(TextStream &s, s << "static PythonToCppFunc " << convertibleToCppFunctionName(sourceTypeName, targetTypeName); s << "(PyObject *pyIn)\n{\n" << indent; if (acceptNoneAsCppNull) { - s << "if (pyIn == Py_None)\n"; - Indentation indent(s); - s << "return Shiboken::Conversions::nonePythonToCppNullPtr;\n"; - } - s << "if (" << condition << ")\n"; - { - Indentation indent(s); - s << "return " << pythonToCppFuncName << ";\n"; + s << "if (pyIn == Py_None)\n" << indent + << "return Shiboken::Conversions::nonePythonToCppNullPtr;\n" << outdent; + } else { + if (!condition.contains(u"pyIn")) + s << sbkUnusedVariableCast("pyIn"); } - s << "return {};\n" << outdent << "}\n"; + s << "if (" << condition << ")\n" << indent + << "return " << pythonToCppFuncName << ";\n" << outdent + << "return {};\n" << outdent << "}\n"; } void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, @@ -3161,102 +3343,97 @@ void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, // Python to C++ conversion function. StringStream c(TextStream::Language::Cpp); if (conversion.isEmpty()) - conversion = QLatin1Char('*') + cpythonWrapperCPtr(sourceType, QLatin1String("pyIn")); + conversion = u'*' + cpythonWrapperCPtr(sourceType, u"pyIn"_s); if (!preConversion.isEmpty()) c << preConversion << '\n'; const QString fullTypeName = targetType.isSmartPointer() ? targetType.cppSignature() : getFullTypeName(targetType.typeEntry()); c << "*reinterpret_cast<" << fullTypeName << " *>(cppOut) = " - << fullTypeName << '(' << conversion << ");"; + << fullTypeName << '(' + << (sourceType.isUniquePointer() ? stdMove(conversion) : conversion) + << ");"; QString sourceTypeName = fixedCppTypeName(sourceType); QString targetTypeName = fixedCppTypeName(targetType); writePythonToCppFunction(s, c.toString(), sourceTypeName, targetTypeName); // Python to C++ convertible check function. if (typeCheck.isEmpty()) - typeCheck = QString::fromLatin1("PyObject_TypeCheck(pyIn, %1)").arg(sourcePyType); + typeCheck = u"PyObject_TypeCheck(pyIn, "_s + sourcePyType + u')'; writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, typeCheck); s << '\n'; } void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, - const CustomConversion::TargetToNativeConversion *toNative, - const TypeEntry *targetType) const + const TargetToNativeConversion &toNative, + const TypeEntryCPtr &targetType) const { // Python to C++ conversion function. - QString code = toNative->conversion(); + QString code = toNative.conversion(); QString inType; - if (toNative->sourceType()) - inType = cpythonTypeNameExt(toNative->sourceType()); + if (toNative.sourceType()) + inType = cpythonTypeNameExt(toNative.sourceType()); else - inType = QString::fromLatin1("(%1_TypeF())").arg(toNative->sourceTypeName()); - code.replace(QLatin1String("%INTYPE"), inType); - code.replace(QLatin1String("%OUTTYPE"), targetType->qualifiedCppName()); - code.replace(QLatin1String("%in"), QLatin1String("pyIn")); - code.replace(QLatin1String("%out"), - QLatin1String("*reinterpret_cast<") + getFullTypeName(targetType) + QLatin1String(" *>(cppOut)")); + inType = u'(' + toNative.sourceTypeName() + u"_TypeF())"_s; + code.replace(u"%INTYPE"_s, inType); + code.replace(u"%OUTTYPE"_s, targetType->qualifiedCppName()); + code.replace(u"%in"_s, u"pyIn"_s); + code.replace(u"%out"_s, + u"*reinterpret_cast<"_s + getFullTypeName(targetType) + u" *>(cppOut)"_s); QString sourceTypeName = fixedCppTypeName(toNative); QString targetTypeName = fixedCppTypeName(targetType); writePythonToCppFunction(s, code, sourceTypeName, targetTypeName); // Python to C++ convertible check function. - QString typeCheck = toNative->sourceTypeCheck(); + QString typeCheck = toNative.sourceTypeCheck(); if (typeCheck.isEmpty()) { - QString pyTypeName = toNative->sourceTypeName(); - if (pyTypeName == QLatin1String("Py_None") || pyTypeName == QLatin1String("PyNone")) - typeCheck = QLatin1String("%in == Py_None"); - else if (pyTypeName == QLatin1String("SbkEnumType")) - typeCheck = QLatin1String("Shiboken::isShibokenEnum(%in)"); - else if (pyTypeName == QLatin1String("SbkObject")) - typeCheck = QLatin1String("Shiboken::Object::checkType(%in)"); - else if (pyTypeName == cPyTypeObjectT()) - typeCheck = QLatin1String("PyType_Check(%in)"); - else if (pyTypeName == cPyObjectT()) - typeCheck = QLatin1String("PyObject_TypeCheck(%in, &PyBaseObject_Type)"); - // PYSIDE-795: We abuse PySequence for iterables - else if (pyTypeName == cPySequenceT()) - typeCheck = QLatin1String("Shiboken::String::checkIterable(%in)"); - else if (pyTypeName.startsWith(QLatin1String("Py"))) - typeCheck = pyTypeName + QLatin1String("_Check(%in)"); + QString pyTypeName = toNative.sourceTypeName(); + if (pyTypeName == u"Py_None" || pyTypeName == u"PyNone") + typeCheck = u"%in == Py_None"_s; + else if (pyTypeName == u"SbkObject") + typeCheck = u"Shiboken::Object::checkType(%in)"_s; } if (typeCheck.isEmpty()) { - if (!toNative->sourceType() || toNative->sourceType()->isPrimitive()) { + if (!toNative.sourceType() || toNative.sourceType()->isPrimitive()) { QString m; QTextStream(&m) << "User added implicit conversion for C++ type '" << targetType->qualifiedCppName() << "' must provide either an input type check function or a non primitive type entry."; throw Exception(m); } - typeCheck = QString::fromLatin1("PyObject_TypeCheck(%in, %1)").arg(cpythonTypeNameExt(toNative->sourceType())); + typeCheck = u"PyObject_TypeCheck(%in, "_s + + cpythonTypeNameExt(toNative.sourceType()) + u')'; } - typeCheck.replace(QLatin1String("%in"), QLatin1String("pyIn")); - processCodeSnip(typeCheck); + typeCheck.replace(u"%in"_s, u"pyIn"_s); + processCodeSnip(typeCheck, targetType->qualifiedCppName()); writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, typeCheck); } void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, const AbstractMetaType &containerType) const { - const CustomConversion *customConversion = containerType.typeEntry()->customConversion(); - if (!customConversion) { - //qFatal - return; - } - const CustomConversion::TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions(); - if (toCppConversions.isEmpty()) { - //qFatal - return; - } + Q_ASSERT(containerType.typeEntry()->isContainer()); + const auto cte = std::static_pointer_cast<const ContainerTypeEntry>(containerType.typeEntry()); + const auto customConversion = cte->customConversion(); + for (const auto &conv : customConversion->targetToNativeConversions()) + writePythonToCppConversionFunction(s, containerType, conv); +} + +void CppGenerator::writePythonToCppConversionFunction(TextStream &s, + const AbstractMetaType &containerType, + const TargetToNativeConversion &conv) const +{ // Python to C++ conversion function. QString cppTypeName = getFullTypeNameWithoutModifiers(containerType); - QString code = toCppConversions.constFirst()->conversion(); - const QString line = QLatin1String("auto &cppOutRef = *reinterpret_cast<") - + cppTypeName + QLatin1String(" *>(cppOut);"); + QString code = conv.conversion(); + const QString line = u"auto &cppOutRef = *reinterpret_cast<"_s + + cppTypeName + u" *>(cppOut);"_s; CodeSnipAbstract::prependCode(&code, line); - for (int i = 0; i < containerType.instantiations().count(); ++i) { + for (qsizetype i = 0; i < containerType.instantiations().size(); ++i) { const AbstractMetaType &type = containerType.instantiations().at(i); QString typeName = getFullTypeName(type); - if (valueTypeWithCopyConstructorOnlyPassed(api(), type)) { + // Containers of opaque containers are not handled here. + const auto generatorArg = GeneratorArgument::fromMetaType(type); + if (generatorArg.indirections > 0 && !type.generateOpaqueContainer()) { for (int pos = 0; ; ) { const QRegularExpressionMatch match = convertToCppRegEx().match(code, pos); if (!match.hasMatch()) @@ -3264,171 +3441,220 @@ void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, const Abst pos = match.capturedEnd(); const QString varName = match.captured(1); QString rightCode = code.mid(pos); - rightCode.replace(varName, QLatin1Char('*') + varName); + rightCode.replace(varName, u'*' + varName); code.replace(pos, code.size() - pos, rightCode); } - typeName.append(QLatin1String(" *")); + typeName.append(u" *"_s); } - code.replace(QString::fromLatin1("%OUTTYPE_%1").arg(i), typeName); + code.replace(u"%OUTTYPE_"_s + QString::number(i), typeName); } - code.replace(QLatin1String("%OUTTYPE"), cppTypeName); - code.replace(QLatin1String("%in"), QLatin1String("pyIn")); - code.replace(QLatin1String("%out"), QLatin1String("cppOutRef")); + code.replace(u"%OUTTYPE"_s, cppTypeName); + code.replace(u"%in"_s, u"pyIn"_s); + code.replace(u"%out"_s, u"cppOutRef"_s); QString typeName = fixedCppTypeName(containerType); - writePythonToCppFunction(s, code, typeName, typeName); + const QString &sourceTypeName = conv.sourceTypeName(); + writePythonToCppFunction(s, code, sourceTypeName, typeName); // Python to C++ convertible check function. QString typeCheck = cpythonCheckFunction(containerType); if (typeCheck.isEmpty()) - typeCheck = QLatin1String("false"); + typeCheck = u"false"_s; else - typeCheck = typeCheck + QLatin1String("pyIn)"); - writeIsPythonConvertibleToCppFunction(s, typeName, typeName, typeCheck); + typeCheck = typeCheck + u"pyIn)"_s; + writeIsPythonConvertibleToCppFunction(s, sourceTypeName, typeName, typeCheck); s << '\n'; } +static void writeSetConverterFunction(TextStream &s, + const char *function, + const QString &converterVar, + const QString &pythonToCppFunc, + const QString &isConvertibleFunc) +{ + s << "Shiboken::Conversions::" << function << '(' << converterVar << ',' << '\n' + << indent << pythonToCppFunc << ',' << '\n' << isConvertibleFunc + << outdent << ");\n"; +} + void CppGenerator::writeAddPythonToCppConversion(TextStream &s, const QString &converterVar, const QString &pythonToCppFunc, const QString &isConvertibleFunc) { - s << "Shiboken::Conversions::addPythonToCppValueConversion(" << converterVar << ',' << '\n'; - { - Indentation indent(s); - s << pythonToCppFunc << ',' << '\n' << isConvertibleFunc; - } - s << ");\n"; + writeSetConverterFunction(s, "addPythonToCppValueConversion", + converterVar, pythonToCppFunc, isConvertibleFunc); +} + +void CppGenerator::writeSetPythonToCppPointerConversion(TextStream &s, + const QString &converterVar, + const QString &pythonToCppFunc, + const QString &isConvertibleFunc) +{ + writeSetConverterFunction(s, "setPythonToCppPointerFunctions", + converterVar, pythonToCppFunc, isConvertibleFunc); +} + +// PYSIDE-1986: Some QObject derived classes, (QVBoxLayout) do not have default +// arguments, which breaks setting properties by named arguments. Force the +// handling code to be generated nevertheless for applicable widget classes, +// so that the mechanism of falling through to the error handling to set +// the properties works nevertheless. +static bool forceQObjectNamedArguments(const AbstractMetaFunctionCPtr &func) +{ + if (func->functionType() != AbstractMetaFunction::ConstructorFunction) + return false; + const auto owner = func->ownerClass(); + Q_ASSERT(owner); + if (!isQObject(owner)) + return false; + const QString &name = owner->name(); + return name == u"QVBoxLayout" || name == u"QHBoxLayout" + || name == u"QSplitterHandle" || name == u"QSizeGrip"; } -void CppGenerator::writeNamedArgumentResolution(TextStream &s, const AbstractMetaFunctionCPtr &func, - bool usePyArgs, const OverloadData &overloadData) const +void CppGenerator::writeNamedArgumentResolution(TextStream &s, + const AbstractMetaFunctionCPtr &func, + bool usePyArgs, + const OverloadData &overloadData, + ErrorReturn errorReturn) { const AbstractMetaArgumentList &args = OverloadData::getArgumentsWithDefaultValues(func); - if (args.isEmpty()) { + const bool hasDefaultArguments = !args.isEmpty(); + const bool force = !hasDefaultArguments && usePySideExtensions() + && forceQObjectNamedArguments(func); + if (!hasDefaultArguments && !force) { if (overloadData.hasArgumentWithDefaultValue()) { - s << "if (kwds) {\n"; - { - Indentation indent(s); - s << "errInfo.reset(kwds);\n" - << "Py_INCREF(errInfo.object());\n" - << "goto " << cpythonFunctionName(func) << "_TypeError;\n"; - } - s << "}\n"; + // PySide-535: Allow for empty dict instead of nullptr in PyPy + s << "if (kwds != nullptr && PyDict_Size(kwds) > 0) {\n" << indent + << "errInfo.reset(kwds);\n" + << "Py_INCREF(errInfo.object());\n" + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" + << outdent << "}\n"; } return; } - s << "if (kwds) {\n"; - { - Indentation indent(s); - s << "PyObject *value{};\n" - << "PyObject *kwds_dup = PyDict_Copy(kwds);\n"; - for (const AbstractMetaArgument &arg : args) { - const int pyArgIndex = arg.argumentIndex() - - OverloadData::numberOfRemovedArguments(func, arg.argumentIndex()); - QString pyArgName = usePyArgs ? pythonArgsAt(pyArgIndex) : QLatin1String(PYTHON_ARG); - QString pyKeyName = QLatin1String("key_") + arg.name(); - s << "static PyObject *const " << pyKeyName - << " = Shiboken::String::createStaticString(\"" << arg.name() << "\");\n" - << "if (PyDict_Contains(kwds, " << pyKeyName << ")) {\n"; - { - Indentation indent(s); - s << "value = PyDict_GetItem(kwds, " << pyKeyName << ");\n" - << "if (value && " << pyArgName << ") {\n"; - { - Indentation indent(s); - s << "errInfo.reset(" << pyKeyName << ");\n" - << "Py_INCREF(errInfo.object());\n" - << "goto " << cpythonFunctionName(func) << "_TypeError;\n"; - } - s << "}\nif (value) {\n"; - { - Indentation indent(s); - s << pyArgName << " = value;\nif (!"; - writeTypeCheck(s, arg.type(), pyArgName, isNumber(arg.type().typeEntry()), - func->typeReplaced(arg.argumentIndex() + 1)); - s << ")\n"; - { - Indentation indent(s); - s << "goto " << cpythonFunctionName(func) << "_TypeError;\n"; - } - } - s << "}\nPyDict_DelItem(kwds_dup, " << pyKeyName << ");\n"; - } - s << "}\n"; - } - // PYSIDE-1305: Handle keyword args correctly. - // Normal functions handle their parameters immediately. - // For constructors that are QObject, we need to delay that - // until extra keyword signals and properties are handled. - s << "if (PyDict_Size(kwds_dup) > 0) {\n"; - { - Indentation indent(s); - s << "errInfo.reset(kwds_dup);\n"; - if (!(func->isConstructor() && func->ownerClass()->isQObject())) - s << "goto " << cpythonFunctionName(func) << "_TypeError;\n"; - else - s << "// fall through to handle extra keyword signals and properties\n"; - } - s << "}\n"; - } - s << "}\n"; + // PySide-535: Allow for empty dict instead of nullptr in PyPy + s << "if (kwds && PyDict_Size(kwds) > 0) {\n" << indent; + if (!force) + s << "PyObject *value{};\n"; + s << "Shiboken::AutoDecRef kwds_dup(PyDict_Copy(kwds));\n"; + for (const AbstractMetaArgument &arg : args) { + const int pyArgIndex = arg.argumentIndex() + - OverloadData::numberOfRemovedArguments(func, arg.argumentIndex()); + QString pyArgName = usePyArgs ? pythonArgsAt(pyArgIndex) + : PYTHON_ARG; + QString pyKeyName = u"key_"_s + arg.name(); + s << "static PyObject *const " << pyKeyName + << " = Shiboken::String::createStaticString(\"" << arg.name() << "\");\n" + << "if (PyDict_Contains(kwds, " << pyKeyName << ") != 0) {\n" << indent + << "value = PyDict_GetItem(kwds, " << pyKeyName << ");\n" + << "if (value != nullptr && " << pyArgName << " != nullptr ) {\n" + << indent << "errInfo.reset(" << pyKeyName << ");\n" + << "Py_INCREF(errInfo.object());\n" + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" + << outdent << "}\nif (value != nullptr) {\n" << indent + << pyArgName << " = value;\nif (!"; + const auto &type = arg.modifiedType(); + writeTypeCheck(s, type, pyArgName, isNumber(type.typeEntry()), {}); + s << ")\n" << indent + << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n" + << outdent << outdent + << "}\nPyDict_DelItem(kwds_dup, " << pyKeyName << ");\n" + << outdent << "}\n"; + } + // PYSIDE-1305: Handle keyword args correctly. + // Normal functions handle their parameters immediately. + // For constructors that are QObject, we need to delay that + // until extra keyword signals and properties are handled. + s << "if (PyDict_Size(kwds_dup) > 0) {\n" << indent + << "errInfo.reset(kwds_dup.release());\n"; + if (!(func->isConstructor() && isQObject(func->ownerClass()))) + s << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"; + else + s << "// fall through to handle extra keyword signals and properties\n"; + s << outdent << "}\n" + << outdent << "}\n"; } QString CppGenerator::argumentNameFromIndex(const ApiExtractorResult &api, - const AbstractMetaFunctionCPtr &func, int argIndex, - const AbstractMetaClass **wrappedClass, - QString *errorMessage) -{ - if (errorMessage != nullptr) - errorMessage->clear(); - *wrappedClass = nullptr; - QString pyArgName; - if (argIndex == -1) { - pyArgName = QLatin1String("self"); - *wrappedClass = func->implementingClass(); - } else if (argIndex == 0) { - const auto funcType = func->type(); - AbstractMetaType returnType = getTypeWithoutContainer(funcType); - if (!returnType.isVoid()) { - pyArgName = QLatin1String(PYTHON_RETURN_VAR); - *wrappedClass = AbstractMetaClass::findClass(api.classes(), returnType.typeEntry()); - if (*wrappedClass == nullptr && errorMessage != nullptr) - *errorMessage = msgClassNotFound(returnType.typeEntry()); - } else { - if (errorMessage != nullptr) { - QTextStream str(errorMessage); - str << "Invalid Argument index (0, return value) on function modification: " - << funcType.name() << ' '; - if (const AbstractMetaClass *declaringClass = func->declaringClass()) - str << declaringClass->name() << "::"; - str << func->name() << "()"; - } - } + const AbstractMetaFunctionCPtr &func, int argIndex) +{ + switch (argIndex) { + case -1: + return PYTHON_SELF_VAR; + case 0: + return PYTHON_RETURN_VAR; + case 1: { // Single argument? + OverloadData data(getFunctionGroups(func->implementingClass()).value(func->name()), api); + if (!data.pythonFunctionWrapperUsesListOfArguments()) + return PYTHON_ARG; + break; + } + } + return pythonArgsAt(argIndex - 1); +} + +AbstractMetaClassCPtr +CppGenerator::argumentClassFromIndex(const ApiExtractorResult &api, + const AbstractMetaFunctionCPtr &func, int argIndex) +{ + if (argIndex == -1) + return func->implementingClass(); + + AbstractMetaType type; + if (argIndex == 0) { + type = func->type(); } else { - int realIndex = argIndex - 1 - OverloadData::numberOfRemovedArguments(func, argIndex - 1); - AbstractMetaType argType = getTypeWithoutContainer(func->arguments().at(realIndex).type()); - *wrappedClass = AbstractMetaClass::findClass(api.classes(), argType.typeEntry()); - if (*wrappedClass == nullptr && errorMessage != nullptr) - *errorMessage = msgClassNotFound(argType.typeEntry()); - if (argIndex == 1 - && !func->isConstructor() - && OverloadData::isSingleArgument(getFunctionGroups(func->implementingClass()).value(func->name()))) - pyArgName = QLatin1String(PYTHON_ARG); - else - pyArgName = pythonArgsAt(argIndex - 1); + const int arg = argIndex - 1; + const int realIndex = arg - OverloadData::numberOfRemovedArguments(func, arg); + type = func->arguments().at(realIndex).type(); + } + + if (type.typeEntry()->isContainer()) { + // only support containers with 1 type + if (type.instantiations().size() == 1) + type = type.instantiations().constFirst(); } - return pyArgName; + + auto te = type.typeEntry(); + if (type.isVoid() || !te->isComplex()) + throw Exception(msgInvalidArgumentModification(func, argIndex)); + const auto result = AbstractMetaClass::findClass(api.classes(), te); + if (!result) + throw Exception(msgClassNotFound(te)); + return result; } +const char tryBlock[] = R"( +PyObject *errorType{}; +PyObject *errorString{}; +try { +)"; + const char defaultExceptionHandling[] = R"(} catch (const std::exception &e) { - PyErr_SetString(PyExc_RuntimeError, e.what()); + errorType = PyExc_RuntimeError; + errorString = Shiboken::String::fromCString(e.what()); } catch (...) { - PyErr_SetString(PyExc_RuntimeError, "An unknown exception was caught"); + errorType = PyExc_RuntimeError; + errorString = Shiboken::Messages::unknownException(); } )"; +const char propagateException[] = R"( +if (errorType != nullptr) + PyErr_SetObject(errorType, errorString); +)"; + +static QString explicitConversion(const QString &v, const AbstractMetaType &t) +{ + return t.plainType().cppSignature() + u'(' + v + u')'; +} + void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr &func, - const GeneratorContext &context, int maxArgs) const + const GeneratorContext &context, bool usesPyArgs, + int maxArgs, + const QList<qsizetype> &argumentIndirections, + ErrorReturn errorReturn) const { s << "// " << func->minimalSignature() << (func->isReverseOperator() ? " [reverse operator]": "") << '\n'; if (func->isConstructor()) { @@ -3445,14 +3671,10 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr } if (func->isAbstract()) { - s << "if (Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(self))) {\n"; - { - Indentation indent(s); - s << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '"; - s << func->ownerClass()->name() << '.' << func->name() << "()' not implemented.\");\n"; - s << returnStatement(m_currentErrorCode) << '\n'; - } - s << "}\n"; + s << "if (Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(self))) {\n" + << indent << "Shiboken::Errors::setPureVirtualMethodError(\"" + << func->ownerClass()->name() << '.' << func->name() << "\");\n" + << errorReturn << outdent << "}\n"; } // Used to provide contextual information to custom code writer function. @@ -3468,18 +3690,20 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr if (maxArgs > 0 && maxArgs < func->arguments().size() - OverloadData::numberOfRemovedArguments(func)) { int removedArgs = 0; for (int i = 0; i < maxArgs + removedArgs; i++) { - lastArg = &func->arguments().at(i); - if (func->argumentRemoved(i + 1)) + if (func->arguments().at(i).isModifiedRemoved()) removedArgs++; } } else if (maxArgs != 0 && !func->arguments().isEmpty()) { lastArg = &func->arguments().constLast(); } - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode, func, lastArg); + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode, func, + usesPyArgs, lastArg); } - writeConversionRule(s, func, TypeSystem::NativeCode); + writeConversionRule(s, func, TypeSystem::NativeCode, usesPyArgs); + + bool generateExceptionHandling = false; if (!func->isUserAdded()) { QStringList userArgs; @@ -3487,9 +3711,9 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr int removedArgs = 0; for (int i = 0; i < maxArgs + removedArgs; i++) { const AbstractMetaArgument &arg = func->arguments().at(i); - bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode, - arg.argumentIndex() + 1).isEmpty(); - if (func->argumentRemoved(i + 1)) { + const bool hasConversionRule = + func->hasConversionRule(TypeSystem::NativeCode, arg.argumentIndex() + 1); + if (arg.isModifiedRemoved()) { // If some argument with default value is removed from a // method signature, the said value must be explicitly // added to the method call. @@ -3497,25 +3721,26 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr // If have conversion rules I will use this for removed args if (hasConversionRule) - userArgs << arg.name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX); + userArgs << arg.name() + CONV_RULE_OUT_VAR_SUFFIX; else if (!arg.defaultValueExpression().isEmpty()) - userArgs.append(QLatin1String(CPP_ARG_REMOVED) + QString::number(i)); + userArgs.append(CPP_ARG_REMOVED(i)); } else { - int idx = arg.argumentIndex() - removedArgs; - bool deRef = valueTypeWithCopyConstructorOnlyPassed(api(), arg.type()) - || arg.type().isObjectTypeUsedAsValueType() - || (arg.type().referenceType() == LValueReference - && arg.type().isWrapperType() && !arg.type().isPointer()); if (hasConversionRule) { - userArgs.append(arg.name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX)); + userArgs.append(arg.name() + CONV_RULE_OUT_VAR_SUFFIX); } else { - QString argName; - if (deRef) - argName += QLatin1Char('*'); - argName += QLatin1String(CPP_ARG) + QString::number(idx); + const int idx = arg.argumentIndex() - removedArgs; + const auto deRef = argumentIndirections.at(i); + QString argName = AbstractMetaType::dereferencePrefix(deRef) + + CPP_ARG_N(idx); userArgs.append(argName); } } + // "Pass unique ptr by value" pattern: Apply std::move() + auto type = arg.type(); + if (type.useStdMove()) + userArgs.last() = stdMove(userArgs.constLast()); + else if (type.viewOn() != nullptr) + userArgs.last() = explicitConversion(userArgs.constLast(), type); } // If any argument's default value was modified the method must be called @@ -3525,19 +3750,19 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr QStringList otherArgs; bool otherArgsModified = false; bool argsClear = true; - for (int i = func->arguments().size() - 1; i >= maxArgs + removedArgs; i--) { + for (auto i = func->arguments().size() - 1; i >= maxArgs + removedArgs; i--) { const AbstractMetaArgument &arg = func->arguments().at(i); const bool defValModified = arg.hasModifiedDefaultValueExpression(); - bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode, - arg.argumentIndex() + 1).isEmpty(); + const bool hasConversionRule = + func->hasConversionRule(TypeSystem::NativeCode, arg.argumentIndex() + 1); if (argsClear && !defValModified && !hasConversionRule) continue; argsClear = false; - otherArgsModified |= defValModified || hasConversionRule || func->argumentRemoved(i + 1); + otherArgsModified |= defValModified || hasConversionRule || arg.isModifiedRemoved(); if (hasConversionRule) - otherArgs.prepend(arg.name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX)); + otherArgs.prepend(arg.name() + CONV_RULE_OUT_VAR_SUFFIX); else - otherArgs.prepend(QLatin1String(CPP_ARG_REMOVED) + QString::number(i)); + otherArgs.prepend(CPP_ARG_REMOVED(i)); } if (otherArgsModified) userArgs << otherArgs; @@ -3548,17 +3773,14 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr StringStream uva(TextStream::Language::Cpp); if (func->isOperatorOverload() && !func->isCallOperator()) { - QString firstArg(QLatin1Char('(')); + QString firstArg(u'('); if (!func->isPointerOperator()) // no de-reference operator - firstArg += QLatin1Char('*'); - firstArg += QLatin1String(CPP_SELF_VAR); - firstArg += QLatin1Char(')'); - QString secondArg = QLatin1String(CPP_ARG0); - if (!func->isUnaryOperator() - && func->arguments().constFirst().type().shouldDereferencePointer()) { - secondArg.prepend(QLatin1String("(*")); - secondArg.append(QLatin1Char(')')); - } + firstArg += u'*'; + firstArg += CPP_SELF_VAR; + firstArg += u')'; + QString secondArg = CPP_ARG0; + if (!func->isUnaryOperator()) + AbstractMetaType::applyDereference(&secondArg, argumentIndirections.at(0)); if (func->isUnaryOperator()) std::swap(firstArg, secondArg); @@ -3571,7 +3793,7 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr std::swap(firstArg, secondArg); // Emulate operator+=/-= (__iadd__, __isub__) by using ++/-- - if (((op == QLatin1String("++")) || (op == QLatin1String("--"))) && !func->isReverseOperator()) { + if (((op == u"++") || (op == u"--")) && !func->isReverseOperator()) { s << "\nfor (int i = 0; i < " << secondArg << "; ++i, " << op << firstArg << ");\n"; mc << firstArg; @@ -3586,31 +3808,23 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr isCtor = true; const auto owner = func->ownerClass(); Q_ASSERT(owner == context.metaClass()); - QString className = context.useWrapper() - ? context.wrapperName() : owner->qualifiedCppName(); - - if (func->functionType() == AbstractMetaFunction::CopyConstructorFunction && maxArgs == 1) { - mc << "new ::" << className << "(*" << CPP_ARG0 << ')'; + if (func->functionType() == AbstractMetaFunction::CopyConstructorFunction + && maxArgs == 1) { + mc << "new " << globalScopePrefix(context) << context.effectiveClassName() + << "(*" << CPP_ARG0 << ')'; } else { - QString ctorCall = className + QLatin1Char('(') + userArgs.join(QLatin1String(", ")) + QLatin1Char(')'); - if (usePySideExtensions() && owner->isQObject()) { + const QString ctorCall = context.effectiveClassName() + u'(' + + userArgs.join(u", "_s) + u')'; + if (usePySideExtensions() && isQObject(owner)) { s << "void *addr = PySide::nextQObjectMemoryAddr();\n"; - uva << "if (addr) {\n"; - { - Indentation indent(uva); - - uva << "cptr = new (addr) ::" << ctorCall << ";\n" - << "PySide::setNextQObjectMemoryAddr(0);" - << '\n'; - } - uva << "} else {\n"; - { - Indentation indent(uva); - uva << "cptr = new ::" << ctorCall << ";\n"; - } - uva << "}\n"; + uva << "if (addr != nullptr) {\n" << indent + << "cptr = new (addr) " << globalScopePrefix(context) << ctorCall + << ";\nPySide::setNextQObjectMemoryAddr(nullptr);\n" << outdent + << "} else {\n" << indent + << "cptr = new " << globalScopePrefix(context) << ctorCall << ";\n" + << outdent << "}\n"; } else { - mc << "new ::" << ctorCall; + mc << "new " << globalScopePrefix(context) << ctorCall; } } } else { @@ -3624,21 +3838,22 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr const bool hasWrapper = shouldGenerateCppWrapper(ownerClass); if (!avoidProtectedHack() || !func->isProtected() || !hasWrapper) { if (func->isStatic()) { - mc << "::" << methodCallClassName << "::"; + mc << m_gsp << methodCallClassName << "::"; } else { + const QString cppSelfVar = CPP_SELF_VAR; const QString selfVarCast = func->ownerClass() == func->implementingClass() - ? QLatin1String(CPP_SELF_VAR) - : QLatin1String("reinterpret_cast<") + methodCallClassName - + QLatin1String(" *>(") + QLatin1String(CPP_SELF_VAR) + QLatin1Char(')'); + ? cppSelfVar + : u"reinterpret_cast<"_s + methodCallClassName + + u" *>("_s + cppSelfVar + u')'; if (func->isConstant()) { if (avoidProtectedHack()) { - mc << "const_cast<const ::"; + mc << "const_cast<const " << globalScopePrefix(context); if (ownerClass->cppWrapper().testFlag(AbstractMetaClass::CppProtectedHackWrapper)) { // PYSIDE-500: Need a special wrapper cast when inherited const QString selfWrapCast = ownerClass == func->implementingClass() - ? QLatin1String(CPP_SELF_VAR) - : QLatin1String("reinterpret_cast<") + wrapperName(ownerClass) - + QLatin1String(" *>(") + QLatin1String(CPP_SELF_VAR) + QLatin1Char(')'); + ? cppSelfVar + : u"reinterpret_cast<"_s + wrapperName(ownerClass) + + u" *>("_s + cppSelfVar + u')'; mc << wrapperName(ownerClass); mc << " *>(" << selfWrapCast << ")->"; } @@ -3647,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 { @@ -3663,26 +3878,26 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr if (!func->isStatic()) { const bool directInheritance = context.metaClass() == ownerClass; mc << (directInheritance ? "static_cast" : "reinterpret_cast") - << "<::" << wrapperName(ownerClass) << " *>(" << CPP_SELF_VAR << ")->"; + << '<' << wrapperName(ownerClass) << " *>(" + << CPP_SELF_VAR << ")->"; } if (!func->isAbstract()) mc << (func->isProtected() ? wrapperName(func->ownerClass()) : - QLatin1String("::") - + methodCallClassName) << "::"; + m_gsp + methodCallClassName) << "::"; mc << func->originalName() << "_protected"; } } else { mc << func->originalName(); } - mc << '(' << userArgs.join(QLatin1String(", ")) << ')'; + mc << '(' << userArgs.join(u", "_s) << ')'; if (!func->isAbstract() && func->isVirtual()) { if (!avoidProtectedHack() || !func->isProtected()) { QString virtualCall = mc; QString normalCall = virtualCall; - virtualCall.replace(QLatin1String("%CLASS_NAME"), + virtualCall.replace(u"%CLASS_NAME"_s, methodCallClassName); - normalCall.remove(QLatin1String("::%CLASS_NAME::")); + normalCall.remove(u"::%CLASS_NAME::"_s); mc.clear(); mc << "Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(self))\n" << " ? " << virtualCall << '\n' @@ -3694,9 +3909,9 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr if (!injectedCodeCallsCppFunction(context, func)) { const bool allowThread = func->allowThread(); - const bool generateExceptionHandling = func->generateExceptionHandling(); + generateExceptionHandling = func->generateExceptionHandling(); if (generateExceptionHandling) { - s << "try {\n" << indent; + s << tryBlock << indent; if (allowThread) { s << "Shiboken::ThreadStateSaver threadSaver;\n" << "threadSaver.save();\n"; @@ -3716,13 +3931,13 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr if (metaEnum.has_value()) { QString enumName; if (metaEnum->isProtected()) { - enumName = context.wrapperName() + QLatin1String("::") + enumName = context.wrapperName() + u"::"_s + metaEnum.value().name(); } else { enumName = func->type().cppSignature(); } - const QString methodCall = enumName + QLatin1Char('(') - + mc.toString() + QLatin1Char(')'); + const QString methodCall = enumName + u'(' + + mc.toString() + u')'; mc.clear(); mc << methodCall; s << enumName; @@ -3734,9 +3949,9 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr s << func->type().cppSignature(); if (func->type().isObjectTypeUsedAsValueType()) { s << '*'; - methodCall = QLatin1String("new ") + methodCall = u"new "_s + func->type().typeEntry()->qualifiedCppName() - + QLatin1Char('(') + mc.toString() + QLatin1Char(')'); + + u'(' + mc.toString() + u')'; } } s << " " << CPP_RETURN_VAR << " = " << methodCall << ";\n"; @@ -3746,21 +3961,29 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr if (allowThread) { s << (generateExceptionHandling - ? "threadSaver.restore();" : END_ALLOW_THREADS) << '\n'; + ? u"threadSaver.restore();"_s : END_ALLOW_THREADS) << '\n'; } // Convert result - if (!func->conversionRule(TypeSystem::TargetLangCode, 0).isEmpty()) { - writeConversionRule(s, func, TypeSystem::TargetLangCode, QLatin1String(PYTHON_RETURN_VAR)); + const auto funcType = func->type(); + if (func->hasConversionRule(TypeSystem::TargetLangCode, 0)) { + writeConversionRule(s, func, TypeSystem::TargetLangCode, + PYTHON_RETURN_VAR); } else if (!isCtor && !func->isInplaceOperator() && !func->isVoid() && !func->injectedCodeHasReturnValueAttribution(TypeSystem::TargetLangCode)) { - s << PYTHON_RETURN_VAR << " = "; if (func->type().isObjectTypeUsedAsValueType()) { - s << "Shiboken::Object::newObject(reinterpret_cast<SbkObjectType *>(" + s << PYTHON_RETURN_VAR << " = Shiboken::Object::newObject(" << cpythonTypeNameExt(func->type().typeEntry()) - << "), " << CPP_RETURN_VAR << ", true, true)"; + << ", " << CPP_RETURN_VAR << ", true, true)"; + } else if (func->generateOpaqueContainerReturn()) { + const QString creationFunc = opaqueContainerCreationFunc(funcType); + writeOpaqueContainerCreationFuncDecl(s, creationFunc, funcType); + s << PYTHON_RETURN_VAR << " = " << creationFunc + << "(&" << CPP_RETURN_VAR << ");\n"; } else { - writeToPythonConversion(s, func->type(), func->ownerClass(), QLatin1String(CPP_RETURN_VAR)); + s << PYTHON_RETURN_VAR << " = "; + writeToPythonConversion(s, funcType, func->ownerClass(), + CPP_RETURN_VAR); } s << ";\n"; } @@ -3768,11 +3991,12 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr if (generateExceptionHandling) { // "catch" code s << outdent << defaultExceptionHandling; } - } - } + } // !injected code calls C++ function + } // !userAdded if (func->hasInjectedCode() && !func->isConstructor()) - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode, func, lastArg); + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, + TypeSystem::TargetLangCode, func, usesPyArgs, lastArg); bool hasReturnPolicy = false; @@ -3795,25 +4019,11 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr if (!ownership_mods.isEmpty()) { s << '\n' << "// Ownership transferences.\n"; - for (const ArgumentModification &arg_mod : qAsConst(ownership_mods)) { - const AbstractMetaClass *wrappedClass = nullptr; - QString errorMessage; - QString pyArgName = argumentNameFromIndex(api(), func, arg_mod.index(), - &wrappedClass, &errorMessage); - if (!wrappedClass) { - QString message; - QTextStream str(&message); - str << "Invalid ownership modification for argument " << arg_mod.index() - << " (" << pyArgName << ") of "; - if (const AbstractMetaClass *declaringClass = func->declaringClass()) - str << declaringClass->name() << "::"; - str << func->name() << "(): " << errorMessage; - qCWarning(lcShiboken, "%s", qPrintable(message)); - s << "#error " << message << '\n'; - break; - } + for (const ArgumentModification &arg_mod : std::as_const(ownership_mods)) { + const int argIndex = arg_mod.index(); + const QString pyArgName = argumentNameFromIndex(api(), func, argIndex); - if (arg_mod.index() == 0 || arg_mod.owner().index == 0) + if (argIndex == 0 || arg_mod.owner().index == 0) hasReturnPolicy = true; // The default ownership does nothing. This is useful to avoid automatic heuristically @@ -3825,11 +4035,9 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr s << "Shiboken::Object::"; if (ownership == TypeSystem::TargetLangOwnership) { s << "getOwnership(" << pyArgName << ");"; - } else if (wrappedClass->hasVirtualDestructor()) { - if (arg_mod.index() == 0) - s << "releaseOwnership(" << PYTHON_RETURN_VAR << ");"; - else - s << "releaseOwnership(" << pyArgName << ");"; + } else if (auto ac = argumentClassFromIndex(api(), func, argIndex); + ac && ac->hasVirtualDestructor()) { + s << "releaseOwnership(" << pyArgName << ");"; } else { s << "invalidate(" << pyArgName << ");"; } @@ -3837,7 +4045,7 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr } } else if (!refcount_mods.isEmpty()) { - for (const ArgumentModification &arg_mod : qAsConst(refcount_mods)) { + for (const ArgumentModification &arg_mod : std::as_const(refcount_mods)) { ReferenceCount refCount = arg_mod.referenceCounts().constFirst(); if (refCount.action != ReferenceCount::Set && refCount.action != ReferenceCount::Remove @@ -3845,28 +4053,9 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr qCWarning(lcShiboken) << "\"set\", \"add\" and \"remove\" are the only values supported by Shiboken for action attribute of reference-count tag."; continue; } - const AbstractMetaClass *wrappedClass = nullptr; - - QString pyArgName; - if (refCount.action == ReferenceCount::Remove) { - pyArgName = QLatin1String("Py_None"); - } else { - QString errorMessage; - pyArgName = argumentNameFromIndex(api(), func, arg_mod.index(), - &wrappedClass, &errorMessage); - if (pyArgName.isEmpty()) { - QString message; - QTextStream str(&message); - str << "Invalid reference count modification for argument " - << arg_mod.index() << " of "; - if (const AbstractMetaClass *declaringClass = func->declaringClass()) - str << declaringClass->name() << "::"; - str << func->name() << "(): " << errorMessage; - qCWarning(lcShiboken, "%s", qPrintable(message)); - s << "#error " << message << "\n\n"; - break; - } - } + const int argIndex = arg_mod.index(); + const QString pyArgName = refCount.action == ReferenceCount::Remove + ? u"Py_None"_s : argumentNameFromIndex(api(), func, argIndex); if (refCount.action == ReferenceCount::Add || refCount.action == ReferenceCount::Set) s << "Shiboken::Object::keepReference("; @@ -3876,25 +4065,28 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr s << "reinterpret_cast<SbkObject *>(self), \""; QString varName = arg_mod.referenceCounts().constFirst().varName; if (varName.isEmpty()) - varName = func->minimalSignature() + QString::number(arg_mod.index()); + varName = func->minimalSignature() + QString::number(argIndex); s << varName << "\", " << pyArgName << (refCount.action == ReferenceCount::Add ? ", true" : "") << ");\n"; - if (arg_mod.index() == 0) + if (argIndex == 0) hasReturnPolicy = true; } } - writeParentChildManagement(s, func, !hasReturnPolicy); + writeParentChildManagement(s, func, usesPyArgs, !hasReturnPolicy); + + if (generateExceptionHandling) + s << propagateException; } -QStringList CppGenerator::getAncestorMultipleInheritance(const AbstractMetaClass *metaClass) +QStringList CppGenerator::getAncestorMultipleInheritance(const AbstractMetaClassCPtr &metaClass) { QStringList result; - const AbstractMetaClassList &baseClases = metaClass->typeSystemBaseClasses(); + const auto &baseClases = metaClass->typeSystemBaseClasses(); if (!baseClases.isEmpty()) { - for (const AbstractMetaClass *baseClass : baseClases) { + for (const auto &baseClass : baseClases) { QString offset; QTextStream(&offset) << "reinterpret_cast<uintptr_t>(static_cast<const " << baseClass->qualifiedCppName() << " *>(class_ptr)) - base"; @@ -3907,247 +4099,203 @@ QStringList CppGenerator::getAncestorMultipleInheritance(const AbstractMetaClass result.append(offset); } - for (const AbstractMetaClass *baseClass : baseClases) + for (const auto &baseClass : baseClases) result.append(getAncestorMultipleInheritance(baseClass)); } return result; } -void CppGenerator::writeMultipleInheritanceInitializerFunction(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeMultipleInheritanceInitializerFunction(TextStream &s, + const AbstractMetaClassCPtr &metaClass) { QString className = metaClass->qualifiedCppName(); const QStringList ancestors = getAncestorMultipleInheritance(metaClass); - s << "static int mi_offsets[] = { "; - for (int i = 0; i < ancestors.size(); i++) - s << "-1, "; - s << "-1 };\n" - << "int *\n" + s << "int *\n" << multipleInheritanceInitializerFunctionName(metaClass) << "(const void *cptr)\n" - << "{\n" << indent - << "if (mi_offsets[0] == -1) {\n"; - { - Indentation indent(s); - s << "std::set<int> offsets;\n" - << "const auto *class_ptr = reinterpret_cast<const " << className << " *>(cptr);\n" - << "const auto base = reinterpret_cast<uintptr_t>(class_ptr);\n"; - - for (const QString &ancestor : ancestors) - s << "offsets.insert(int(" << ancestor << "));\n"; - - s << "\noffsets.erase(0);\n\n" - << "std::copy(offsets.cbegin(), offsets.cend(), mi_offsets);\n"; - } - s << "}\nreturn mi_offsets;\n" << outdent << "}\n"; -} - -void CppGenerator::writeSpecialCastFunction(TextStream &s, const AbstractMetaClass *metaClass) + << "{\n" << indent; + s << "static int mi_offsets[] = {-2"; + for (qsizetype i = 0; i < ancestors.size(); i++) + s << ", 0"; + s << "};\n" + << "if (mi_offsets[0] == -2) {\n" << indent + << "const auto *class_ptr = reinterpret_cast<const " << className << " *>(cptr);\n" + << "const auto base = reinterpret_cast<uintptr_t>(class_ptr);\n" + << "int *p = mi_offsets;\n"; + + for (const QString &ancestor : ancestors) + s << "*p++ = int(" << ancestor << ");\n"; + s << "std::sort(mi_offsets, p);\n" + << "auto *end = std::unique(mi_offsets, p);\n" + << "*end++ = -1;\n" + << "if (mi_offsets[0] == 0)\n" + << indent + << "std::memmove(&mi_offsets[0], &mi_offsets[1], (end - mi_offsets - 1) * sizeof(int));\n" + << outdent << outdent + << "}\nreturn mi_offsets;\n" << outdent << "}\n"; +} + +void CppGenerator::writeSpecialCastFunction(TextStream &s, const AbstractMetaClassCPtr &metaClass) { QString className = metaClass->qualifiedCppName(); s << "static void * " << cpythonSpecialCastFunctionName(metaClass) - << "(void *obj, SbkObjectType *desiredType)\n{\n" << indent - << "auto me = reinterpret_cast< ::" << className << " *>(obj);\n"; + << "(void *obj, PyTypeObject *desiredType)\n{\n" << indent + << "auto me = reinterpret_cast< " << m_gsp << className << " *>(obj);\n"; bool firstClass = true; - const AbstractMetaClassList &allAncestors = metaClass->allTypeSystemAncestors(); - for (const AbstractMetaClass *baseClass : allAncestors) { + const auto &allAncestors = metaClass->allTypeSystemAncestors(); + for (const auto &baseClass : allAncestors) { if (!firstClass) s << "else "; - s << "if (desiredType == reinterpret_cast<SbkObjectType *>(" - << cpythonTypeNameExt(baseClass->typeEntry()) << "))\n"; - Indentation indent(s); - s << "return static_cast< ::" << baseClass->qualifiedCppName() << " *>(me);\n"; + s << "if (desiredType == " << cpythonTypeNameExt(baseClass->typeEntry()) + << ")\n" << indent + << "return static_cast< " << getFullTypeName(baseClass) << " *>(me);\n" + << outdent; firstClass = false; } s << "return me;\n" << outdent << "}\n\n"; } void CppGenerator::writePrimitiveConverterInitialization(TextStream &s, - const CustomConversion *customConversion) + const CustomConversionPtr &customConversion) { - const TypeEntry *type = customConversion->ownerType(); + TypeEntryCPtr type = customConversion->ownerType(); QString converter = converterObject(type); s << "// Register converter for type '" << type->qualifiedTargetLangName() << "'.\n" << converter << " = Shiboken::Conversions::createConverter("; - if (type->targetLangApiName() == type->name()) - s << '0'; - else if (type->targetLangApiName() == cPyObjectT()) + if (!type->hasTargetLangApiType()) + s << "nullptr"; + else if (type->targetLangApiName() == cPyObjectT) s << "&PyBaseObject_Type"; else s << '&' << type->targetLangApiName() << "_Type"; QString typeName = fixedCppTypeName(type); s << ", " << cppToPythonFunctionName(typeName, typeName) << ");\n" - << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" - << type->qualifiedCppName() << "\");\n"; + << registerConverterName(type->qualifiedCppName(), converter); writeCustomConverterRegister(s, customConversion, converter); } -void CppGenerator::writeEnumConverterInitialization(TextStream &s, const AbstractMetaEnum &metaEnum) +static void registerConverterInScopes(TextStream &s, QStringView signature, + QAnyStringView varName = converterVar) { - if (metaEnum.isPrivate() || metaEnum.isAnonymous()) - return; - writeEnumConverterInitialization(s, metaEnum.typeEntry()); + while (true) { + s << registerConverterName(signature, varName); + const auto qualifierPos = signature.indexOf("::"_L1); + if (qualifierPos == -1) + break; + signature = signature.sliced(qualifierPos + 2); + } } -void CppGenerator::writeEnumConverterInitialization(TextStream &s, const TypeEntry *enumType) +void CppGenerator::writeEnumConverterInitialization(TextStream &s, const AbstractMetaEnum &metaEnum) { - if (!enumType) + if (metaEnum.isPrivate() || metaEnum.isAnonymous()) return; - QString enumFlagName = enumType->isFlags() ? QLatin1String("flag") : QLatin1String("enum"); - QString enumPythonType = cpythonTypeNameExt(enumType); + EnumTypeEntryCPtr enumType = metaEnum.typeEntry(); + Q_ASSERT(enumType); - const FlagsTypeEntry *flags = nullptr; - if (enumType->isFlags()) - flags = static_cast<const FlagsTypeEntry *>(enumType); + static const char enumPythonVar[] = "EType"; - s << "// Register converter for " << enumFlagName << " '" << enumType->qualifiedCppName() - << "'.\n{\n"; - { - Indentation indent(s); - QString typeName = fixedCppTypeName(enumType); - s << "SbkConverter *converter = Shiboken::Conversions::createConverter(" - << enumPythonType << ',' << '\n'; - { - Indentation indent(s); - s << cppToPythonFunctionName(typeName, typeName) << ");\n"; - } - - if (flags) { - QString enumTypeName = fixedCppTypeName(flags->originator()); - QString toCpp = pythonToCppFunctionName(enumTypeName, typeName); - QString isConv = convertibleToCppFunctionName(enumTypeName, typeName); - writeAddPythonToCppConversion(s, QLatin1String("converter"), toCpp, isConv); - } - - QString toCpp = pythonToCppFunctionName(typeName, typeName); - QString isConv = convertibleToCppFunctionName(typeName, typeName); - writeAddPythonToCppConversion(s, QLatin1String("converter"), toCpp, isConv); + s << "// Register converter for enum '" << enumType->qualifiedCppName() + << "'.\n{\n" << indent; - if (flags) { - QString toCpp = pythonToCppFunctionName(QLatin1String("number"), typeName); - QString isConv = convertibleToCppFunctionName(QLatin1String("number"), typeName); - writeAddPythonToCppConversion(s, QLatin1String("converter"), toCpp, isConv); - } + const QString typeName = fixedCppTypeName(enumType); + s << "SbkConverter *converter = Shiboken::Conversions::createConverter(" + << enumPythonVar << ',' << '\n' << indent + << cppToPythonFunctionName(typeName, typeName) << ");\n" << outdent; - s << "Shiboken::Enum::setTypeConverter(" << enumPythonType << ", converter);\n"; - - QString signature = enumType->qualifiedCppName(); - // Replace "QFlags<Class::Option>" by "Class::Options" - if (flags && signature.startsWith(QLatin1String("QFlags<")) && signature.endsWith(QLatin1Char('>'))) { - signature.chop(1); - signature.remove(0, 7); - const int lastQualifierPos = signature.lastIndexOf(QLatin1String("::")); - if (lastQualifierPos != -1) { - signature.replace(lastQualifierPos + 2, signature.size() - lastQualifierPos - 2, - flags->flagsName()); - } else { - signature = flags->flagsName(); - } - } + const QString toCpp = pythonToCppFunctionName(typeName, typeName); + const QString isConv = convertibleToCppFunctionName(typeName, typeName); + writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv); + s << "Shiboken::Enum::setTypeConverter(" << enumPythonVar + << ", converter);\n"; - while (true) { - s << "Shiboken::Conversions::registerConverterName(converter, \"" - << signature << "\");\n"; - const int qualifierPos = signature.indexOf(QLatin1String("::")); - if (qualifierPos != -1) - signature.remove(0, qualifierPos + 2); - else - break; - } - } - s << "}\n"; + registerConverterInScopes(s, enumType->qualifiedCppName()); + if (auto flags = enumType->flags()) + s << "// Register converter for flag '" << flags->qualifiedCppName() << "'.\n" + << registerConverterName(flags->name()) // QMetaType + << registerConverterName(flags->originalName()); // Signals with flags - if (!flags) - writeEnumConverterInitialization(s, static_cast<const EnumTypeEntry *>(enumType)->flags()); + s << outdent << "}\n"; } -void CppGenerator::writeContainerConverterInitialization(TextStream &s, const AbstractMetaType &type) const +QString CppGenerator::writeContainerConverterInitialization(TextStream &s, + const AbstractMetaType &type, + const ApiExtractorResult &api) { - QByteArray cppSignature = QMetaObject::normalizedSignature(type.cppSignature().toUtf8()); + const auto cppSignature = + QString::fromUtf8(QMetaObject::normalizedSignature(type.cppSignature().toUtf8())); s << "// Register converter for type '" << cppSignature << "'.\n"; - QString converter = converterObject(type); + const QString converter = converterObject(type); s << converter << " = Shiboken::Conversions::createConverter("; - if (type.typeEntry()->targetLangApiName() == cPyObjectT()) { + + Q_ASSERT(type.typeEntry()->isContainer()); + const auto typeEntry = std::static_pointer_cast<const ContainerTypeEntry>(type.typeEntry()); + + const QString targetTypeName = containerNativeToTargetTypeName(typeEntry); + if (targetTypeName == cPyObjectT) { s << "&PyBaseObject_Type"; } else { - QString baseName = cpythonBaseName(type.typeEntry()); - if (baseName == cPySequenceT()) - baseName = cPyListT(); - s << '&' << baseName << "_Type"; + s << '&' << targetTypeName << "_Type"; } - QString typeName = fixedCppTypeName(type); - s << ", " << cppToPythonFunctionName(typeName, typeName) << ");\n"; - QString toCpp = pythonToCppFunctionName(typeName, typeName); - QString isConv = convertibleToCppFunctionName(typeName, typeName); - s << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << cppSignature << "\");\n"; - if (usePySideExtensions() && cppSignature.startsWith("const ") && cppSignature.endsWith("&")) { - cppSignature.chop(1); - cppSignature.remove(0, sizeof("const ") / sizeof(char) - 1); - s << "Shiboken::Conversions::registerConverterName(" << converter << ", \"" << cppSignature << "\");\n"; - } - writeAddPythonToCppConversion(s, converterObject(type), toCpp, isConv); -} - -void CppGenerator::writeSmartPointerConverterInitialization(TextStream &s, const AbstractMetaType &type) const -{ - const QByteArray cppSignature = type.cppSignature().toUtf8(); - auto writeConversionRegister = [&s](const AbstractMetaType &sourceType, const QString &targetTypeName, const QString &targetConverter) - { - const QString sourceTypeName = fixedCppTypeName(sourceType); - const QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); - const QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName); - writeAddPythonToCppConversion(s, targetConverter, toCpp, isConv); - }; + const QString typeName = fixedCppTypeName(type); + s << ", " << cppToPythonFunctionName(typeName, targetTypeName) << ");\n"; - auto klass = AbstractMetaClass::findClass(api().classes(), type.instantiations().at(0).typeEntry()); - if (!klass) - return; + s << registerConverterName(cppSignature, converter); + if (usePySideExtensions() && cppSignature.startsWith("const "_L1) + && cppSignature.endsWith(u'&')) { + auto underlyingType = QStringView{cppSignature}.sliced(6, cppSignature.size() - 7); + s << registerConverterName(underlyingType, converter); + } - const auto classes = klass->typeSystemBaseClasses(); - if (classes.isEmpty()) - return; + for (const auto &conv : typeEntry->customConversion()->targetToNativeConversions()) { + const QString &sourceTypeName = conv.sourceTypeName(); + QString toCpp = pythonToCppFunctionName(sourceTypeName, typeName); + QString isConv = convertibleToCppFunctionName(sourceTypeName, typeName); + writeAddPythonToCppConversion(s, converter, toCpp, isConv); + } - s << "// Register SmartPointer converter for type '" << cppSignature << "'." << '\n' - << "///////////////////////////////////////////////////////////////////////////////////////\n\n"; - - for (auto k : classes) { - auto smartTargetType = findSmartPointerInstantiation(k->typeEntry()); - if (smartTargetType.has_value()) { - s << "// Convert to SmartPointer derived class: [" - << smartTargetType->cppSignature() << "]\n"; - const QString converter = - QLatin1String("Shiboken::Conversions::getConverter(\"%1\")").arg(smartTargetType->cppSignature()); - writeConversionRegister(type, fixedCppTypeName(smartTargetType.value()), converter); - } else { - s << "// Class not found:" << type.instantiations().at(0).cppSignature(); + auto typedefItPair = api.typedefTargetToName().equal_range(type.cppSignature()); + if (typedefItPair.first != typedefItPair.second) { + auto *typeDb = TypeDatabase::instance(); + s << "// Register converters for type aliases of " << cppSignature << "'.\n"; + for (auto it = typedefItPair.first; it != typedefItPair.second; ++it) { + if (!typeDb->findType(it.value())) + s << registerConverterName(it.value(), converter); } } - s << "///////////////////////////////////////////////////////////////////////////////////////" << '\n' << '\n'; + return converter; } -void CppGenerator::writeExtendedConverterInitialization(TextStream &s, const TypeEntry *externalType, +QString CppGenerator::typeInitStruct(const TypeEntryCPtr &te) +{ + return cppApiVariableName(te->targetLangPackage()) + u'[' + + getTypeIndexVariableName(te) + u']'; +} + +void CppGenerator::writeExtendedConverterInitialization(TextStream &s, + const TypeEntryCPtr &externalType, const AbstractMetaClassCList &conversions) { s << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName() << ".\n"; - for (const AbstractMetaClass *sourceClass : conversions) { - const QString converterVar = QLatin1String("reinterpret_cast<SbkObjectType *>(") - + cppApiVariableName(externalType->targetLangPackage()) + QLatin1Char('[') - + getTypeIndexVariableName(externalType) + QLatin1String("])"); + for (const auto &sourceClass : conversions) { QString sourceTypeName = fixedCppTypeName(sourceClass->typeEntry()); QString targetTypeName = fixedCppTypeName(externalType); QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName); - writeAddPythonToCppConversion(s, converterVar, toCpp, isConv); + if (!externalType->isPrimitive()) + s << cpythonTypeNameExt(externalType) << ";\n"; + writeAddPythonToCppConversion(s, typeInitStruct(externalType), toCpp, isConv); } } -QString CppGenerator::multipleInheritanceInitializerFunctionName(const AbstractMetaClass *metaClass) +QString CppGenerator::multipleInheritanceInitializerFunctionName(const AbstractMetaClassCPtr &metaClass) { - return cpythonBaseName(metaClass->typeEntry()) + QLatin1String("_mi_init"); + return cpythonBaseName(metaClass->typeEntry()) + u"_mi_init"_s; } -bool CppGenerator::supportsMappingProtocol(const AbstractMetaClass *metaClass) +bool CppGenerator::supportsMappingProtocol(const AbstractMetaClassCPtr &metaClass) { for (const auto &m : mappingProtocols()) { if (metaClass->hasFunction(m.name)) @@ -4157,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() @@ -4166,18 +4314,18 @@ bool CppGenerator::supportsNumberProtocol(const AbstractMetaClass *metaClass) co || hasBoolCast(metaClass); } -bool CppGenerator::supportsSequenceProtocol(const AbstractMetaClass *metaClass) +bool CppGenerator::supportsSequenceProtocol(const AbstractMetaClassCPtr &metaClass) { for (const auto &seq : sequenceProtocols()) { if (metaClass->hasFunction(seq.name)) return true; } - const ComplexTypeEntry *baseType = metaClass->typeEntry()->baseContainerType(); + ComplexTypeEntryCPtr baseType = metaClass->typeEntry()->baseContainerType(); return baseType && baseType->isContainer(); } -bool CppGenerator::shouldGenerateGetSetList(const AbstractMetaClass *metaClass) const +bool CppGenerator::shouldGenerateGetSetList(const AbstractMetaClassCPtr &metaClass) { for (const AbstractMetaField &f : metaClass->fields()) { if (!f.isStatic()) @@ -4195,63 +4343,57 @@ bool CppGenerator::shouldGenerateGetSetList(const AbstractMetaClass *metaClass) struct pyTypeSlotEntry { - explicit pyTypeSlotEntry(const char *name, const QString &function) : + explicit pyTypeSlotEntry(QAnyStringView name, QAnyStringView function) : m_name(name), m_function(function) {} - const char *m_name; - const QString &m_function; + QAnyStringView m_name; + QAnyStringView m_function; }; TextStream &operator<<(TextStream &str, const pyTypeSlotEntry &e) { - str << '{' << e.m_name << ','; - const int padding = qMax(0, 18 - int(strlen(e.m_name))); - for (int p = 0; p < padding; ++p) - str << ' '; - if (e.m_function.isEmpty()) - str << NULL_PTR; - else - str << "reinterpret_cast<void *>(" << e.m_function << ')'; - str << "},\n"; + if (!e.m_function.isEmpty()) { + str << '{' << e.m_name << ',' << Pad(' ', qMax(0, 18 - e.m_name.size())) + << "reinterpret_cast<void *>(" << e.m_function << ")},\n"; + } return str; } void CppGenerator::writeClassDefinition(TextStream &s, - const AbstractMetaClass *metaClass, + const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext) { - QString tp_flags; QString tp_init; QString tp_new; QString tp_dealloc; QString tp_hash; QString tp_call; const QString className = chopType(cpythonTypeName(metaClass)); - QString baseClassName; AbstractMetaFunctionCList ctors; - const auto &allCtors = metaClass->queryFunctions(FunctionQueryOption::Constructors); + const auto &allCtors = metaClass->queryFunctions(FunctionQueryOption::AnyConstructor); for (const auto &f : allCtors) { - if (!f->isPrivate() && !f->isModifiedRemoved() && !classContext.forSmartPointer()) + if (!f->isPrivate() && !f->isModifiedRemoved() + && f->functionType() != AbstractMetaFunction::MoveConstructorFunction) { ctors.append(f); + } } - if (!metaClass->baseClass()) - baseClassName = QLatin1String("reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())"); - bool onlyPrivCtor = !metaClass->hasNonPrivateConstructor(); - const AbstractMetaClass *qCoreApp = AbstractMetaClass::findClass(api().classes(), QLatin1String("QCoreApplication")); - const bool isQApp = qCoreApp != Q_NULLPTR && metaClass->inheritsFrom(qCoreApp); + const bool isQApp = usePySideExtensions() + && inheritsFrom(metaClass, u"QCoreApplication"_s); - tp_flags = QLatin1String("Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES"); + QString tp_flags = u"Py_TPFLAGS_DEFAULT"_s; + if (!metaClass->attributes().testFlag(AbstractMetaClass::FinalCppClass)) + tp_flags += u"|Py_TPFLAGS_BASETYPE"_s; if (metaClass->isNamespace() || metaClass->hasPrivateDestructor()) { tp_dealloc = metaClass->hasPrivateDestructor() ? - QLatin1String("SbkDeallocWrapperWithPrivateDtor") : - QLatin1String("Sbk_object_dealloc /* PYSIDE-832: Prevent replacement of \"0\" with subtype_dealloc. */"); + u"SbkDeallocWrapperWithPrivateDtor"_s : + u"Sbk_object_dealloc /* PYSIDE-832: Prevent replacement of \"0\" with subtype_dealloc. */"_s; tp_init.clear(); } else { tp_dealloc = isQApp - ? QLatin1String("&SbkDeallocQAppWrapper") : QLatin1String("&SbkDeallocWrapper"); + ? u"&SbkDeallocQAppWrapper"_s : u"&SbkDeallocWrapper"_s; if (!onlyPrivCtor && !ctors.isEmpty()) tp_init = cpythonFunctionName(ctors.constFirst()); } @@ -4263,53 +4405,59 @@ void CppGenerator::writeClassDefinition(TextStream &s, ? cpythonSetattroFunctionName(metaClass) : QString(); if (metaClass->hasPrivateDestructor() || onlyPrivCtor) { - // tp_flags = QLatin1String("Py_TPFLAGS_DEFAULT|Py_TPFLAGS_CHECKTYPES"); + // tp_flags = u"Py_TPFLAGS_DEFAULT"_s; // This is not generally possible, because PySide does not care about // privacy the same way. This worked before the heap types were used, // because inheritance is not really checked for static types. - // Instead, we check this at runtime, see SbkObjectTypeTpNew. - if (metaClass->fullName().startsWith(QLatin1String("PySide6.Qt"))) { + // Instead, we check this at runtime, see SbkObjectType_tp_new. + if (metaClass->fullName().startsWith(u"PySide6.Qt")) { // PYSIDE-595: No idea how to do non-inheritance correctly. // Since that is only relevant in shiboken, I used a shortcut for // PySide. - tp_new = QLatin1String("SbkObjectTpNew"); + tp_new = u"SbkObject_tp_new"_s; } else { - tp_new = QLatin1String("SbkDummyNew /* PYSIDE-595: Prevent replacement " - "of \"0\" with base->tp_new. */"); + tp_new = u"SbkDummyNew /* PYSIDE-595: Prevent replacement " + "of \"0\" with base->tp_new. */"_s; } } else if (isQApp) { - tp_new = QLatin1String("SbkQAppTpNew"); // PYSIDE-571: need singleton app + tp_new = u"SbkQApp_tp_new"_s; // PYSIDE-571: need singleton app } else { - tp_new = QLatin1String("SbkObjectTpNew"); + tp_new = u"SbkObject_tp_new"_s; } - tp_flags.append(QLatin1String("|Py_TPFLAGS_HAVE_GC")); + tp_flags.append(u"|Py_TPFLAGS_HAVE_GC"_s); QString tp_richcompare; - if (!metaClass->isNamespace() && metaClass->hasComparisonOperatorOverload()) - tp_richcompare = cpythonBaseName(metaClass) + QLatin1String("_richcompare"); + if (generateRichComparison(classContext)) + tp_richcompare = cpythonBaseName(metaClass) + u"_richcompare"_s; + const bool isSmartPointer = classContext.forSmartPointer(); QString tp_getset; - if (shouldGenerateGetSetList(metaClass) && !classContext.forSmartPointer()) + if (shouldGenerateGetSetList(metaClass) && !isSmartPointer) tp_getset = cpythonGettersSettersDefinitionName(metaClass); // search for special functions clearTpFuncs(); for (const auto &func : metaClass->functions()) { - if (m_tpFuncs.contains(func->name())) - m_tpFuncs[func->name()] = cpythonFunctionName(func); + // Special non-operator functions identified by name + auto it = m_tpFuncs.find(func->name()); + if (it != m_tpFuncs.end()) + it.value() = cpythonFunctionName(func); + else if ( it = m_nbFuncs.find(func->name()); it != m_nbFuncs.end() ) + it.value() = cpythonFunctionName(func); } - if (m_tpFuncs.value(reprFunction()).isEmpty() - && metaClass->hasToStringCapability()) { - m_tpFuncs[reprFunction()] = writeReprFunction(s, - classContext, - metaClass->toStringCapabilityIndirections()); + if (m_tpFuncs.value(REPR_FUNCTION).isEmpty() + && (isSmartPointer || metaClass->hasToStringCapability())) { + const QString name = isSmartPointer + ? writeSmartPointerReprFunction(s, classContext) + : writeReprFunction(s, classContext, metaClass->toStringCapabilityIndirections()); + m_tpFuncs[REPR_FUNCTION] = name; } // class or some ancestor has multiple inheritance - const AbstractMetaClass *miClass = getMultipleInheritingClass(metaClass); + const auto miClass = getMultipleInheritingClass(metaClass); if (miClass) { if (metaClass == miClass) writeMultipleInheritanceInitializerFunction(s, metaClass); @@ -4320,39 +4468,33 @@ void CppGenerator::writeClassDefinition(TextStream &s, s << "// Class Definition -----------------------------------------------\n" "extern \"C\" {\n"; - if (!metaClass->typeEntry()->hashFunction().isEmpty()) - tp_hash = QLatin1Char('&') + cpythonBaseName(metaClass) + QLatin1String("_HashFunc"); + if (hasHashFunction(metaClass)) + tp_hash = u'&' + cpythonBaseName(metaClass) + u"_HashFunc"_s; - const auto callOp = metaClass->findFunction(QLatin1String("operator()")); - if (!callOp.isNull() && !callOp->isModifiedRemoved()) - tp_call = QLatin1Char('&') + cpythonFunctionName(callOp); - - QString computedClassTargetFullName; - if (!classContext.forSmartPointer()) - computedClassTargetFullName = getClassTargetFullName(metaClass); - else - computedClassTargetFullName = getClassTargetFullName(classContext.preciseType()); + const auto callOp = metaClass->findFunction("operator()"); + if (callOp && !callOp->isModifiedRemoved()) + tp_call = u'&' + cpythonFunctionName(callOp); - const QString typePtr = QLatin1String("_") + className - + QLatin1String("_Type"); - s << "static SbkObjectType *" << typePtr << " = nullptr;\n" - << "static SbkObjectType *" << className << "_TypeF(void)\n" + const QString typePtr = u"_"_s + className + + u"_Type"_s; + s << "static PyTypeObject *" << typePtr << " = nullptr;\n" + << "static PyTypeObject *" << className << "_TypeF(void)\n" << "{\n" << indent << "return " << typePtr << ";\n" << outdent << "}\n\nstatic PyType_Slot " << className << "_slots[] = {\n" << indent << "{Py_tp_base, nullptr}, // inserted by introduceWrapperType\n" << pyTypeSlotEntry("Py_tp_dealloc", tp_dealloc) - << pyTypeSlotEntry("Py_tp_repr", m_tpFuncs.value(reprFunction())) + << pyTypeSlotEntry("Py_tp_repr", m_tpFuncs.value(REPR_FUNCTION)) << pyTypeSlotEntry("Py_tp_hash", tp_hash) << pyTypeSlotEntry("Py_tp_call", tp_call) - << pyTypeSlotEntry("Py_tp_str", m_tpFuncs.value(QLatin1String("__str__"))) + << pyTypeSlotEntry("Py_tp_str", m_tpFuncs.value(u"__str__"_s)) << pyTypeSlotEntry("Py_tp_getattro", tp_getattro) << pyTypeSlotEntry("Py_tp_setattro", tp_setattro) - << pyTypeSlotEntry("Py_tp_traverse", className + QLatin1String("_traverse")) - << pyTypeSlotEntry("Py_tp_clear", className + QLatin1String("_clear")) + << pyTypeSlotEntry("Py_tp_traverse", className + u"_traverse"_s) + << pyTypeSlotEntry("Py_tp_clear", className + u"_clear"_s) << pyTypeSlotEntry("Py_tp_richcompare", tp_richcompare) - << pyTypeSlotEntry("Py_tp_iter", m_tpFuncs.value(QLatin1String("__iter__"))) - << pyTypeSlotEntry("Py_tp_iternext", m_tpFuncs.value(QLatin1String("__next__"))) - << pyTypeSlotEntry("Py_tp_methods", className + QLatin1String("_methods")) + << pyTypeSlotEntry("Py_tp_iter", m_tpFuncs.value(u"__iter__"_s)) + << pyTypeSlotEntry("Py_tp_iternext", m_tpFuncs.value(u"__next__"_s)) + << pyTypeSlotEntry("Py_tp_methods", className + u"_methods"_s) << pyTypeSlotEntry("Py_tp_getset", tp_getset) << pyTypeSlotEntry("Py_tp_init", tp_init) << pyTypeSlotEntry("Py_tp_new", tp_new); @@ -4365,63 +4507,62 @@ void CppGenerator::writeClassDefinition(TextStream &s, writeTypeAsMappingDefinition(s, metaClass); } if (supportsNumberProtocol(metaClass)) { - // This one must come last. See the function itself. s << "// type supports number protocol\n"; writeTypeAsNumberDefinition(s, metaClass); } s << "{0, " << NULL_PTR << "}\n" << outdent << "};\n"; - int packageLevel = packageName().count(QLatin1Char('.')) + 1; + int packageLevel = packageName().count(u'.') + 1; s << "static PyType_Spec " << className << "_spec = {\n" << indent - << '"' << packageLevel << ':' << computedClassTargetFullName << "\",\n" + << '"' << packageLevel << ':' << getClassTargetFullName(metaClass) << "\",\n" << "sizeof(SbkObject),\n0,\n" << tp_flags << ",\n" << className << "_slots\n" << outdent << "};\n\n} //extern \"C\"\n"; } void CppGenerator::writeMappingMethods(TextStream &s, - const AbstractMetaClass *metaClass, + const AbstractMetaClassCPtr &metaClass, const GeneratorContext &context) const { for (const auto & m : mappingProtocols()) { const auto func = metaClass->findFunction(m.name); - if (func.isNull()) + if (!func) continue; QString funcName = cpythonFunctionName(func); CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode); - s << m.returnType << ' ' << funcName << '(' << m.arguments << ")\n{\n"; - writeInvalidPyObjectCheck(s, QLatin1String("self")); + s << m.returnType << ' ' << funcName << '(' << m.arguments << ")\n{\n" << indent; - writeCppSelfDefinition(s, func, context); + writeCppSelfDefinition(s, func, context, ErrorReturn::Default); const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast(); - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode, func, lastArg); - s<< "}\n\n"; + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionAny, + TypeSystem::TargetLangCode, func, false, lastArg); + s << outdent << "}\n\n"; } } void CppGenerator::writeSequenceMethods(TextStream &s, - const AbstractMetaClass *metaClass, + const AbstractMetaClassCPtr &metaClass, const GeneratorContext &context) const { bool injectedCode = false; for (const auto &seq : sequenceProtocols()) { const auto func = metaClass->findFunction(seq.name); - if (func.isNull()) + if (!func) continue; injectedCode = true; QString funcName = cpythonFunctionName(func); CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode); s << seq.returnType << ' ' << funcName << '(' << seq.arguments << ")\n{\n" << indent; - writeInvalidPyObjectCheck(s, QLatin1String("self")); - writeCppSelfDefinition(s, func, context); + writeCppSelfDefinition(s, func, context, ErrorReturn::Default); const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast(); - writeCodeSnips(s, snips,TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode, func, lastArg); + writeCodeSnips(s, snips,TypeSystem::CodeSnipPositionAny, + TypeSystem::TargetLangCode, func, false /* uses PyArgs */, lastArg); s<< outdent << "}\n\n"; } @@ -4433,26 +4574,26 @@ void CppGenerator::writeSequenceMethods(TextStream &s, static const QHash<QString, QString> &sqFuncs() { static const QHash<QString, QString> result = { - {QLatin1String("__concat__"), QLatin1String("sq_concat")}, - {QLatin1String("__contains__"), QLatin1String("sq_contains")}, - {QLatin1String("__getitem__"), QLatin1String("sq_item")}, - {QLatin1String("__getslice__"), QLatin1String("sq_slice")}, - {QLatin1String("__len__"), QLatin1String("sq_length")}, - {QLatin1String("__setitem__"), QLatin1String("sq_ass_item")}, - {QLatin1String("__setslice__"), QLatin1String("sq_ass_slice")} + {u"__concat__"_s, u"Py_sq_concat"_s}, + {u"__contains__"_s, u"Py_sq_contains"_s}, + {u"__getitem__"_s, u"Py_sq_item"_s}, + {u"__getslice__"_s, u"Py_sq_slice"_s}, + {u"__len__"_s, u"Py_sq_length"_s}, + {u"__setitem__"_s, u"Py_sq_ass_item"_s}, + {u"__setslice__"_s, u"Py_sq_ass_slice"_s} }; return result; } void CppGenerator::writeTypeAsSequenceDefinition(TextStream &s, - const AbstractMetaClass *metaClass) + const AbstractMetaClassCPtr &metaClass) { bool hasFunctions = false; QMap<QString, QString> funcs; for (const auto &seq : sequenceProtocols()) { const auto func = metaClass->findFunction(seq.name); - if (!func.isNull()) { - funcs.insert(seq.name, QLatin1Char('&') + cpythonFunctionName(func)); + if (func) { + funcs.insert(seq.name, u'&' + cpythonFunctionName(func)); hasFunctions = true; } } @@ -4461,46 +4602,42 @@ void CppGenerator::writeTypeAsSequenceDefinition(TextStream &s, //use default implementation if (!hasFunctions) { - funcs[QLatin1String("__len__")] = baseName + QLatin1String("__len__"); - funcs[QLatin1String("__getitem__")] = baseName + QLatin1String("__getitem__"); - funcs[QLatin1String("__setitem__")] = baseName + QLatin1String("__setitem__"); + funcs[u"__len__"_s] = baseName + u"__len__"_s; + funcs[u"__getitem__"_s] = baseName + u"__getitem__"_s; + funcs[u"__setitem__"_s] = baseName + u"__setitem__"_s; } for (auto it = sqFuncs().cbegin(), end = sqFuncs().cend(); it != end; ++it) { const QString &sqName = it.key(); auto fit = funcs.constFind(sqName); - if (fit != funcs.constEnd()) { - s << "{Py_" << it.value() << ", reinterpret_cast<void *>(" - << fit.value() << ")},\n"; - } + if (fit != funcs.constEnd()) + s << pyTypeSlotEntry(it.value(), fit.value()); } } void CppGenerator::writeTypeAsMappingDefinition(TextStream &s, - const AbstractMetaClass *metaClass) + const AbstractMetaClassCPtr &metaClass) { // Sequence protocol structure members names static const QHash<QString, QString> mpFuncs{ - {QLatin1String("__mlen__"), QLatin1String("mp_length")}, - {QLatin1String("__mgetitem__"), QLatin1String("mp_subscript")}, - {QLatin1String("__msetitem__"), QLatin1String("mp_ass_subscript")}, + {u"__mlen__"_s, u"Py_mp_length"_s}, + {u"__mgetitem__"_s, u"Py_mp_subscript"_s}, + {u"__msetitem__"_s, u"Py_mp_ass_subscript"_s}, }; QMap<QString, QString> funcs; for (const auto &m : mappingProtocols()) { const auto func = metaClass->findFunction(m.name); - if (!func.isNull()) { - const QString entry = QLatin1String("reinterpret_cast<void *>(&") - + cpythonFunctionName(func) + QLatin1Char(')'); + if (func) { + const QString entry = u"reinterpret_cast<void *>(&"_s + + cpythonFunctionName(func) + u')'; funcs.insert(m.name, entry); - } else { - funcs.insert(m.name, QLatin1String(NULL_PTR)); } } for (auto it = mpFuncs.cbegin(), end = mpFuncs.cend(); it != end; ++it) { const auto fit = funcs.constFind(it.key()); if (fit != funcs.constEnd()) - s << "{Py_" << it.value() << ", " << fit.value() << "},\n"; + s << pyTypeSlotEntry(it.value(), fit.value()); } } @@ -4508,106 +4645,103 @@ void CppGenerator::writeTypeAsMappingDefinition(TextStream &s, static const QHash<QString, QString> &nbFuncs() { static const QHash<QString, QString> result = { - {QLatin1String("__add__"), QLatin1String("nb_add")}, - {QLatin1String("__sub__"), QLatin1String("nb_subtract")}, - {QLatin1String("__mul__"), QLatin1String("nb_multiply")}, - {QLatin1String("__div__"), QLatin1String("nb_divide")}, - {QLatin1String("__mod__"), QLatin1String("nb_remainder")}, - {QLatin1String("__neg__"), QLatin1String("nb_negative")}, - {QLatin1String("__pos__"), QLatin1String("nb_positive")}, - {QLatin1String("__invert__"), QLatin1String("nb_invert")}, - {QLatin1String("__lshift__"), QLatin1String("nb_lshift")}, - {QLatin1String("__rshift__"), QLatin1String("nb_rshift")}, - {QLatin1String("__and__"), QLatin1String("nb_and")}, - {QLatin1String("__xor__"), QLatin1String("nb_xor")}, - {QLatin1String("__or__"), QLatin1String("nb_or")}, - {QLatin1String("__iadd__"), QLatin1String("nb_inplace_add")}, - {QLatin1String("__isub__"), QLatin1String("nb_inplace_subtract")}, - {QLatin1String("__imul__"), QLatin1String("nb_inplace_multiply")}, - {QLatin1String("__idiv__"), QLatin1String("nb_inplace_divide")}, - {QLatin1String("__imod__"), QLatin1String("nb_inplace_remainder")}, - {QLatin1String("__ilshift__"), QLatin1String("nb_inplace_lshift")}, - {QLatin1String("__irshift__"), QLatin1String("nb_inplace_rshift")}, - {QLatin1String("__iand__"), QLatin1String("nb_inplace_and")}, - {QLatin1String("__ixor__"), QLatin1String("nb_inplace_xor")}, - {QLatin1String("__ior__"), QLatin1String("nb_inplace_or")}, - {boolT(), QLatin1String("nb_nonzero")} + {u"__abs__"_s, u"Py_nb_absolute"_s}, + {u"__add__"_s, u"Py_nb_add"_s}, + {u"__sub__"_s, u"Py_nb_subtract"_s}, + {u"__mul__"_s, u"Py_nb_multiply"_s}, + {u"__div__"_s, u"Py_nb_true_divide"_s}, + {u"__mod__"_s, u"Py_nb_remainder"_s}, + {u"__neg__"_s, u"Py_nb_negative"_s}, + {u"__pos__"_s, u"Py_nb_positive"_s}, + {u"__pow__"_s, u"Py_nb_power"_s}, + {u"__invert__"_s, u"Py_nb_invert"_s}, + {u"__lshift__"_s, u"Py_nb_lshift"_s}, + {u"__rshift__"_s, u"Py_nb_rshift"_s}, + {u"__and__"_s, u"Py_nb_and"_s}, + {u"__xor__"_s, u"Py_nb_xor"_s}, + {u"__or__"_s, u"Py_nb_or"_s}, + {u"__iadd__"_s, u"Py_nb_inplace_add"_s}, + {u"__isub__"_s, u"Py_nb_inplace_subtract"_s}, + {u"__imul__"_s, u"Py_nb_inplace_multiply"_s}, + {u"__imod__"_s, u"Py_nb_inplace_remainder"_s}, + {u"__ilshift__"_s, u"Py_nb_inplace_lshift"_s}, + {u"__irshift__"_s, u"Py_nb_inplace_rshift"_s}, + {u"__iand__"_s, u"Py_nb_inplace_and"_s}, + {u"__ixor__"_s, u"Py_nb_inplace_xor"_s}, + {u"__ior__"_s, u"Py_nb_inplace_or"_s}, + {u"__bool__"_s, u"Py_nb_bool"_s}, + {u"__int__"_s, u"Py_nb_int"_s}, + {u"__float__"_s, u"Py_nb_float"_s} }; return result; } -void CppGenerator::writeTypeAsNumberDefinition(TextStream &s, const AbstractMetaClass *metaClass) const +void CppGenerator::writeTypeAsNumberDefinition(TextStream &s, const AbstractMetaClassCPtr &metaClass) const { QMap<QString, QString> nb; - const QList<AbstractMetaFunctionCList> opOverloads = - filterGroupedOperatorFunctions(metaClass, - OperatorQueryOption::ArithmeticOp - | OperatorQueryOption::IncDecrementOp - | OperatorQueryOption::LogicalOp - | OperatorQueryOption::BitwiseOp); - - for (const AbstractMetaFunctionCList &opOverload : opOverloads) { + const QList<AbstractMetaFunctionCList> opOverloads = numberProtocolOperators(metaClass); + for (const auto &opOverload : opOverloads) { const auto rfunc = opOverload.at(0); QString opName = ShibokenGenerator::pythonOperatorFunctionName(rfunc); nb[opName] = cpythonFunctionName(rfunc); } + for (auto it = m_nbFuncs.cbegin(), end = m_nbFuncs.cend(); it != end; ++it) { + if (!it.value().isEmpty()) + nb.insert(it.key(), it.value()); + } + QString baseName = cpythonBaseName(metaClass); if (hasBoolCast(metaClass)) - nb.insert(boolT(), baseName + QLatin1String("___nb_bool")); + nb.insert(u"__bool__"_s, baseName + u"___nb_bool"_s); for (auto it = nbFuncs().cbegin(), end = nbFuncs().cend(); it != end; ++it) { const QString &nbName = it.key(); - if (nbName == QLatin1String("__div__") || nbName == QLatin1String("__idiv__")) - continue; // excludeFromPy3K const auto nbIt = nb.constFind(nbName); - if (nbIt != nb.constEnd()) { - const QString fixednbName = nbName == boolT() - ? QLatin1String("nb_bool") : it.value(); - s << "{Py_" << fixednbName << ", reinterpret_cast<void *>(" - << nbIt.value() << ")},\n"; - } - } - - auto nbIt = nb.constFind(QLatin1String("__div__")); - if (nbIt != nb.constEnd()) - s << "{Py_nb_true_divide, reinterpret_cast<void *>(" << nbIt.value() << ")},\n"; - - nbIt = nb.constFind(QLatin1String("__idiv__")); - if (nbIt != nb.constEnd()) { - s << "// This function is unused in Python 3. We reference it here.\n" - << "{0, reinterpret_cast<void *>(" << nbIt.value() << ")},\n" - << "// This list is ending at the first 0 entry.\n" - << "// Therefore, we need to put the unused functions at the very end.\n"; + if (nbIt != nb.constEnd()) + s << pyTypeSlotEntry(it.value(), nbIt.value()); } } -void CppGenerator::writeTpTraverseFunction(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeTpTraverseFunction(TextStream &s, const AbstractMetaClassCPtr &metaClass) { QString baseName = cpythonBaseName(metaClass); s << "static int " << baseName << "_traverse(PyObject *self, visitproc visit, void *arg)\n{\n" << indent - << "return reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())->tp_traverse(self, visit, arg);\n" + << "auto traverseProc = " + << pyTypeGetSlot("traverseproc", sbkObjectTypeF, "Py_tp_traverse") << ";\n" + << "return traverseProc(self, visit, arg);\n" << outdent << "}\n"; } -void CppGenerator::writeTpClearFunction(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeTpClearFunction(TextStream &s, const AbstractMetaClassCPtr &metaClass) { QString baseName = cpythonBaseName(metaClass); s << "static int " << baseName << "_clear(PyObject *self)\n{\n" << indent - << "return reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())->tp_clear(self);\n" + << "auto clearProc = " + << pyTypeGetSlot("inquiry", sbkObjectTypeF, "Py_tp_clear") << ";\n" + << "return clearProc(self);\n" << outdent << "}\n"; } -void CppGenerator::writeCopyFunction(TextStream &s, const GeneratorContext &context) const +QString CppGenerator::writeCopyFunction(TextStream &s, + TextStream &definitionStream, + TextStream &signatureStream, + const GeneratorContext &context) { - const AbstractMetaClass *metaClass = context.metaClass(); + const auto metaClass = context.metaClass(); const QString className = chopType(cpythonTypeName(metaClass)); - s << "static PyObject *" << className << "___copy__(PyObject *self)\n" - << "{\n" << indent; - writeCppSelfDefinition(s, context, false, true); + const QString funcName = className + u"__copy__"_s; + + signatureStream << fullPythonClassName(metaClass) << ".__copy__()\n"; + definitionStream << PyMethodDefEntry{u"__copy__"_s, funcName, {"METH_NOARGS"_ba}, {}} + << ",\n"; + + s << "static PyObject *" << funcName << "(PyObject *self)\n" + << "{\n" << indent; + writeCppSelfDefinition(s, context, ErrorReturn::Default, CppSelfDefinitionFlag::CppSelfAsReference); QString conversionCode; if (!context.forSmartPointer()) conversionCode = cpythonToPythonConversionFunction(metaClass); @@ -4616,19 +4750,21 @@ void CppGenerator::writeCopyFunction(TextStream &s, const GeneratorContext &cont s << "PyObject *" << PYTHON_RETURN_VAR << " = " << conversionCode << CPP_SELF_VAR << ");\n"; - writeFunctionReturnErrorCheckSection(s); + writeFunctionReturnErrorCheckSection(s, ErrorReturn::Default); s << "return " << PYTHON_RETURN_VAR << ";\n" << outdent << "}\n\n"; + + return funcName; } static inline void writeGetterFunctionStart(TextStream &s, const QString &funcName) { - s << "static PyObject *" << funcName << "(PyObject *self, void *)\n" + s << "static PyObject *" << funcName << "(PyObject *self, void * /* closure */)\n" << "{\n" << indent; } QString CppGenerator::cppFieldAccess(const AbstractMetaField &metaField, - const GeneratorContext &context) const + const GeneratorContext &context) { QString result; QTextStream str(&result); @@ -4642,9 +4778,8 @@ QString CppGenerator::cppFieldAccess(const AbstractMetaField &metaField, void CppGenerator::writeGetterFunction(TextStream &s, const AbstractMetaField &metaField, - const GeneratorContext &context) const + const GeneratorContext &context) { - ErrorCode errorCode(QString::fromLatin1(NULL_PTR)); writeGetterFunctionStart(s, cpythonGetterFunctionName(metaField)); writeCppSelfDefinition(s, context); @@ -4655,6 +4790,17 @@ void CppGenerator::writeGetterFunction(TextStream &s, && !fieldType.isPointer(); QString cppField = cppFieldAccess(metaField, context); + + if (metaField.generateOpaqueContainer() + && fieldType.generateOpaqueContainer()) { + const QString creationFunc = opaqueContainerCreationFunc(fieldType); + writeOpaqueContainerCreationFuncDecl(s, creationFunc, fieldType); + s << "PyObject *pyOut = " << creationFunc + << "(&" << cppField << ");\nPy_IncRef(pyOut);\n" + << "return pyOut;\n" << outdent << "}\n"; + return; + } + if (newWrapperSameObject) { cppField.prepend(u"&("); cppField.append(u')'); @@ -4662,44 +4808,41 @@ void CppGenerator::writeGetterFunction(TextStream &s, if (fieldType.isCppIntegralPrimitive() || fieldType.isEnum()) { s << getFullTypeNameWithoutModifiers(fieldType) << " cppOut_local = " << cppField << ";\n"; - cppField = QLatin1String("cppOut_local"); + cppField = u"cppOut_local"_s; } s << "PyObject *pyOut = {};\n"; if (newWrapperSameObject) { // Special case colocated field with same address (first field in a struct) s << "if (reinterpret_cast<void *>(" - << cppField - << ") == reinterpret_cast<void *>(" - << CPP_SELF_VAR << ")) {\n"; - { - Indentation indent(s); - s << "pyOut = reinterpret_cast<PyObject *>(Shiboken::Object::findColocatedChild(" - << "reinterpret_cast<SbkObject *>(self), reinterpret_cast<SbkObjectType *>(" - << cpythonTypeNameExt(fieldType) - << ")));\n"; - s << "if (pyOut) {\n"; - { - Indentation indent(s); - s << "Py_IncRef(pyOut);\n" - << "return pyOut;\n"; - } - s << "}\n"; - } + << cppField << ") == reinterpret_cast<void *>(" + << CPP_SELF_VAR << ")) {\n" << indent + << "pyOut = reinterpret_cast<PyObject *>(Shiboken::Object::findColocatedChild(" + << "reinterpret_cast<SbkObject *>(self), " + << cpythonTypeNameExt(fieldType) << "));\n" + << "if (pyOut != nullptr) {\n" << indent + << "Py_IncRef(pyOut);\n" + << "return pyOut;\n" + << outdent << "}\n"; // Check if field wrapper has already been created. - s << "} else if (Shiboken::BindingManager::instance().hasWrapper(" << cppField << ")) {" << "\n"; - { - Indentation indent(s); - s << "pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(" - << cppField << "));" << "\n" - << "Py_IncRef(pyOut);" << "\n" - << "return pyOut;" << "\n"; - } - s << "}\n"; - // Create and register new wrapper + s << outdent << "} else if (Shiboken::BindingManager::instance().hasWrapper(" + << cppField << ")) {" << "\n" << indent + << "pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(" + << cppField << "));" << "\n" + << "Py_IncRef(pyOut);" << "\n" + << "return pyOut;" << "\n" + << outdent << "}\n"; + // Create and register new wrapper. We force a pointer conversion also + // for wrapped value types so that they refer to the struct member, + // avoiding any trouble copying them. Add a parent relationship to + // properly notify if the struct is deleted (see protected_test.py, + // testProtectedValueTypeProperty()). Note that this has currently + // unsolved issues when using temporary Python lists of structs + // which can cause elements to be reported deleted in expressions like + // "foo.list_of_structs[2].field". s << "pyOut = " - << "Shiboken::Object::newObject(reinterpret_cast<SbkObjectType *>(" << cpythonTypeNameExt(fieldType) - << "), " << cppField << ", false, true);\n" + << "Shiboken::Object::newObject(" << cpythonTypeNameExt(fieldType) + << ", " << cppField << ", false, true);\n" << "Shiboken::Object::setParent(self, pyOut)"; } else { s << "pyOut = "; @@ -4709,57 +4852,52 @@ void CppGenerator::writeGetterFunction(TextStream &s, } // Write a getter for QPropertySpec -void CppGenerator::writeGetterFunction(TextStream &s, const QPropertySpec &property, - const GeneratorContext &context) const +void CppGenerator::writeGetterFunction(TextStream &s, + const QPropertySpec &property, + const GeneratorContext &context) { - ErrorCode errorCode(0); writeGetterFunctionStart(s, cpythonGetterFunctionName(property, context.metaClass())); writeCppSelfDefinition(s, context); - const QString value = QStringLiteral("value"); + const QString value = "value"_L1; s << "auto " << value << " = " << CPP_SELF_VAR << "->" << property.read() << "();\n" - << "auto pyResult = "; + << "auto *pyResult = "; writeToPythonConversion(s, property.type(), context.metaClass(), value); - s << ";\nif (PyErr_Occurred() || !pyResult) {\n"; - { - Indentation indent(s); - s << "Py_XDECREF(pyResult);\nreturn {};\n"; - } - s << "}\nreturn pyResult;\n" << outdent << "}\n\n"; + s << ";\nif (" << shibokenErrorsOccurred << " || pyResult == nullptr) {\n" + << indent << "Py_XDECREF(pyResult);\nreturn {};\n" << outdent + << "}\nreturn pyResult;\n" << outdent << "}\n\n"; } // Write setter function preamble (type checks on "pyIn") -void CppGenerator::writeSetterFunctionPreamble(TextStream &s, const QString &name, +void CppGenerator::writeSetterFunctionPreamble(TextStream &s, + const QString &name, const QString &funcName, const AbstractMetaType &type, - const GeneratorContext &context) const + const GeneratorContext &context) { - s << "static int " << funcName << "(PyObject *self, PyObject *pyIn, void *)\n" + s << "static int " << funcName << "(PyObject *self, PyObject *pyIn, void * /* closure */)\n" << "{\n" << indent; - writeCppSelfDefinition(s, context); + writeCppSelfDefinition(s, context, ErrorReturn::Zero); s << "if (pyIn == " << NULL_PTR << ") {\n" << indent - << "PyErr_SetString(PyExc_TypeError, \"'" - << name << "' may not be deleted\");\n" + << "Shiboken::Errors::setInvalidTypeDeletion(\"" << name << "\");\n" << "return -1;\n" << outdent << "}\n"; - s << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << "{nullptr};\n" + s << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR << ";\n" << "if (!"; - writeTypeCheck(s, type, QLatin1String("pyIn"), isNumber(type.typeEntry())); + writeTypeCheck(s, type, u"pyIn"_s, isNumber(type.typeEntry())); s << ") {\n" << indent - << "PyErr_SetString(PyExc_TypeError, \"wrong type attributed to '" - << name << "', '" << type.name() << "' or convertible type expected\");\n" + << "Shiboken::Errors::setSetterTypeError(\"" << name << "\", \"" + << type.name() << "\");\n" << "return -1;\n" << outdent << "}\n\n"; } void CppGenerator::writeSetterFunction(TextStream &s, const AbstractMetaField &metaField, - const GeneratorContext &context) const + const GeneratorContext &context) { - ErrorCode errorCode(0); - const AbstractMetaType &fieldType = metaField.type(); writeSetterFunctionPreamble(s, metaField.name(), cpythonSetterFunctionName(metaField), fieldType, context); @@ -4790,218 +4928,193 @@ void CppGenerator::writeSetterFunction(TextStream &s, } // Write a setter for QPropertySpec -void CppGenerator::writeSetterFunction(TextStream &s, const QPropertySpec &property, - const GeneratorContext &context) const +void CppGenerator::writeSetterFunction(TextStream &s, + const QPropertySpec &property, + const GeneratorContext &context) { - ErrorCode errorCode(0); - writeSetterFunctionPreamble(s, property.name(), + writeSetterFunctionPreamble(s, + property.name(), cpythonSetterFunctionName(property, context.metaClass()), property.type(), context); s << "auto cppOut = " << CPP_SELF_VAR << "->" << property.read() << "();\n" << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut);\n" - << "if (PyErr_Occurred())\n"; - { - Indentation indent(s); - s << "return -1;\n"; - } - s << CPP_SELF_VAR << "->" << property.write() << "(cppOut);\n" + << "if (" << shibokenErrorsOccurred << ")\n" << indent + << "return -1;\n" << outdent + << CPP_SELF_VAR << "->" << property.write() << "(cppOut);\n" << "return 0;\n" << outdent << "}\n\n"; } -void CppGenerator::writeRichCompareFunction(TextStream &s, - const GeneratorContext &context) const +void CppGenerator::writeRichCompareFunctionHeader(TextStream &s, + const QString &baseName, + const GeneratorContext &context) { - const AbstractMetaClass *metaClass = context.metaClass(); - QString baseName = cpythonBaseName(metaClass); s << "static PyObject * "; s << baseName << "_richcompare(PyObject *self, PyObject *" << PYTHON_ARG << ", int op)\n{\n" << indent; - writeCppSelfDefinition(s, context, false, true); - writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR)); - s << "PyObject *" << PYTHON_RETURN_VAR << "{};\n" - << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ";\n"; - writeUnusedVariableCast(s, QLatin1String(PYTHON_TO_CPP_VAR)); - s << '\n'; + writeCppSelfDefinition(s, context, ErrorReturn::Default, CppSelfDefinitionFlag::CppSelfAsReference); + s << sbkUnusedVariableCast(CPP_SELF_VAR) + << "PyObject *" << PYTHON_RETURN_VAR << "{};\n" + << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR << ";\n" + << sbkUnusedVariableCast(PYTHON_TO_CPP_VAR) << '\n'; +} - s << "switch (op) {\n"; - { - Indentation indent(s); - const QList<AbstractMetaFunctionCList> &groupedFuncs = - filterGroupedOperatorFunctions(metaClass, OperatorQueryOption::ComparisonOp); - for (const AbstractMetaFunctionCList &overloads : groupedFuncs) { - const auto rfunc = overloads[0]; +void CppGenerator::writeRichCompareFunction(TextStream &s, + const GeneratorContext &context) const +{ + const auto metaClass = context.metaClass(); + QString baseName = cpythonBaseName(metaClass); + writeRichCompareFunctionHeader(s, baseName, context); - QString operatorId = ShibokenGenerator::pythonRichCompareOperatorId(rfunc); - s << "case " << operatorId << ':' << '\n'; + s << "switch (op) {\n" << indent; + const QList<AbstractMetaFunctionCList> &groupedFuncs = + filterGroupedOperatorFunctions(metaClass, OperatorQueryOption::ComparisonOp); + for (const AbstractMetaFunctionCList &overloads : groupedFuncs) { + const auto rfunc = overloads[0]; - Indentation indent(s); + const auto op = rfunc->comparisonOperatorType().value(); + s << "case " << AbstractMetaFunction::pythonRichCompareOpCode(op) + << ":\n" << indent; - QString op = rfunc->originalName(); - op = op.right(op.size() - QLatin1String("operator").size()); + int alternativeNumericTypes = 0; + for (const auto &func : overloads) { + if (!func->isStatic() && + ShibokenGenerator::isNumber(func->arguments().at(0).type().typeEntry())) + alternativeNumericTypes++; + } - int alternativeNumericTypes = 0; - for (const auto &func : overloads) { - if (!func->isStatic() && - ShibokenGenerator::isNumber(func->arguments().at(0).type().typeEntry())) - alternativeNumericTypes++; + bool first = true; + OverloadData overloadData(overloads, api()); + const OverloadDataList &nextOverloads = overloadData.children(); + for (const auto &od : nextOverloads) { + const auto func = od->referenceFunction(); + if (func->isStatic()) + continue; + auto argType = getArgumentType(func, 0); + if (!first) { + s << " else "; + } else { + first = false; } - - bool first = true; - OverloadData overloadData(overloads, api()); - const OverloadDataList &nextOverloads = overloadData.nextOverloadData(); - for (OverloadData *od : nextOverloads) { - const auto func = od->referenceFunction(); - if (func->isStatic()) - continue; - auto argTypeO = getArgumentType(func, 1); - if (!argTypeO.has_value()) - continue; - auto argType = argTypeO.value(); - if (!first) { - s << " else "; - } else { - first = false; + s << "if ("; + writeTypeCheck(s, argType, PYTHON_ARG, + alternativeNumericTypes == 1 || isPyInt(argType)); + s << ") {\n" << indent + << "// " << func->signature() << '\n'; + writeArgumentConversion(s, argType, CPP_ARG0, + PYTHON_ARG, ErrorReturn::Default, + metaClass, + QString(), func->isUserAdded()); + // If the function is user added, use the inject code + bool generateOperatorCode = true; + if (func->isUserAdded()) { + CodeSnipList snips = func->injectedCodeSnips(); + if (!snips.isEmpty()) { + writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionAny, + TypeSystem::TargetLangCode, func, + false /* uses PyArgs */, &func->arguments().constLast()); + generateOperatorCode = false; } - s << "if ("; - writeTypeCheck(s, argType, QLatin1String(PYTHON_ARG), alternativeNumericTypes == 1 || isPyInt(argType)); - s << ") {\n"; - { - Indentation indent(s); - s << "// " << func->signature() << '\n'; - writeArgumentConversion(s, argType, QLatin1String(CPP_ARG0), - QLatin1String(PYTHON_ARG), metaClass, - QString(), func->isUserAdded()); - - // If the function is user added, use the inject code - bool generateOperatorCode = true; - if (func->isUserAdded()) { - CodeSnipList snips = func->injectedCodeSnips(); - if (!snips.isEmpty()) { - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionAny, - TypeSystem::TargetLangCode, func, - &func->arguments().constLast()); - generateOperatorCode = false; - } - } - if (generateOperatorCode) { - if (!func->isVoid()) - s << func->type().cppSignature() << " " << CPP_RETURN_VAR << " = "; - // expression - if (func->isPointerOperator()) - s << '&'; - s << CPP_SELF_VAR << ' ' << op << '('; - if (argType.shouldDereferencePointer()) - s << '*'; - s << CPP_ARG0 << ");\n" - << PYTHON_RETURN_VAR << " = "; - if (!func->isVoid()) - writeToPythonConversion(s, func->type(), metaClass, QLatin1String(CPP_RETURN_VAR)); - else - s << "Py_None;\n" << "Py_INCREF(Py_None)"; - s << ";\n"; - } - } - s << '}'; } - - s << " else {\n"; - if (operatorId == QLatin1String("Py_EQ") || operatorId == QLatin1String("Py_NE")) { - Indentation indent(s); - s << PYTHON_RETURN_VAR << " = " - << (operatorId == QLatin1String("Py_EQ") ? "Py_False" : "Py_True") << ";\n" - << "Py_INCREF(" << PYTHON_RETURN_VAR << ");\n"; - } else { - Indentation indent(s); - s << "goto " << baseName << "_RichComparison_TypeError;\n"; + if (generateOperatorCode) { + if (!func->isVoid()) + s << func->type().cppSignature() << " " << CPP_RETURN_VAR << " = "; + // expression + if (func->isPointerOperator()) + s << '&'; + s << CPP_SELF_VAR << ' ' + << AbstractMetaFunction::cppComparisonOperator(op) << " ("; + auto generatorArg = GeneratorArgument::fromMetaType(argType); + if (generatorArg.indirections != 0) + s << QByteArray(generatorArg.indirections, '*'); + s << CPP_ARG0 << ");\n" + << PYTHON_RETURN_VAR << " = "; + if (!func->isVoid()) { + writeToPythonConversion(s, func->type(), metaClass, + CPP_RETURN_VAR); + } else { + s << "Py_None;\n" << "Py_INCREF(Py_None)"; + } + s << ";\n"; } - s << "}\n\n"; - - s << "break;\n"; + s << outdent << '}'; } - s << "default:\n"; - { - Indentation indent(s); - s << "// PYSIDE-74: By default, we redirect to object's tp_richcompare (which is `==`, `!=`).\n" - << "return FallbackRichCompare(self, " << PYTHON_ARG << ", op);\n" - << "goto " << baseName << "_RichComparison_TypeError;\n"; + + s << " else {\n"; + if (op == AbstractMetaFunction::OperatorEqual || + op == AbstractMetaFunction::OperatorNotEqual) { + s << indent << PYTHON_RETURN_VAR << " = " + << (op == AbstractMetaFunction::OperatorEqual ? "Py_False" : "Py_True") << ";\n" + << "Py_INCREF(" << PYTHON_RETURN_VAR << ");\n" << outdent; + } else { + s << indent << "return Shiboken::returnFromRichCompare(" + << PYTHON_RETURN_VAR << ");\n" << outdent; } - } - s << "}\n\n"; + s << "}\n\n"; - s << "if (" << PYTHON_RETURN_VAR << " && !PyErr_Occurred())\n"; - { - Indentation indent(s); - s << "return " << PYTHON_RETURN_VAR << ";\n"; + s << "break;\n" << outdent; } - s << baseName << "_RichComparison_TypeError:\n" - << "PyErr_SetString(PyExc_NotImplementedError, \"operator not implemented.\");\n" - << returnStatement(m_currentErrorCode) << '\n' << '\n' - << outdent << "}\n\n"; + s << "default:\n" << indent + << richCompareComment + << "return FallbackRichCompare(self, " << PYTHON_ARG << ", op);\n" + << outdent << outdent << "}\n\n" + << "return Shiboken::returnFromRichCompare(" << PYTHON_RETURN_VAR << ");\n" << outdent + << "}\n\n"; } -QString CppGenerator::methodDefinitionParameters(const OverloadData &overloadData) const +// Return a flag combination for PyMethodDef +QByteArrayList CppGenerator::methodDefinitionParameters(const OverloadData &overloadData) const { - bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(overloadData); - const auto func = overloadData.referenceFunction(); + const bool usePyArgs = overloadData.pythonFunctionWrapperUsesListOfArguments(); int min = overloadData.minArgs(); int max = overloadData.maxArgs(); - QString result; - QTextStream s(&result); - s << "reinterpret_cast<PyCFunction>(" - << cpythonFunctionName(func) << "), "; + QByteArrayList result; if ((min == max) && (max < 2) && !usePyArgs) { - if (max == 0) - s << "METH_NOARGS"; - else - s << "METH_O"; + result.append(max == 0 ? QByteArrayLiteral("METH_NOARGS") + : QByteArrayLiteral("METH_O")); } else { - s << "METH_VARARGS"; + result.append(QByteArrayLiteral("METH_VARARGS")); if (overloadData.hasArgumentWithDefaultValue()) - s << "|METH_KEYWORDS"; + result.append(QByteArrayLiteral("METH_KEYWORDS")); } // METH_STATIC causes a crash when used for global functions (also from // invisible namespaces). - auto ownerClass = func->ownerClass(); + const auto ownerClass = overloadData.referenceFunction()->ownerClass(); if (ownerClass - && !invisibleTopNamespaces().contains(const_cast<AbstractMetaClass *>(ownerClass)) - && overloadData.hasStaticFunction()) { - s << "|METH_STATIC"; + && !invisibleTopNamespaces().contains(std::const_pointer_cast<AbstractMetaClass>(ownerClass))) { + if (overloadData.hasStaticFunction()) + result.append(QByteArrayLiteral("METH_STATIC")); + if (overloadData.hasClassMethod()) + result.append(QByteArrayLiteral("METH_CLASS")); } return result; } -void CppGenerator::writeMethodDefinitionEntries(TextStream &s, - const AbstractMetaFunctionCList &overloads, - qsizetype maxEntries) const +QList<PyMethodDefEntry> + CppGenerator::methodDefinitionEntries(const OverloadData &overloadData) const { - Q_ASSERT(!overloads.isEmpty()); - OverloadData overloadData(overloads, api()); + const QStringList names = overloadData.referenceFunction()->definitionNames(); - const QString parameters = methodDefinitionParameters(overloadData); - const qsizetype count = maxEntries > 0 - ? qMin(names.size(), maxEntries) : names.size(); - for (qsizetype i = 0; i < count; ++i) { - if (i) - s << ",\n"; - s << "{\"" << names.at(i) << "\", " << parameters << '}'; - } + const QString funcName = cpythonFunctionName(overloadData.referenceFunction()); + const QByteArrayList parameters = methodDefinitionParameters(overloadData); + + QList<PyMethodDefEntry> result; + result.reserve(names.size()); + for (const auto &name : names) + result.append({name, funcName, parameters, {}}); + return result; } -void CppGenerator::writeMethodDefinition(TextStream &s, const AbstractMetaFunctionCList &overloads) const +QString CppGenerator::pythonSignature(const AbstractMetaType &type) const { - Q_ASSERT(!overloads.isEmpty()); - const auto func = overloads.constFirst(); - if (m_tpFuncs.contains(func->name())) - return; - - if (OverloadData::hasStaticAndInstanceFunctions(overloads)) { - s << cpythonMethodDefinitionName(func); - } else { - writeMethodDefinitionEntries(s, overloads); + 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(); } - s << ',' << '\n'; + return type.pythonSignature(); } // Format the type signature of a function parameter @@ -5015,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(); @@ -5039,204 +5157,229 @@ QString CppGenerator::signatureParameter(const AbstractMetaArgument &arg) const if (size > 1) s << ']'; - if (!arg.defaultValueExpression().isEmpty()) { - s << '='; - QString e = arg.defaultValueExpression(); - e.replace(QLatin1String("::"), QLatin1String(".")); - s << e; - } return result; } -void CppGenerator::writeSignatureInfo(TextStream &s, const AbstractMetaFunctionCList &overloads) const +void CppGenerator::writeSignatureInfo(TextStream &s, const OverloadData &overloadData) const { - OverloadData overloadData(overloads, api()); const auto rfunc = overloadData.referenceFunction(); QString funcName = fullPythonFunctionName(rfunc, false); - int idx = overloads.length() - 1; + int idx = overloadData.overloads().length() - 1; bool multiple = idx > 0; - for (const auto &f : overloads) { + for (const auto &f : overloadData.overloads()) { QStringList args; // PYSIDE-1328: `self`-ness cannot be computed in Python because there are mixed cases. // Toplevel functions like `PySide6.QtCore.QEnum` are always self-less. if (!(f->isStatic()) && f->ownerClass()) - args << QLatin1String("self"); + args << PYTHON_SELF_VAR; const auto &arguments = f->arguments(); for (qsizetype i = 0, size = arguments.size(); i < size; ++i) { - QString t = f->pyiTypeReplaced(i + 1); - if (t.isEmpty()) { - t = signatureParameter(arguments.at(i)); - } else { - t.prepend(u':'); - t.prepend(arguments.at(i).name()); + const auto n = i + 1; + const auto &arg = arguments.at(i); + if (!f->argumentRemoved(n)) { + QString t = f->pyiTypeReplaced(n); + if (t.isEmpty()) { + t = signatureParameter(arg); + } else { + t.prepend(u':'); + t.prepend(arg.name()); + } + QString defaultValue = arg.defaultValueExpression(); + if (!defaultValue.isEmpty()) + t += u'=' + defaultValue.replace(u"::"_s, u"."_s); + args.append(t); } - args.append(t); } // mark the multiple signatures as such, to make it easier to generate different code if (multiple) s << idx-- << ':'; - s << funcName << '(' << args.join(QLatin1Char(',')) << ')'; - if (!f->isVoid()) { - QString t = f->pyiTypeReplaced(0); - if (t.isEmpty()) - t = f->type().pythonSignature(); - s << "->" << t; - } + s << funcName << '(' << args.join(u',') << ')'; + + QString returnType = f->pyiTypeReplaced(0); // pyi or modified type + if (returnType.isEmpty() && !f->isVoid()) + returnType = pythonSignature(f->type()); + if (!returnType.isEmpty()) + s << "->" << returnType; + s << '\n'; } } -void CppGenerator::writeEnumsInitialization(TextStream &s, AbstractMetaEnumList &enums) const +void CppGenerator::writeEnumsInitialization(TextStream &s, AbstractMetaEnumList &enums) { if (enums.isEmpty()) return; - s << "// Initialization of enums.\n\n"; - for (const AbstractMetaEnum &cppEnum : qAsConst(enums)) { + bool preambleWritten = false; + bool etypeUsed = false; + + for (const AbstractMetaEnum &cppEnum : std::as_const(enums)) { if (cppEnum.isPrivate()) continue; - writeEnumInitialization(s, cppEnum); + if (!preambleWritten) { + s << "// Initialization of enums.\n" + << "Shiboken::AutoDecRef tpDict{};\n" + << "PyTypeObject *EType{};\n\n"; + preambleWritten = true; + } + ConfigurableScope configScope(s, cppEnum.typeEntry()); + etypeUsed |= writeEnumInitialization(s, cppEnum); } + if (preambleWritten && !etypeUsed) + s << sbkUnusedVariableCast("EType"); } -static QString mangleName(QString name) +static qsizetype maxLineLength(const QStringList &list) { - if ( name == QLatin1String("None") - || name == QLatin1String("False") - || name == QLatin1String("True")) - name += QLatin1Char('_'); - return name; + qsizetype result = 0; + for (const auto &s : list) { + if (auto len = s.size(); len > result) + result = len; + } + return result; } -void CppGenerator::writeEnumInitialization(TextStream &s, const AbstractMetaEnum &cppEnum) const +bool CppGenerator::writeEnumInitialization(TextStream &s, const AbstractMetaEnum &cppEnum) { - const AbstractMetaClass *enclosingClass = cppEnum.targetLangEnclosingClass(); - bool hasUpperEnclosingClass = enclosingClass && enclosingClass->targetLangEnclosingClass() != nullptr; - const EnumTypeEntry *enumTypeEntry = cppEnum.typeEntry(); + const auto enclosingClass = cppEnum.targetLangEnclosingClass(); + const bool hasUpperEnclosingClass = enclosingClass + && enclosingClass->targetLangEnclosingClass(); + EnumTypeEntryCPtr enumTypeEntry = cppEnum.typeEntry(); QString enclosingObjectVariable; if (enclosingClass) enclosingObjectVariable = cpythonTypeName(enclosingClass); else if (hasUpperEnclosingClass) - enclosingObjectVariable = QLatin1String("enclosingClass"); + enclosingObjectVariable = u"enclosingClass"_s; else - enclosingObjectVariable = QLatin1String("module"); + enclosingObjectVariable = u"module"_s; s << "// Initialization of "; s << (cppEnum.isAnonymous() ? "anonymous enum identified by enum value" : "enum"); s << " '" << cppEnum.name() << "'.\n"; - QString enumVarTypeObj; - if (!cppEnum.isAnonymous()) { - int packageLevel = packageName().count(QLatin1Char('.')) + 1; - FlagsTypeEntry *flags = enumTypeEntry->flags(); - if (flags) { - // The following could probably be made nicer: - // We need 'flags->flagsName()' with the full module/class path. - QString fullPath = getClassTargetFullName(cppEnum); - fullPath.truncate(fullPath.lastIndexOf(QLatin1Char('.')) + 1); - s << cpythonTypeNameExt(flags) << " = PySide::QFlags::create(\"" - << packageLevel << ':' << fullPath << flags->flagsName() << "\", " - << cpythonEnumName(cppEnum) << "_number_slots);\n"; - } + const QString userType = cppEnum.typeEntry()->cppType(); + const bool isSigned = cppEnum.isSigned() && !userType.contains(u"unsigned"_s); + const bool isAccessible = !avoidProtectedHack() || !cppEnum.isProtected(); + const auto enumValues = cppEnum.nonRejectedValues(); - enumVarTypeObj = cpythonTypeNameExt(enumTypeEntry); - - s << enumVarTypeObj << " = Shiboken::Enum::" - << ((enclosingClass || hasUpperEnclosingClass) ? "createScopedEnum" : "createGlobalEnum") - << '(' << enclosingObjectVariable << ',' << '\n'; - { - Indentation indent(s); - s << '"' << cppEnum.name() << "\",\n" - << '"' << packageLevel << ':' << getClassTargetFullName(cppEnum) << "\",\n" - << '"' << cppEnum.qualifiedCppName() << '"'; - if (flags) - s << ",\n" << cpythonTypeNameExt(flags); - s << ");\n"; - } - s << "if (!" << cpythonTypeNameExt(cppEnum.typeEntry()) << ")\n"; - { - Indentation indent(s); - s << returnStatement(m_currentErrorCode) << "\n\n"; + const QString prefix = cppEnum.name(); + + const QString intType = userType.isEmpty() ? cppEnum.underlyingType() : userType; + + // Create a list of values + const QString initializerValues = prefix + u"_InitializerValues"_s; + const QString initializerName = prefix + u"_Initializer"_s; + + // Build maybe array of enum names. + if (cppEnum.enumKind() != AnonymousEnum) { + s << "const char *" << initializerName << "[] = {\n" << indent; + for (const auto &enumValue : enumValues) { + QString name = mangleName(enumValue.name()); + s << '\"' << name << "\",\n"; } + s << "nullptr};\n" << outdent; } - for (const AbstractMetaEnumValue &enumValue : cppEnum.values()) { - if (enumTypeEntry->isEnumValueRejected(enumValue.name())) - continue; + int targetHexLen = 0; + QString usedIntType = userType; + if (usedIntType.isEmpty()) { + const int usedBits = cppEnum.usedBits(); + targetHexLen = usedBits / 4; + usedIntType = AbstractMetaEnum::intTypeForSize(usedBits, cppEnum.isSigned()); + } - QString enumValueText; - if (!avoidProtectedHack() || !cppEnum.isProtected()) { - enumValueText = QLatin1String("(long) "); - if (cppEnum.enclosingClass()) - enumValueText += cppEnum.enclosingClass()->qualifiedCppName() + QLatin1String("::"); - // Fully qualify the value which is required for C++ 11 enum classes. - if (!cppEnum.isAnonymous()) - enumValueText += cppEnum.name() + QLatin1String("::"); - enumValueText += enumValue.name(); - } else { - enumValueText += enumValue.value().toString(); - } + if (usedIntType != intType) + s << "// \"" << usedIntType << "\" used instead of \"" << intType << "\"\n"; - const QString mangledName = mangleName(enumValue.name()); - switch (cppEnum.enumKind()) { - case AnonymousEnum: + // Calculating formatting columns + QString enumValuePrefix; + if (isAccessible) { + if (cppEnum.enclosingClass()) + enumValuePrefix += cppEnum.enclosingClass()->qualifiedCppName() + u"::"_s; + if (!cppEnum.isAnonymous()) + enumValuePrefix += cppEnum.name() + u"::"_s; + } + + // Build array of enum values + if (enumValues.isEmpty()) { + s << "const " << usedIntType << " *" << initializerValues << "{};\n"; + } else { + QStringList values; + values.reserve(enumValues.size()); + s << "constexpr " << usedIntType << ' ' << initializerValues << "[] = {\n" << indent; + for (qsizetype idx = 0, last = enumValues.size() - 1; idx <= last; ++idx) { + const auto &enumValue = enumValues.at(idx); + QString line = usedIntType + u'(' + (isAccessible + ? enumValuePrefix + enumValue.name() + : enumValue.value().toString()) + u')'; + if (idx != last) + line += u','; + values.append(line); + } + + const auto len = maxLineLength(values) + 1; + for (qsizetype idx = 0, size = enumValues.size(); idx < size; ++idx) { + const auto &enumValue = enumValues.at(idx).value(); + const char *numberSpace = enumValue.isNegative() ? " " : " "; + s << values.at(idx) << Pad(' ', len - values.at(idx).size()) + << "//" << numberSpace << enumValue.toHex(targetHexLen) + << numberSpace << enumValue.toString() << '\n'; + } + s << "};\n" << outdent; + } + + // Build initialization of anonymous enums + if (cppEnum.enumKind() == AnonymousEnum) { + int idx = 0; + for (const auto &enumValue : enumValues) { + const QString mangledName = mangleName(enumValue.name()); + const QString pyValue = initializerValues + u'[' + QString::number(idx++) + u']'; if (enclosingClass || hasUpperEnclosingClass) { - s << "{\n"; - { - Indentation indent(s); - s << "PyObject *anonEnumItem = PyInt_FromLong(" << enumValueText << ");\n" - << "if (PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(reinterpret_cast<SbkObjectType *>(" - << enclosingObjectVariable - << "))->tp_dict, \"" << mangledName << "\", anonEnumItem) < 0)\n"; - { - Indentation indent(s); - s << returnStatement(m_currentErrorCode) << '\n'; - } - s << "Py_DECREF(anonEnumItem);\n"; - } - s << "}\n"; + s << "tpDict.reset(PepType_GetDict(reinterpret_cast<PyTypeObject *>(" + << enclosingObjectVariable << ")));\n" + << "PyDict_SetItemString(tpDict.object(), \"" << mangledName << "\",\n" + << indent << (isSigned ? "PyLong_FromLongLong" : "PyLong_FromUnsignedLongLong") + << "(" << pyValue << "));\n" << outdent; } else { - s << "if (PyModule_AddIntConstant(module, \"" << mangledName << "\", "; - s << enumValueText << ") < 0)\n"; - { - Indentation indent(s); - s << returnStatement(m_currentErrorCode) << '\n'; - } + s << "PyModule_AddObject(module, \"" << mangledName << "\",\n" << indent + << (isSigned ? "PyLong_FromLongLong" : "PyLong_FromUnsignedLongLong") << "(" + << pyValue << "));\n" << outdent; } - break; - case CEnum: { - s << "if (!Shiboken::Enum::"; - s << ((enclosingClass || hasUpperEnclosingClass) ? "createScopedEnumItem" : "createGlobalEnumItem"); - s << '(' << enumVarTypeObj << ',' << '\n'; - Indentation indent(s); - s << enclosingObjectVariable << ", \"" << mangledName << "\", " - << enumValueText << "))\n" - << returnStatement(m_currentErrorCode) << '\n'; - } - break; - case EnumClass: { - s << "if (!Shiboken::Enum::createScopedEnumItem(" - << enumVarTypeObj << ',' << '\n'; - Indentation indent(s); - s << enumVarTypeObj<< ", \"" << mangledName << "\", " - << enumValueText << "))\n" - << returnStatement(m_currentErrorCode) << '\n'; - } - break; } } + bool etypeUsed = false; + + QString enumVarTypeObj = cpythonTypeNameExtSet(enumTypeEntry); + if (!cppEnum.isAnonymous()) { + int packageLevel = packageName().count(u'.') + 1; + s << "EType = Shiboken::Enum::" + << "createPythonEnum" + << '(' << enclosingObjectVariable << ",\n" << indent + << '"' << packageLevel << ':' << getClassTargetFullName(cppEnum) << "\",\n" + << initializerName << ", " << initializerValues << ");\n" << outdent + << enumVarTypeObj << " = EType;\n"; + etypeUsed = true; + } + + if (cppEnum.typeEntry()->flags()) { + s << "// PYSIDE-1735: Mapping the flags class to the same enum class.\n" + << cpythonTypeNameExtSet(cppEnum.typeEntry()->flags()) << " =\n" + << indent << "EType;\n" << outdent; + } writeEnumConverterInitialization(s, cppEnum); s << "// End of '" << cppEnum.name() << "' enum"; if (cppEnum.typeEntry()->flags()) s << "/flags"; s << ".\n\n"; + + return etypeUsed; } -void CppGenerator::writeSignalInitialization(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeSignalInitialization(TextStream &s, const AbstractMetaClassCPtr &metaClass) { // Try to check something and print some warnings const auto &signalFuncs = metaClass->cppSignalFunctions(); @@ -5258,151 +5401,31 @@ void CppGenerator::writeSignalInitialization(TextStream &s, const AbstractMetaCl } } - s << "PySide::Signal::registerSignals(" << cpythonTypeName(metaClass) << ", &::" + s << "PySide::Signal::registerSignals(pyType, &" << m_gsp << metaClass->qualifiedCppName() << "::staticMetaObject);\n"; } -void CppGenerator::writeFlagsToLong(TextStream &s, const AbstractMetaEnum &cppEnum) -{ - FlagsTypeEntry *flagsEntry = cppEnum.typeEntry()->flags(); - if (!flagsEntry) - return; - s << "static PyObject *" << cpythonEnumName(cppEnum) << "_long(PyObject *self)\n" - << "{\n" << indent - << "int val;\n"; - AbstractMetaType flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); - s << cpythonToCppConversionFunction(flagsType) << "self, &val);\n" - << "return Shiboken::Conversions::copyToPython(Shiboken::Conversions::PrimitiveTypeConverter<int>(), &val);\n" - << outdent << "}\n"; -} - -void CppGenerator::writeFlagsNonZero(TextStream &s, const AbstractMetaEnum &cppEnum) -{ - FlagsTypeEntry *flagsEntry = cppEnum.typeEntry()->flags(); - if (!flagsEntry) - return; - s << "static int " << cpythonEnumName(cppEnum) << "__nonzero(PyObject *self)\n"; - s << "{\n" << indent << "int val;\n"; - AbstractMetaType flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); - s << cpythonToCppConversionFunction(flagsType) << "self, &val);\n" - << "return val != 0;\n" - << outdent << "}\n"; -} - -void CppGenerator::writeFlagsMethods(TextStream &s, const AbstractMetaEnum &cppEnum) -{ - writeFlagsBinaryOperator(s, cppEnum, QLatin1String("and"), QLatin1String("&")); - writeFlagsBinaryOperator(s, cppEnum, QLatin1String("or"), QLatin1String("|")); - writeFlagsBinaryOperator(s, cppEnum, QLatin1String("xor"), QLatin1String("^")); - - writeFlagsUnaryOperator(s, cppEnum, QLatin1String("invert"), QLatin1String("~")); - writeFlagsToLong(s, cppEnum); - writeFlagsNonZero(s, cppEnum); - - s << '\n'; -} - -void CppGenerator::writeFlagsNumberMethodsDefinition(TextStream &s, const AbstractMetaEnum &cppEnum) -{ - QString cpythonName = cpythonEnumName(cppEnum); - - s << "static PyType_Slot " << cpythonName << "_number_slots[] = {\n" << indent - << "{Py_nb_bool, reinterpret_cast<void *>(" << cpythonName << "__nonzero)},\n" - << "{Py_nb_invert, reinterpret_cast<void *>(" << cpythonName << "___invert__)},\n" - << "{Py_nb_and, reinterpret_cast<void *>(" << cpythonName << "___and__)},\n" - << "{Py_nb_xor, reinterpret_cast<void *>(" << cpythonName << "___xor__)},\n" - << "{Py_nb_or, reinterpret_cast<void *>(" << cpythonName << "___or__)},\n" - << "{Py_nb_int, reinterpret_cast<void *>(" << cpythonName << "_long)},\n" - << "{Py_nb_index, reinterpret_cast<void *>(" << cpythonName << "_long)},\n" - << "{0, " << NULL_PTR << "} // sentinel\n" << outdent - << "};\n\n"; -} - -void CppGenerator::writeFlagsNumberMethodsDefinitions(TextStream &s, - const AbstractMetaEnumList &enums) -{ - for (const AbstractMetaEnum &e : enums) { - if (!e.isAnonymous() && !e.isPrivate() && e.typeEntry()->flags()) { - writeFlagsMethods(s, e); - writeFlagsNumberMethodsDefinition(s, e); - s << '\n'; - } - } -} - -void CppGenerator::writeFlagsBinaryOperator(TextStream &s, const AbstractMetaEnum &cppEnum, - const QString &pyOpName, const QString &cppOpName) -{ - FlagsTypeEntry *flagsEntry = cppEnum.typeEntry()->flags(); - Q_ASSERT(flagsEntry); - - s << "PyObject *" << cpythonEnumName(cppEnum) << "___" << pyOpName - << "__(PyObject *self, PyObject *" << PYTHON_ARG << ")\n{\n" << indent; - - AbstractMetaType flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); - s << "::" << flagsEntry->originalName() << " cppResult, " << CPP_SELF_VAR - << ", cppArg;\n" - << CPP_SELF_VAR << " = static_cast<::" << flagsEntry->originalName() - << ">(int(PyLong_AsLong(self)));\n" - << "cppArg = static_cast<" << flagsEntry->originalName() - << ">(int(PyLong_AsLong(" << PYTHON_ARG << ")));\n" - << "if (PyErr_Occurred())\n" << indent - << "return nullptr;\n" << outdent - << "cppResult = " << CPP_SELF_VAR << " " << cppOpName << " cppArg;\n" - << "return "; - writeToPythonConversion(s, flagsType, nullptr, QLatin1String("cppResult")); - s << ";\n" << outdent << "}\n\n"; -} - -void CppGenerator::writeFlagsUnaryOperator(TextStream &s, const AbstractMetaEnum &cppEnum, - const QString &pyOpName, - const QString &cppOpName, bool boolResult) -{ - FlagsTypeEntry *flagsEntry = cppEnum.typeEntry()->flags(); - Q_ASSERT(flagsEntry); - - s << "PyObject *" << cpythonEnumName(cppEnum) << "___" << pyOpName - << "__(PyObject *self, PyObject *" << PYTHON_ARG << ")\n{\n" << indent; - - AbstractMetaType flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); - s << "::" << flagsEntry->originalName() << " " << CPP_SELF_VAR << ";\n" - << cpythonToCppConversionFunction(flagsType) << "self, &" << CPP_SELF_VAR - << ");\n"; - if (boolResult) - s << "bool"; - else - s << "::" << flagsEntry->originalName(); - s << " cppResult = " << cppOpName << CPP_SELF_VAR << ";\n" - << "return "; - if (boolResult) - s << "PyBool_FromLong(cppResult)"; - else - writeToPythonConversion(s, flagsType, nullptr, QLatin1String("cppResult")); - s << ";\n" << outdent << "}\n\n"; -} - -QString CppGenerator::getSimpleClassInitFunctionName(const AbstractMetaClass *metaClass) +QString CppGenerator::getSimpleClassInitFunctionName(const AbstractMetaClassCPtr &metaClass) { QString initFunctionName; // Disambiguate namespaces per module to allow for extending them. if (metaClass->isNamespace()) initFunctionName += moduleName(); initFunctionName += metaClass->qualifiedCppName(); - initFunctionName.replace(QLatin1String("::"), QLatin1String("_")); + initFunctionName.replace(u"::"_s, u"_"_s); return initFunctionName; } -QString CppGenerator::getSimpleClassStaticFieldsInitFunctionName(const AbstractMetaClass *metaClass) +QString + CppGenerator::getSimpleClassStaticFieldsInitFunctionName(const AbstractMetaClassCPtr &metaClass) { - return QLatin1String("init_") + getSimpleClassInitFunctionName(metaClass) - + QLatin1String("StaticFields"); + return u"init_"_s + getSimpleClassInitFunctionName(metaClass) + + u"StaticFields"_s; } QString CppGenerator::getInitFunctionName(const GeneratorContext &context) { - return !context.forSmartPointer() - ? getSimpleClassInitFunctionName(context.metaClass()) - : getFilteredCppSignatureString(context.preciseType().cppSignature()); + return getSimpleClassInitFunctionName(context.metaClass()); } void CppGenerator::writeSignatureStrings(TextStream &s, @@ -5416,7 +5439,7 @@ void CppGenerator::writeSignatureStrings(TextStream &s, const auto lines = QStringView{signatures}.split(u'\n', Qt::SkipEmptyParts); for (auto line : lines) { // must anything be escaped? - if (line.contains(QLatin1Char('"')) || line.contains(QLatin1Char('\\'))) + if (line.contains(u'"') || line.contains(u'\\')) s << "R\"CPP(" << line << ")CPP\",\n"; else s << '"' << line << "\",\n"; @@ -5425,13 +5448,13 @@ void CppGenerator::writeSignatureStrings(TextStream &s, } // Return the class name for which to invoke the destructor -QString CppGenerator::destructorClassName(const AbstractMetaClass *metaClass, - const GeneratorContext &classContext) const +QString CppGenerator::destructorClassName(const AbstractMetaClassCPtr &metaClass, + const GeneratorContext &classContext) { if (metaClass->isNamespace() || metaClass->hasPrivateDestructor()) return {}; if (classContext.forSmartPointer()) - return classContext.smartPointerWrapperName(); + return classContext.effectiveClassName(); const bool isValue = metaClass->typeEntry()->isValue(); const bool hasProtectedDestructor = metaClass->hasProtectedDestructor(); if (((avoidProtectedHack() && hasProtectedDestructor) || isValue) @@ -5443,130 +5466,162 @@ QString CppGenerator::destructorClassName(const AbstractMetaClass *metaClass, return metaClass->qualifiedCppName(); } +// Return the base type entries for introduceWrapperType() +static ComplexTypeEntryCList pyBaseTypeEntries(const AbstractMetaClassCPtr &metaClass) +{ + ComplexTypeEntryCList result; + if (metaClass->isNamespace()) { + if (auto extended = metaClass->extendedNamespace()) + result.append(extended->typeEntry()); + return result; + } + + const auto &baseClasses = metaClass->typeSystemBaseClasses(); + for (auto base : baseClasses) { + for (; base != nullptr; base = base->baseClass()) { // Find a type that is not disabled. + const auto ct = base->typeEntry()->codeGeneration(); + if (ct == TypeEntry::GenerateCode || ct == TypeEntry::GenerateForSubclass) + break; + } + result.append(base->typeEntry()); + } + return result; +} + +// Return the base type strings for introduceWrapperType() +QStringList CppGenerator::pyBaseTypes(const AbstractMetaClassCPtr &metaClass) +{ + const auto &baseEntries = pyBaseTypeEntries(metaClass); + QStringList result; + for (const auto &baseEntry : baseEntries) + result.append("reinterpret_cast<PyObject *>("_L1 + cpythonTypeNameExt(baseEntry) + u')'); + if (result.isEmpty()) // no base classes -> SbkObjectType. + result.append(sbkObjectTypeF); + return result; +} + +void CppGenerator::writeInitInheritance(TextStream &s) const +{ + s << "static void " << initInheritanceFunction << "()\n{\n" << indent + << "auto &bm = Shiboken::BindingManager::instance();\n" + << sbkUnusedVariableCast("bm"); + for (const auto &cls : api().classes()){ + auto te = cls->typeEntry(); + if (shouldGenerate(te)) { + const auto &baseEntries = pyBaseTypeEntries(cls); + if (!baseEntries.isEmpty()) { + const QString childTypeInitStruct = typeInitStruct(cls->typeEntry()); + for (const auto &baseEntry : baseEntries) { + s << "bm.addClassInheritance(&" << typeInitStruct(baseEntry) << ",\n" + << Pad(' ', 23) << '&' << childTypeInitStruct << ");\n"; + } + } + } + } + s << outdent << "}\n\n"; +} + void CppGenerator::writeClassRegister(TextStream &s, - const AbstractMetaClass *metaClass, + const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext, const QString &signatures) const { - const ComplexTypeEntry *classTypeEntry = metaClass->typeEntry(); + ComplexTypeEntryCPtr classTypeEntry = metaClass->typeEntry(); - const AbstractMetaClass *enc = metaClass->targetLangEnclosingClass(); - QString enclosingObjectVariable = enc ? QLatin1String("enclosingClass") : QLatin1String("module"); + AbstractMetaClassCPtr enc = metaClass->targetLangEnclosingClass(); + QString enclosingObjectVariable = enc ? u"enclosingClass"_s : u"module"_s; QString pyTypeName = cpythonTypeName(metaClass); QString initFunctionName = getInitFunctionName(classContext); // PYSIDE-510: Create a signatures string for the introspection feature. writeSignatureStrings(s, signatures, initFunctionName, "functions"); - s << "void init_" << initFunctionName; - s << "(PyObject *" << enclosingObjectVariable << ")\n{\n" << indent; + s << "PyTypeObject *init_" << initFunctionName + << "(PyObject *" << enclosingObjectVariable << ")\n{\n" << indent; + + const QString globalTypeVarExpr = !classContext.forSmartPointer() + ? cpythonTypeNameExtSet(classTypeEntry) + : cpythonTypeNameExtSet(classContext.preciseType()); + s << "if (" << globalTypeVarExpr << " != nullptr)\n" << indent + << "return " << globalTypeVarExpr << ";\n\n" << outdent; // Multiple inheritance - QString pyTypeBasesVariable = chopType(pyTypeName) + QLatin1String("_Type_bases"); - const AbstractMetaClassList baseClasses = metaClass->typeSystemBaseClasses(); - if (metaClass->baseClassNames().size() > 1) { - s << "PyObject *" << pyTypeBasesVariable - << " = PyTuple_Pack(" << baseClasses.size() << ',' << '\n'; - Indentation indent(s); - for (int i = 0, size = baseClasses.size(); i < size; ++i) { - if (i) - s << ",\n"; - s << "reinterpret_cast<PyObject *>(" - << cpythonTypeNameExt(baseClasses.at(i)->typeEntry()) << ')'; - } - s << ");\n\n"; + QString pyTypeBasesVariable = chopType(pyTypeName) + u"_Type_bases"_s; + const QStringList pyBases = pyBaseTypes(metaClass); + s << "Shiboken::AutoDecRef " << pyTypeBasesVariable << "(PyTuple_Pack(" + << pyBases.size() << ",\n" << indent; + for (qsizetype i = 0, size = pyBases.size(); i < size; ++i) { + if (i) + s << ",\n"; + s << pyBases.at(i); } + s << "));\n\n" << outdent; // Create type and insert it in the module or enclosing class. - const QString typePtr = QLatin1String("_") + chopType(pyTypeName) - + QLatin1String("_Type"); + const QString typePtr = u"_"_s + chopType(pyTypeName) + + u"_Type"_s; - s << typePtr << " = Shiboken::ObjectType::introduceWrapperType(\n"; - { - Indentation indent(s); - // 1:enclosingObject - s << enclosingObjectVariable << ",\n"; - QString typeName; - if (!classContext.forSmartPointer()) - typeName = metaClass->name(); - else - typeName = classContext.preciseType().cppSignature(); + s << typePtr << " = Shiboken::ObjectType::introduceWrapperType(\n" << indent; + // 1:enclosingObject + s << enclosingObjectVariable << ",\n"; - // 2:typeName - s << "\"" << typeName << "\",\n"; + // 2:typeName + s << "\"" << metaClass->name() << "\",\n"; - // 3:originalName - s << "\""; - if (!classContext.forSmartPointer()) { - s << metaClass->qualifiedCppName(); - if (classTypeEntry->isObject()) - s << '*'; - } else { - s << classContext.preciseType().cppSignature(); - } + // 3:originalName + s << "\""; + if (!classContext.forSmartPointer()) { + s << metaClass->qualifiedCppName(); + if (classTypeEntry->isObject()) + s << '*'; + } else { + s << classContext.preciseType().cppSignature(); + } - s << "\",\n"; - // 4:typeSpec - s << '&' << chopType(pyTypeName) << "_spec,\n"; + s << "\",\n"; + // 4:typeSpec + s << '&' << chopType(pyTypeName) << "_spec,\n"; - // 5:cppObjDtor - QString dtorClassName = destructorClassName(metaClass, classContext); - if (dtorClassName.isEmpty()) - s << "nullptr,\n"; - else - s << "&Shiboken::callCppDestructor< ::" << dtorClassName << " >,\n"; - - // 6:baseType: Find a type that is not disabled. - auto base = metaClass->isNamespace() - ? metaClass->extendedNamespace() : metaClass->baseClass(); - if (!metaClass->isNamespace()) { - for (; base != nullptr; base = base->baseClass()) { - const auto ct = base->typeEntry()->codeGeneration(); - if (ct == TypeEntry::GenerateCode || ct == TypeEntry::GenerateForSubclass) - break; - } - } - if (base) { - s << "reinterpret_cast<SbkObjectType *>(" - << cpythonTypeNameExt(base->typeEntry()) << "),\n"; - } else { - s << "0,\n"; - } + // 5:cppObjDtor + QString dtorClassName = destructorClassName(metaClass, classContext); + if (dtorClassName.isEmpty()) + s << "nullptr,\n"; + else + s << "&Shiboken::callCppDestructor< " << globalScopePrefix(classContext) + << dtorClassName << " >,\n"; + + // 7:baseTypes + s << pyTypeBasesVariable << ".object()," << '\n'; + + // 8:wrapperflags + QByteArrayList wrapperFlags; + if (enc) + wrapperFlags.append(QByteArrayLiteral("Shiboken::ObjectType::WrapperFlags::InnerClass")); + if (metaClass->deleteInMainThread()) + wrapperFlags.append(QByteArrayLiteral("Shiboken::ObjectType::WrapperFlags::DeleteInMainThread")); + if (classTypeEntry->isValue()) + wrapperFlags.append("Shiboken::ObjectType::WrapperFlags::Value"_ba); + if (wrapperFlags.isEmpty()) + s << '0'; + else + s << wrapperFlags.join(" | "); - // 7:baseTypes - if (metaClass->baseClassNames().size() > 1) - s << pyTypeBasesVariable << ',' << '\n'; - else - s << "0,\n"; - - // 8:wrapperflags - QByteArrayList wrapperFlags; - if (enc) - wrapperFlags.append(QByteArrayLiteral("Shiboken::ObjectType::WrapperFlags::InnerClass")); - if (metaClass->deleteInMainThread()) - wrapperFlags.append(QByteArrayLiteral("Shiboken::ObjectType::WrapperFlags::DeleteInMainThread")); - if (wrapperFlags.isEmpty()) - s << '0'; - else - s << wrapperFlags.join(" | "); - } - s << ");\nauto pyType = reinterpret_cast<PyTypeObject *>(" << typePtr << ");\n" + s << outdent << ");\nauto *pyType = " << pyTypeName << "; // references " + << typePtr << "\n" << "InitSignatureStrings(pyType, " << initFunctionName << "_SignatureStrings);\n"; - if (usePySideExtensions()) - s << "SbkObjectType_SetPropertyStrings(reinterpret_cast<PyTypeObject *>(" << typePtr << "), " + if (usePySideExtensions() && !classContext.forSmartPointer()) + s << "SbkObjectType_SetPropertyStrings(pyType, " << chopType(pyTypeName) << "_PropertyStrings);\n"; - - if (!classContext.forSmartPointer()) - s << cpythonTypeNameExt(classTypeEntry) << '\n'; - else - s << cpythonTypeNameExt(classContext.preciseType()) << '\n'; - s << " = reinterpret_cast<PyTypeObject *>(" << pyTypeName << ");\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(), @@ -5576,14 +5631,14 @@ 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) { s << multipleInheritanceInitializerFunctionName(miClass) << ";\n"; } else { - s << "Shiboken::ObjectType::getMultipleInheritanceFunction(reinterpret_cast<SbkObjectType *>(" - << cpythonTypeNameExt(miClass->typeEntry()) << "));\n"; + s << "Shiboken::ObjectType::getMultipleInheritanceFunction(" + << cpythonTypeNameExt(miClass->typeEntry()) << ");\n"; } s << "Shiboken::ObjectType::setMultipleInheritanceFunction(" << cpythonTypeName(metaClass) << ", func);\n" @@ -5592,15 +5647,19 @@ void CppGenerator::writeClassRegister(TextStream &s, } // Set typediscovery struct or fill the struct of another one - if (metaClass->isPolymorphic() && metaClass->baseClass()) { - s << "Shiboken::ObjectType::setTypeDiscoveryFunctionV2(" << cpythonTypeName(metaClass) - << ", &" << cpythonBaseName(metaClass) << "_typeDiscovery);\n\n"; + if (needsTypeDiscoveryFunction(metaClass)) { + s << "Shiboken::ObjectType::setTypeDiscoveryFunctionV2(\n" << indent + << cpythonTypeName(metaClass) + << ", &" << cpythonBaseName(metaClass) << "_typeDiscovery);" << outdent << "\n\n"; } AbstractMetaEnumList classEnums = metaClass->enums(); metaClass->getEnumsFromInvisibleNamespacesToBeGenerated(&classEnums); - ErrorCode errorCode(QString::fromLatin1("")); + if (!classContext.forSmartPointer() && !classEnums.isEmpty()) + s << "// Pass the ..._EnumFlagInfo to the class.\n" + << "SbkObjectType_SetEnumFlagInfo(pyType, " << chopType(pyTypeName) + << "_EnumFlagInfo);\n\n"; writeEnumsInitialization(s, classEnums); if (metaClass->hasSignals()) @@ -5621,26 +5680,40 @@ void CppGenerator::writeClassRegister(TextStream &s, writeInitQtMetaTypeFunctionBody(s, classContext); } - if (usePySideExtensions() && metaClass->isQObject()) { - s << "Shiboken::ObjectType::setSubTypeInitHook(" << pyTypeName - << ", &PySide::initQObjectSubType);\n" - << "PySide::initDynamicMetaObject(" << pyTypeName << ", &::" - << metaClass->qualifiedCppName() << "::staticMetaObject, sizeof("; - if (shouldGenerateCppWrapper(metaClass)) - s << wrapperName(metaClass); - else - s << "::" << metaClass->qualifiedCppName(); - s << "));\n"; + if (usePySideExtensions() && isQObject(metaClass)) { + s << "Shiboken::ObjectType::setSubTypeInitHook(pyType, &PySide::initQObjectSubType);\n" + << "PySide::initDynamicMetaObject(pyType, &" << m_gsp + << metaClass->qualifiedCppName() << "::staticMetaObject, sizeof(" + << (shouldGenerateCppWrapper(metaClass) + ? wrapperName(metaClass) : getFullTypeName(metaClass)) + << "));\n"; } - s << outdent << "}\n"; + s << "\nreturn pyType;\n" << outdent << "}\n"; } -void CppGenerator::writeStaticFieldInitialization(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeStaticFieldInitialization(TextStream &s, + const AbstractMetaClassCPtr &metaClass) { - s << "\nvoid " << getSimpleClassStaticFieldsInitFunctionName(metaClass) - << "()\n{\n" << indent << "auto dict = reinterpret_cast<PyTypeObject *>(" - << cpythonTypeName(metaClass) << ")->tp_dict;\n"; + // cpythonTypeName == "Sbk_QRhiShaderResourceBinding_Data_TypeF" + QString name = cpythonTypeName(metaClass); + const auto parts = QStringView{name}.split(u'_', Qt::SkipEmptyParts); + if (parts.size() < 4) { + s << "\nPyTypeObject *" << getSimpleClassStaticFieldsInitFunctionName(metaClass) + << "(PyObject *module)\n{\n" << indent + << "auto *obType = PyObject_GetAttrString(module, \"" << metaClass->name() << "\");\n" + << "auto *type = reinterpret_cast<PyTypeObject *>(obType);\n" + << "Shiboken::AutoDecRef dict(PepType_GetDict(type));\n"; + } else { + s << "\nPyTypeObject *" << getSimpleClassStaticFieldsInitFunctionName(metaClass) + << "(PyObject *module)\n{\n" << indent + << "auto *obContainerType = PyObject_GetAttrString(module, \"" + << parts.at(1) << "\");\n" + << "auto *obType = PyObject_GetAttrString(obContainerType, \"" + << parts.at(2) << "\");\n" + << "auto *type = reinterpret_cast<PyTypeObject *>(obType);\n" + << "Shiboken::AutoDecRef dict(PepType_GetDict(type));\n"; + } for (const AbstractMetaField &field : metaClass->fields()) { if (field.isStatic()) { s << "PyDict_SetItemString(dict, \"" << field.name() @@ -5649,12 +5722,59 @@ void CppGenerator::writeStaticFieldInitialization(TextStream &s, const AbstractM s << ");\n"; } } - s << '\n' << outdent << "}\n"; + s << "return type;\n" << outdent << "}\n"; +} + +enum class QtRegisterMetaType +{ + None, Pointer, Value +}; + +static bool hasQtMetaTypeRegistrationSpec(const AbstractMetaClassCPtr &c) +{ + return c->typeEntry()->qtMetaTypeRegistration() != + TypeSystem::QtMetaTypeRegistration::Unspecified; +} + +// Returns if and how to register the Qt meta type. By default, "pointer" for +// non-QObject object types and "value" for non-abstract, default-constructible +// value types. +QtRegisterMetaType qtMetaTypeRegistration(const AbstractMetaClassCPtr &c) +{ + if (c->isNamespace()) + return QtRegisterMetaType::None; + + // Specified in type system? + const bool isObject = c->isObjectType(); + switch (c->typeEntry()->qtMetaTypeRegistration()) { + case TypeSystem::QtMetaTypeRegistration::Disabled: + return QtRegisterMetaType::None; + case TypeSystem::QtMetaTypeRegistration::Enabled: + case TypeSystem::QtMetaTypeRegistration::BaseEnabled: + return isObject ? QtRegisterMetaType::Pointer : QtRegisterMetaType::Value; + case TypeSystem::QtMetaTypeRegistration::Unspecified: + break; + } + + // Is there a "base" specification in some base class, meaning only the + // base class is to be registered? + if (auto base = recurseClassHierarchy(c, hasQtMetaTypeRegistrationSpec)) { + const auto baseSpec = base->typeEntry()->qtMetaTypeRegistration(); + if (baseSpec == TypeSystem::QtMetaTypeRegistration::BaseEnabled) + return QtRegisterMetaType::None; + } + + // Default. + if (isObject) + return isQObject(c) ? QtRegisterMetaType::None : QtRegisterMetaType::Pointer; + + return !c->isAbstract() && c->isDefaultConstructible() + ? QtRegisterMetaType::Value : QtRegisterMetaType::None; } void CppGenerator::writeInitQtMetaTypeFunctionBody(TextStream &s, const GeneratorContext &context) { - const AbstractMetaClass *metaClass = context.metaClass(); + const auto metaClass = context.metaClass(); // Gets all class name variants used on different possible scopes QStringList nameVariants; if (!context.forSmartPointer()) @@ -5662,10 +5782,10 @@ void CppGenerator::writeInitQtMetaTypeFunctionBody(TextStream &s, const Generato else nameVariants << context.preciseType().cppSignature(); - const AbstractMetaClass *enclosingClass = metaClass->enclosingClass(); + AbstractMetaClassCPtr enclosingClass = metaClass->enclosingClass(); while (enclosingClass) { if (enclosingClass->typeEntry()->generateCode()) - nameVariants << (enclosingClass->name() + QLatin1String("::") + nameVariants.constLast()); + nameVariants << (enclosingClass->name() + u"::"_s + nameVariants.constLast()); enclosingClass = enclosingClass->enclosingClass(); } @@ -5675,69 +5795,74 @@ void CppGenerator::writeInitQtMetaTypeFunctionBody(TextStream &s, const Generato else className = context.preciseType().cppSignature(); - if (!metaClass->isNamespace() && !metaClass->isAbstract()) { - // Qt metatypes are registered only on their first use, so we do this now. - bool canBeValue = false; - if (!metaClass->isObjectType()) { - // check if there's a empty ctor - for (const auto &func : metaClass->functions()) { - if (func->isConstructor() && !func->arguments().count()) { - canBeValue = true; - break; - } - } - } - - if (canBeValue) { - for (const QString &name : qAsConst(nameVariants)) { - s << "qRegisterMetaType< ::" << className << " >(\"" << name << "\");\n"; - } - } + // Register meta types for signal/slot connections to work + // Qt metatypes are registered only on their first use, so we do this now. + switch (qtMetaTypeRegistration(metaClass)) { + case QtRegisterMetaType::None: + break; + case QtRegisterMetaType::Pointer: + s << "qRegisterMetaType< " << m_gsp << className << " *>();\n"; + break; + case QtRegisterMetaType::Value: + for (const QString &name : std::as_const(nameVariants)) + s << "qRegisterMetaType< " << m_gsp << className << " >(\"" << name << "\");\n"; + break; } for (const AbstractMetaEnum &metaEnum : metaClass->enums()) { if (!metaEnum.isPrivate() && !metaEnum.isAnonymous()) { - for (const QString &name : qAsConst(nameVariants)) { - s << "qRegisterMetaType< ::" + for (const QString &name : std::as_const(nameVariants)) { + s << "qRegisterMetaType< " << m_gsp << metaEnum.typeEntry()->qualifiedCppName() << " >(\"" << name << "::" << metaEnum.name() << "\");\n"; } - if (metaEnum.typeEntry()->flags()) { - QString n = metaEnum.typeEntry()->flags()->originalName(); - s << "qRegisterMetaType< ::" << n << " >(\"" << n << "\");\n"; - } } } } -void CppGenerator::writeTypeDiscoveryFunction(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::replacePolymorphicIdPlaceHolders(const AbstractMetaClassCPtr &metaClass, + QString *id) +{ + if (id->contains("%1"_L1)) { + QString replacement = " reinterpret_cast< "_L1 + m_gsp + metaClass->qualifiedCppName() + + " *>(cptr)"_L1; + id->replace("%1"_L1, replacement); + } + if (id->contains("%B"_L1)) { + auto baseClass = metaClass; + while (!baseClass->typeEntry()->isPolymorphicBase() && baseClass->baseClass()) + baseClass = baseClass->baseClass(); + QString replacement = " reinterpret_cast< "_L1 + m_gsp + baseClass->qualifiedCppName() + + " *>(cptr)"_L1; + id->replace("%B"_L1, replacement); + } +} + +void CppGenerator::writeTypeDiscoveryFunction(TextStream &s, + const AbstractMetaClassCPtr &metaClass) { QString polymorphicExpr = metaClass->typeEntry()->polymorphicIdValue(); s << "static void *" << cpythonBaseName(metaClass) - << "_typeDiscovery(void *cptr, SbkObjectType *instanceType)\n{\n" << indent; + << "_typeDiscovery(void *cptr, PyTypeObject *instanceType)\n{\n" << indent + << sbkUnusedVariableCast("cptr") + << sbkUnusedVariableCast("instanceType"); if (!polymorphicExpr.isEmpty()) { - polymorphicExpr = polymorphicExpr.replace(QLatin1String("%1"), - QLatin1String(" reinterpret_cast< ::") - + metaClass->qualifiedCppName() - + QLatin1String(" *>(cptr)")); - s << " if (" << polymorphicExpr << ")\n"; - { - Indentation indent(s); - s << "return cptr;\n"; - } + replacePolymorphicIdPlaceHolders(metaClass, &polymorphicExpr); + s << "if (" << polymorphicExpr << ")\n" << indent + << "return cptr;\n" << outdent; } else if (metaClass->isPolymorphic()) { - const AbstractMetaClassList &ancestors = metaClass->allTypeSystemAncestors(); - for (AbstractMetaClass *ancestor : ancestors) { - if (ancestor->baseClass()) + const auto &ancestors = metaClass->allTypeSystemAncestors(); + for (const auto &ancestor : ancestors) { + if (ancestor->baseClass() && !ancestor->typeEntry()->isPolymorphicBase()) continue; if (ancestor->isPolymorphic()) { - s << "if (instanceType == reinterpret_cast<SbkObjectType *>(Shiboken::SbkType< ::" - << ancestor->qualifiedCppName() << " >()))\n"; - Indentation indent(s); - s << "return dynamic_cast< ::" << metaClass->qualifiedCppName() - << " *>(reinterpret_cast< ::"<< ancestor->qualifiedCppName() << " *>(cptr));\n"; + s << "if (instanceType == Shiboken::SbkType< " << m_gsp + << ancestor->qualifiedCppName() << " >())\n" << indent + << "return dynamic_cast< " << getFullTypeName(metaClass) + << " *>(reinterpret_cast< "<< getFullTypeName(ancestor) + << " *>(cptr));\n" << outdent; } else { qCWarning(lcShiboken).noquote().nospace() << metaClass->qualifiedCppName() << " inherits from a non polymorphic type (" @@ -5750,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; @@ -5760,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"; @@ -5770,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. @@ -5780,62 +5906,36 @@ void CppGenerator::writeSetattroFunction(TextStream &s, AttroCheck attroCheck, // PYSIDE-803: Detect duck-punching; clear cache if a method is set. if (attroCheck.testFlag(AttroCheckFlag::SetattroMethodOverride) && context.useWrapper()) { - s << "if (value && PyCallable_Check(value)) {\n" << indent - << "auto plain_inst = " << cpythonWrapperCPtr(metaClass, QLatin1String("self")) << ";\n" - << "auto inst = dynamic_cast<" << context.wrapperName() << " *>(plain_inst);\n" - << "if (inst)\n" << indent + s << "if (value != nullptr && PyCallable_Check(value) != 0) {\n" << indent + << "auto plain_inst = " << cpythonWrapperCPtr(metaClass, PYTHON_SELF_VAR) << ";\n" + << "auto *inst = dynamic_cast<" << context.wrapperName() << " *>(plain_inst);\n" + << "if (inst != nullptr)\n" << indent << "inst->resetPyMethodCache();\n" << outdent << outdent << "}\n"; } if (attroCheck.testFlag(AttroCheckFlag::SetattroQObject)) { s << "Shiboken::AutoDecRef pp(reinterpret_cast<PyObject *>(PySide::Property::getObject(self, name)));\n" - << "if (!pp.isNull())\n"; - Indentation indent(s); - s << "return PySide::Property::setValue(reinterpret_cast<PySideProperty *>(pp.object()), self, value);\n"; + << "if (!pp.isNull())\n" << indent + << "return PySide::Property::setValue(reinterpret_cast<PySideProperty *>(pp.object()), self, value);\n" + << outdent; } if (attroCheck.testFlag(AttroCheckFlag::SetattroUser)) { auto func = AbstractMetaClass::queryFirstFunction(metaClass->functions(), FunctionQueryOption::SetAttroFunction); Q_ASSERT(func); - s << "{\n"; - { - Indentation indent(s); - s << "auto " << CPP_SELF_VAR << " = " - << cpythonWrapperCPtr(metaClass, QLatin1String("self")) << ";\n"; - writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny, - TypeSystem::TargetLangCode, context); - } - s << "}\n"; + s << "{\n" << indent + << "auto " << CPP_SELF_VAR << " = " + << cpythonWrapperCPtr(metaClass, PYTHON_SELF_VAR) << ";\n"; + writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny, + TypeSystem::TargetLangCode, context); + s << outdent << "}\n"; } writeSetattroDefaultReturn(s); } -void CppGenerator::writeSmartPointerSetattroFunction(TextStream &s, - const GeneratorContext &context) const -{ - Q_ASSERT(context.forSmartPointer()); - writeSetattroDefinition(s, context.metaClass()); - s << "// Try to find the 'name' attribute, by retrieving the PyObject for the corresponding C++ object held by the smart pointer.\n" - << "PyObject *rawObj = PyObject_CallMethod(self, " - << SMART_POINTER_GETTER << ", 0);\n"; - s << "if (rawObj) {\n"; - { - Indentation indent(s); - s << "int hasAttribute = PyObject_HasAttr(rawObj, name);\n" - << "if (hasAttribute) {\n"; - { - Indentation indent(s); - s << "return PyObject_GenericSetAttr(rawObj, name, value);\n"; - } - s << "}\nPy_DECREF(rawObj);\n"; - } - s << "}\n"; - writeSetattroDefaultReturn(s); -} - -void CppGenerator::writeGetattroDefinition(TextStream &s, const AbstractMetaClass *metaClass) +void CppGenerator::writeGetattroDefinition(TextStream &s, const AbstractMetaClassCPtr &metaClass) { s << "static PyObject *" << cpythonGetattroFunctionName(metaClass) << "(PyObject *self, PyObject *name)\n{\n" << indent; @@ -5845,11 +5945,11 @@ QString CppGenerator::qObjectGetAttroFunction() const { static QString result; if (result.isEmpty()) { - auto qobjectClass = AbstractMetaClass::findClass(api().classes(), qObjectT()); + auto qobjectClass = AbstractMetaClass::findClass(api().classes(), qObjectT); Q_ASSERT(qobjectClass); - result = QLatin1String("PySide::getMetaDataFromQObject(") - + cpythonWrapperCPtr(qobjectClass, QLatin1String("self")) - + QLatin1String(", self, name)"); + result = u"PySide::getHiddenDataFromQObject("_s + + cpythonWrapperCPtr(qobjectClass, PYTHON_SELF_VAR) + + u", self, name)"_s; } return result; } @@ -5858,68 +5958,49 @@ void CppGenerator::writeGetattroFunction(TextStream &s, AttroCheck attroCheck, const GeneratorContext &context) const { Q_ASSERT(!context.forSmartPointer()); - const AbstractMetaClass *metaClass = context.metaClass(); + const auto metaClass = context.metaClass(); writeGetattroDefinition(s, metaClass); // PYSIDE-1019: Switch tp_dict before doing tp_getattro. if (usePySideExtensions()) s << "PySide::Feature::Select(self);\n"; - const QString getattrFunc = usePySideExtensions() && metaClass->isQObject() - ? qObjectGetAttroFunction() : QLatin1String("PyObject_GenericGetAttr(self, name)"); + const QString getattrFunc = usePySideExtensions() && isQObject(metaClass) + ? qObjectGetAttroFunction() : u"PyObject_GenericGetAttr(self, name)"_s; if (attroCheck.testFlag(AttroCheckFlag::GetattroOverloads)) { s << "// Search the method in the instance dict\n" - << "if (auto ob_dict = reinterpret_cast<SbkObject *>(self)->ob_dict) {\n"; - { - Indentation indent(s); - s << "if (auto meth = PyDict_GetItem(ob_dict, name)) {\n"; - { - Indentation indent(s); - s << "Py_INCREF(meth);\n" - << "return meth;\n"; - } - s << "}\n"; - } - s << "}\n" + << "auto *ob_dict = SbkObject_GetDict_NoRef(self);\n"; + s << "if (auto *meth = PyDict_GetItem(ob_dict, name)) {\n" << indent + << "Py_INCREF(meth);\nreturn meth;\n" << outdent << "}\n" << "// Search the method in the type dict\n" - << "if (Shiboken::Object::isUserType(self)) {\n"; - { - Indentation indent(s); - // PYSIDE-772: Perform optimized name mangling. - s << "Shiboken::AutoDecRef tmp(_Pep_PrivateMangle(self, name));\n" - << "if (auto meth = PyDict_GetItem(Py_TYPE(self)->tp_dict, tmp)) {\n"; - { - Indentation indent(s); - // PYSIDE-1523: PyFunction_Check is not accepting compiled functions. - s << "if (strcmp(Py_TYPE(meth)->tp_name, \"compiled_function\") == 0)\n"; - { - Indentation indent(s); - s << "return Py_TYPE(meth)->tp_descr_get(meth, self, nullptr);\n"; - } - s << "return PyFunction_Check(meth) ? SBK_PyMethod_New(meth, self)\n" - << " : " << getattrFunc << ";\n"; - } - s << "}\n"; - } - s << "}\n"; + << "if (Shiboken::Object::isUserType(self)) {\n" << indent; + // PYSIDE-772: Perform optimized name mangling. + s << "Shiboken::AutoDecRef tmp(_Pep_PrivateMangle(self, name));\n" + << "Shiboken::AutoDecRef tpDict(PepType_GetDict(Py_TYPE(self)));\n" + << "if (auto *meth = PyDict_GetItem(tpDict.object(), tmp)) {\n" << indent; + // PYSIDE-1523: PyFunction_Check is not accepting compiled functions. + s << "if (std::strcmp(Py_TYPE(meth)->tp_name, \"compiled_function\") == 0) {\n" << indent + << "auto descrGetFunc = " + << pyTypeGetSlot("descrgetfunc", "Py_TYPE(meth)", "Py_tp_descr_get") << ";\n" + << "return descrGetFunc(meth, self, nullptr);\n" << outdent + << "}\nreturn PyFunction_Check(meth) ? PyMethod_New(meth, self)\n" + << " : " << getattrFunc << ";\n" << outdent + << "}\n" << outdent << "}\n"; const auto &funcs = getMethodsWithBothStaticAndNonStaticMethods(metaClass); for (const auto &func : funcs) { QString defName = cpythonMethodDefinitionName(func); - s << "static PyMethodDef non_static_" << defName << " = {\n"; - { - Indentation indent(s); - s << defName << ".ml_name,\n" - << defName << ".ml_meth,\n" - << defName << ".ml_flags & (~METH_STATIC),\n" - << defName << ".ml_doc,\n"; - } - s << "};\n" + s << "static PyMethodDef non_static_" << defName << " = {\n" << indent + << defName << ".ml_name,\n" + << defName << ".ml_meth,\n" + << defName << ".ml_flags & (~METH_STATIC),\n" + << defName << ".ml_doc,\n" << outdent + << "};\n" << "if (Shiboken::String::compare(name, \"" - << func->definitionNames().constFirst() << "\") == 0)\n"; - Indentation indent(s); - s << "return PyCFunction_NewEx(&non_static_" << defName << ", self, 0);\n"; + << func->definitionNames().constFirst() << "\") == 0)\n" << indent + << "return PyCFunction_NewEx(&non_static_" << defName << ", self, 0);\n" + << outdent; } } @@ -5927,106 +6008,118 @@ void CppGenerator::writeGetattroFunction(TextStream &s, AttroCheck attroCheck, auto func = AbstractMetaClass::queryFirstFunction(metaClass->functions(), FunctionQueryOption::GetAttroFunction); Q_ASSERT(func); - s << "{\n"; - { - Indentation indent(s); - s << "auto " << CPP_SELF_VAR << " = " - << cpythonWrapperCPtr(metaClass, QLatin1String("self")) << ";\n"; - writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny, - TypeSystem::TargetLangCode, context); - } - s << "}\n"; + s << "{\n" << indent + << "auto " << CPP_SELF_VAR << " = " + << cpythonWrapperCPtr(metaClass, PYTHON_SELF_VAR) << ";\n"; + writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny, + TypeSystem::TargetLangCode, context); + s << outdent << "}\n"; } s << "return " << getattrFunc << ";\n" << outdent << "}\n\n"; } -void CppGenerator::writeSmartPointerGetattroFunction(TextStream &s, const GeneratorContext &context) +void CppGenerator::writeNbBoolExpression(TextStream &s, const BoolCastFunction &f, + bool invert) { - Q_ASSERT(context.forSmartPointer()); - const AbstractMetaClass *metaClass = context.metaClass(); - writeGetattroDefinition(s, metaClass); - s << "PyObject *tmp = PyObject_GenericGetAttr(self, name);\n" - << "if (tmp)\n"; - { - Indentation indent(s); - s << "return tmp;\n"; - } - s << "if (!PyErr_ExceptionMatches(PyExc_AttributeError))\n"; - { - Indentation indent(s); - s << "return nullptr;\n"; + if (f.function->isOperatorBool()) { + if (invert) + s << '!'; + s << '*' << CPP_SELF_VAR; + return; } - s << "PyErr_Clear();\n"; + if (invert != f.invert) + s << '!'; + s << CPP_SELF_VAR << "->" << f.function->name() << "()"; +} - // This generates the code which dispatches access to member functions - // and fields from the smart pointer to its pointee. - s << "// Try to find the 'name' attribute, by retrieving the PyObject for " - "the corresponding C++ object held by the smart pointer.\n" - << "if (auto rawObj = PyObject_CallMethod(self, " - << SMART_POINTER_GETTER << ", 0)) {\n"; - { - Indentation indent(s); - s << "if (auto attribute = PyObject_GetAttr(rawObj, name))\n"; - { - Indentation indent(s); - s << "tmp = attribute;\n"; - } - s << "Py_DECREF(rawObj);\n"; - } - s << "}\n" - << "if (!tmp) {\n"; - { - Indentation indent(s); - s << R"(PyTypeObject *tp = Py_TYPE(self); -PyErr_Format(PyExc_AttributeError, - "'%.50s' object has no attribute '%.400s'", - tp->tp_name, Shiboken::String::toCString(name)); -)"; - } - s << "}\n" - << "return tmp;\n" << outdent << "}\n\n"; +void CppGenerator::writeNbBoolFunction(const GeneratorContext &context, + const BoolCastFunction &f, + TextStream &s) +{ + s << "static int " << cpythonBaseName(context.metaClass()) << "___nb_bool(PyObject *self)\n" + << "{\n" << indent; + writeCppSelfDefinition(s, context, ErrorReturn::MinusOne); + + const bool allowThread = f.function->allowThread(); + if (allowThread) + s << "int result;\n" << BEGIN_ALLOW_THREADS << "\nresult = "; + else + s << "return "; + + writeNbBoolExpression(s, f); + s << " ? 1 : 0;\n"; + + if (allowThread) + s << END_ALLOW_THREADS << "\nreturn result;\n"; + s << outdent << "}\n\n"; } // Write declaration and invocation of the init function for the module init // function. -void CppGenerator::writeInitFunc(TextStream &declStr, TextStream &callStr, - const QString &initFunctionName, - const TypeEntry *enclosingEntry) -{ - const 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); const auto functionGroups = getGlobalFunctionGroups(); for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) { - AbstractMetaFunctionCList overloads; - for (const auto &func : it.value()) { - if (!func->isModifiedRemoved()) { - overloads.append(func); - if (func->typeEntry()) - includes << func->typeEntry()->include(); - } + const AbstractMetaFunctionCList &overloads = it.value(); + for (const auto &func : overloads) { + if (auto te = func->typeEntry()) + includes.insert(te->include()); } if (overloads.isEmpty()) @@ -6034,39 +6127,74 @@ bool CppGenerator::finishGeneration() // Dummy context to satisfy the API. GeneratorContext classContext; - writeMethodWrapper(s_globalFunctionImpl, overloads, classContext); - writeSignatureInfo(signatureStream, overloads); - writeMethodDefinition(s_globalFunctionDef, overloads); + OverloadData overloadData(overloads, api()); + + writeMethodWrapper(s_globalFunctionImpl, overloadData, classContext); + writeSignatureInfo(signatureStream, overloadData); + s_globalFunctionDef << methodDefinitionEntries(overloadData); } AbstractMetaClassCList classesWithStaticFields; - for (auto cls : api().classes()){ - if (shouldGenerate(cls)) { - writeInitFunc(s_classInitDecl, s_classPythonDefines, - getSimpleClassInitFunctionName(cls), - cls->typeEntry()->targetLangEnclosingEntry()); + for (const auto &cls : api().classes()){ + auto te = cls->typeEntry(); + if (shouldGenerate(te)) { + const bool hasConfigCondition = te->hasConfigCondition(); + if (hasConfigCondition) { + s_classInitDecl << te->configCondition() << '\n'; + s_classPythonDefines << te->configCondition() << '\n'; + } + const QString initFunc = initFuncPrefix + getSimpleClassInitFunctionName(cls); + writeInitFuncDecl(s_classInitDecl, initFunc); + writeInitFuncCall(s_classPythonDefines, initFunc, + targetLangEnclosingEntry(te), cls->name()); if (cls->hasStaticFields()) { - s_classInitDecl << "void " - << getSimpleClassStaticFieldsInitFunctionName(cls) << "();\n"; + s_classInitDecl << "PyTypeObject *" + << getSimpleClassStaticFieldsInitFunctionName(cls) << "(PyObject *module);\n"; classesWithStaticFields.append(cls); } + if (hasConfigCondition) { + s_classInitDecl << "#endif\n"; + s_classPythonDefines << "#endif\n"; + } } } // Initialize smart pointer types. - const auto &smartPtrs = instantiatedSmartPointers(); - for (const AbstractMetaType &metaType : smartPtrs) { - GeneratorContext context = contextForSmartPointer(nullptr, metaType); - writeInitFunc(s_classInitDecl, s_classPythonDefines, - getInitFunctionName(context), - metaType.typeEntry()->targetLangEnclosingEntry()); + for (const auto &smp : api().instantiatedSmartPointers()) { + GeneratorContext context = contextForSmartPointer(smp.specialized, smp.type); + const auto enclosingClass = context.metaClass()->enclosingClass(); + auto enclosingTypeEntry = targetLangEnclosingEntry(smp.specialized->typeEntry()); + + const QString initFunc = initFuncPrefix + getInitFunctionName(context); + writeInitFuncDecl(s_classInitDecl, initFunc); + writeInitFuncCall(s_classPythonDefines, + initFunc, enclosingTypeEntry, smp.specialized->name()); + includes.insert(smp.type.instantiations().constFirst().typeEntry()->include()); + } + + for (auto &instantiatedContainer : api().instantiatedContainers()) { + includes.insert(instantiatedContainer.typeEntry()->include()); + for (const auto &inst : instantiatedContainer.instantiations()) + includes.insert(inst.typeEntry()->include()); } - QString moduleFileName(outputDirectory() + QLatin1Char('/') + subDirectoryForPackage(packageName())); - moduleFileName += QLatin1Char('/') + moduleName().toLower() + QLatin1String("_module_wrapper.cpp"); + const ExtendedConverterData extendedConverters = getExtendedConverters(); + for (auto it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) { + TypeEntryCPtr te = it.key(); + includes.insert(te->include()); + for (const auto &metaClass : it.value()) + includes.insert(metaClass->typeEntry()->include()); + } + const QList<CustomConversionPtr> &typeConversions = getPrimitiveCustomConversions(); + for (const auto &c : typeConversions) { + if (auto te = c->ownerType()) + includes.insert(te->include()); + } + + QString moduleFileName(outputDirectory() + u'/' + subDirectoryForPackage(packageName())); + moduleFileName += u'/' + moduleName().toLower() + u"_module_wrapper.cpp"_s; - verifyDirectoryFor(moduleFileName); FileOut file(moduleFileName); TextStream &s = file.stream; @@ -6079,60 +6207,78 @@ bool CppGenerator::finishGeneration() #include <algorithm> #include <signature.h> )"; + + if (!api().instantiatedContainers().isEmpty()) + s << "#include <sbkcontainer.h>\n#include <sbkstaticstrings.h>\n"; + if (usePySideExtensions()) { s << includeQDebug; - s << R"(#include <pyside.h> + s << R"(#include <pysidecleanup.h> #include <pysideqenum.h> #include <feature_select.h> +#include <pysidestaticstrings.h> )"; } s << "#include \"" << getModuleHeaderFileName() << '"' << "\n\n"; - for (const Include &include : qAsConst(includes)) + for (const Include &include : includes) s << include; s << '\n'; // Global enums AbstractMetaEnumList globalEnums = api().globalEnums(); - for (const AbstractMetaClass *nsp : invisibleTopNamespaces()) + for (const auto &nsp : invisibleTopNamespaces()) { + const auto oldSize = globalEnums.size(); nsp->getEnumsToBeGenerated(&globalEnums); + if (globalEnums.size() > oldSize) + s << nsp->typeEntry()->include(); + } TypeDatabase *typeDb = TypeDatabase::instance(); - const TypeSystemTypeEntry *moduleEntry = typeDb->defaultTypeSystemType(); + TypeSystemTypeEntryCPtr moduleEntry = typeDb->defaultTypeSystemType(); Q_ASSERT(moduleEntry); - //Extra includes - s << '\n' << "// Extra includes\n"; + s << '\n'; + // Extra includes QList<Include> extraIncludes = moduleEntry->extraIncludes(); - for (const AbstractMetaEnum &cppEnum : qAsConst(globalEnums)) + for (const AbstractMetaEnum &cppEnum : std::as_const(globalEnums)) extraIncludes.append(cppEnum.typeEntry()->extraIncludes()); - std::sort(extraIncludes.begin(), extraIncludes.end()); - for (const Include &inc : qAsConst(extraIncludes)) - s << inc; - s << '\n' - << "// Current module's type array.\n" - << "PyTypeObject **" << cppApiVariableName() << " = nullptr;\n" + if (!extraIncludes.isEmpty()) { + s << "// Extra includes\n"; + std::sort(extraIncludes.begin(), extraIncludes.end()); + for (const Include &inc : std::as_const(extraIncludes)) + s << inc; + s << '\n'; + } + + // FIXME PYSIDE-7: Remove backwards compatible structure + s << "// Current module's type array.\n" + << "Shiboken::Module::TypeInitStruct *" << cppApiVariableName() << " = nullptr;\n" + << "// Backwards compatible structure with identical indexing.\n" + << "PyTypeObject **" << cppApiVariableNameOld() << " = nullptr;\n" << "// Current module's PyObject pointer.\n" << "PyObject *" << pythonModuleObjectName() << " = nullptr;\n" << "// Current module's converter array.\n" - << "SbkConverter **" << convertersVariableName() << " = nullptr;\n"; + << "SbkConverter **" << convertersVariableName() << " = nullptr;\n\n"; const CodeSnipList snips = moduleEntry->codeSnips(); // module inject-code native/beginning - if (!snips.isEmpty()) - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode); + writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode); // cleanup staticMetaObject attribute if (usePySideExtensions()) { + QString iType = cppApiVariableName() + "[i].type"_L1; + QString iName = cppApiVariableName() + "[i].fullName"_L1; + s << "void cleanTypesAttributes() {\n" << indent - << "for (int i = 0, imax = SBK_" << moduleName() - << "_IDX_COUNT; i < imax; i++) {\n" << indent - << "PyObject *pyType = reinterpret_cast<PyObject *>(" << cppApiVariableName() << "[i]);\n" - << "Shiboken::AutoDecRef attrName(Py_BuildValue(\"s\", \"staticMetaObject\"));\n" - << "if (pyType && PyObject_HasAttr(pyType, attrName))\n" << indent + << "static PyObject *attrName = Shiboken::PyName::qtStaticMetaObject();\n" + << "const int imax = SBK_" << moduleName() << "_IDX_COUNT;\n" + << "for (int i = 0; i < imax && " << iName << " != nullptr; ++i) {\n" << indent + << "auto *pyType = reinterpret_cast<PyObject *>(" << iType << ");\n" + << "if (pyType != nullptr && PyObject_HasAttr(pyType, attrName))\n" << indent << "PyObject_SetAttr(pyType, attrName, Py_None);\n" << outdent - << outdent << "}\n" << outdent << "}\n"; + << outdent << "}\n" << outdent << "}\n\n"; } s << "// Global functions " @@ -6140,7 +6286,7 @@ bool CppGenerator::finishGeneration() << s_globalFunctionImpl.toString() << '\n' << "static PyMethodDef " << moduleName() << "_methods[] = {\n" << indent << s_globalFunctionDef.toString() - << "{0} // Sentinel\n" << outdent << "};\n\n" + << METHOD_DEF_SENTINEL << outdent << "};\n\n" << "// Classes initialization functions " << "------------------------------------------------------------\n" << s_classInitDecl.toString() << '\n'; @@ -6150,12 +6296,8 @@ bool CppGenerator::finishGeneration() s << "// Enum definitions " << "------------------------------------------------------------\n"; - for (const AbstractMetaEnum &cppEnum : qAsConst(globalEnums)) { - if (cppEnum.isAnonymous() || cppEnum.isPrivate()) - continue; + for (const AbstractMetaEnum &cppEnum : std::as_const(globalEnums)) writeEnumConverterFunctions(s, cppEnum); - s << '\n'; - } if (convImpl.size() > 0) { s << "// Enum converters " @@ -6165,7 +6307,6 @@ bool CppGenerator::finishGeneration() << "} // namespace Shiboken\n\n"; } - writeFlagsNumberMethodsDefinitions(s, globalEnums); s << '\n'; } @@ -6173,31 +6314,29 @@ bool CppGenerator::finishGeneration() if (!requiredModules.isEmpty()) s << "// Required modules' type and converter arrays.\n"; for (const QString &requiredModule : requiredModules) { - s << "PyTypeObject **" << cppApiVariableName(requiredModule) << ";\n" + s << "Shiboken::Module::TypeInitStruct *" << cppApiVariableName(requiredModule) << ";\n" << "SbkConverter **" << convertersVariableName(requiredModule) << ";\n"; } s << "\n// Module initialization " << "------------------------------------------------------------\n"; - ExtendedConverterData extendedConverters = getExtendedConverters(); if (!extendedConverters.isEmpty()) { s << '\n' << "// Extended Converters.\n\n"; for (ExtendedConverterData::const_iterator it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) { - const TypeEntry *externalType = it.key(); + TypeEntryCPtr externalType = it.key(); s << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName() << '.' << '\n'; - for (const AbstractMetaClass *sourceClass : it.value()) { - AbstractMetaType sourceType = buildAbstractMetaTypeFromAbstractMetaClass(sourceClass); - AbstractMetaType targetType = buildAbstractMetaTypeFromTypeEntry(externalType); + for (const auto &sourceClass : it.value()) { + AbstractMetaType sourceType = AbstractMetaType::fromAbstractMetaClass(sourceClass); + AbstractMetaType targetType = AbstractMetaType::fromTypeEntry(externalType); writePythonToCppConversionFunctions(s, sourceType, targetType); } } } - const QList<const CustomConversion *> &typeConversions = getPrimitiveCustomConversions(); if (!typeConversions.isEmpty()) { s << "\n// Primitive Type converters.\n\n"; - for (const CustomConversion *conversion : typeConversions) { + for (const auto &conversion : typeConversions) { s << "// C++ to Python conversion for primitive type '" << conversion->ownerType()->qualifiedCppName() << "'.\n"; writeCppToPythonFunction(s, conversion); writeCustomConverterFunctions(s, conversion); @@ -6205,23 +6344,20 @@ bool CppGenerator::finishGeneration() s << '\n'; } - const auto &containers = instantiatedContainers(); + QHash<AbstractMetaType, OpaqueContainerData> opaqueContainers; + const auto &containers = api().instantiatedContainers(); + QSet<AbstractMetaType> valueConverters; if (!containers.isEmpty()) { s << "// Container Type converters.\n\n"; for (const AbstractMetaType &container : containers) { - s << "// C++ to Python conversion for container type '" << container.cppSignature() << "'.\n"; + s << "// C++ to Python conversion for container type '" + << container.cppSignature() << "'.\n"; writeContainerConverterFunctions(s, container); - } - s << '\n'; - } - - // Implicit smart pointers conversions - const auto smartPointersList = instantiatedSmartPointers(); - if (!smartPointersList.isEmpty()) { - s << "// SmartPointers converters.\n\n"; - for (const AbstractMetaType &smartPointer : smartPointersList) { - s << "// C++ to Python conversion for smart pointer type '" << smartPointer.cppSignature() << "'.\n"; - writeSmartPointerConverterFunctions(s, smartPointer); + if (container.generateOpaqueContainer()) { + auto data = writeOpaqueContainerConverterFunctions(s, container, + &valueConverters); + opaqueContainers.insert(container, data); + } } s << '\n'; } @@ -6240,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_" @@ -6248,10 +6386,9 @@ bool CppGenerator::finishGeneration() s << "if (" << globalModuleVar << " != nullptr)\n" << indent << "return " << globalModuleVar << ";\n" << outdent; - ErrorCode errorCode(QLatin1String("nullptr")); // module inject-code target/beginning - if (!snips.isEmpty()) - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode); + writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, + TypeSystem::TargetLangCode); for (const QString &requiredModule : requiredModules) { s << "{\n" << indent @@ -6265,11 +6402,32 @@ bool CppGenerator::finishGeneration() << "}\n\n"; } - int maxTypeIndex = getMaxTypeIndex() + instantiatedSmartPointers().size(); + int maxTypeIndex = getMaxTypeIndex() + api().instantiatedSmartPointers().size(); if (maxTypeIndex) { - s << "// Create an array of wrapper types for the current module.\n" - << "static PyTypeObject *cppApi[SBK_" << moduleName() << "_IDX_COUNT];\n" - << cppApiVariableName() << " = cppApi;\n\n"; + s << "// Create an array of wrapper types/names for the current module.\n" + << "static Shiboken::Module::TypeInitStruct cppApi[] = {\n" << indent; + + // Windows did not like an array of QString. + QStringList typeNames; + for (int idx = 0; idx < maxTypeIndex; ++idx) + typeNames.append("+++ unknown entry #"_L1 + QString::number(idx) + + " in "_L1 + moduleName()); + + collectFullTypeNamesArray(typeNames); + + for (auto typeName : typeNames) + s << "{nullptr, \"" << typeName << "\"},\n"; + + s << "{nullptr, nullptr}\n" << outdent << "};\n" + << "// The new global structure consisting of (type, name) pairs.\n" + << cppApiVariableName() << " = cppApi;\n"; + 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" @@ -6279,13 +6437,18 @@ bool CppGenerator::finishGeneration() << "PyObject *module = Shiboken::Module::create(\"" << moduleName() << "\", &moduledef);\n\n" << "// Make module available from global scope\n" - << globalModuleVar << " = module;\n\n" - << "// Initialize classes in the type system\n" + << globalModuleVar << " = module;\n\n"; + + const QString subModuleOf = typeDb->defaultTypeSystemType()->subModuleOf(); + if (!subModuleOf.isEmpty()) + writeSubModuleHandling(s, moduleName(), subModuleOf); + + s << "// Initialize classes in the type system\n" << s_classPythonDefines.toString(); if (!typeConversions.isEmpty()) { s << '\n'; - for (const CustomConversion *conversion : typeConversions) { + for (const auto &conversion : typeConversions) { writePrimitiveConverterInitialization(s, conversion); s << '\n'; } @@ -6294,17 +6457,23 @@ bool CppGenerator::finishGeneration() if (!containers.isEmpty()) { s << '\n'; for (const AbstractMetaType &container : containers) { - writeContainerConverterInitialization(s, container); + const QString converterObj = writeContainerConverterInitialization(s, container, api()); + const auto it = opaqueContainers.constFind(container); + if (it != opaqueContainers.constEnd()) { + writeSetPythonToCppPointerConversion(s, converterObj, + it.value().pythonToConverterFunctionName, + it.value().converterCheckFunctionName); + } s << '\n'; } } - if (!smartPointersList.isEmpty()) { + if (!opaqueContainers.isEmpty()) { + s << "\n// Opaque container type registration\n" + << "PyObject *ob_type{};\n"; + for (const auto &d : opaqueContainers) + s << d.registrationCode; s << '\n'; - for (const AbstractMetaType &smartPointer : smartPointersList) { - writeSmartPointerConverterInitialization(s, smartPointer); - s << '\n'; - } } if (!extendedConverters.isEmpty()) { @@ -6318,21 +6487,14 @@ bool CppGenerator::finishGeneration() writeEnumsInitialization(s, globalEnums); s << "// Register primitive types converters.\n"; - const PrimitiveTypeEntryList &primitiveTypeList = primitiveTypes(); - for (const PrimitiveTypeEntry *pte : primitiveTypeList) { - if (!pte->generateCode() || !pte->isCppPrimitive()) + const PrimitiveTypeEntryCList &primitiveTypeList = primitiveTypes(); + for (const auto &pte : primitiveTypeList) { + if (!pte->generateCode() || !isCppPrimitive(pte)) continue; - const TypeEntry *referencedType = pte->basicReferencedTypeEntry(); - if (!referencedType) + if (!pte->referencesType()) continue; - QString converter = converterObject(referencedType); - QStringList cppSignature = pte->qualifiedCppName().split(QLatin1String("::"), Qt::SkipEmptyParts); - while (!cppSignature.isEmpty()) { - QString signature = cppSignature.join(QLatin1String("::")); - s << "Shiboken::Conversions::registerConverterName(" - << converter << ", \"" << signature << "\");\n"; - cppSignature.removeFirst(); - } + TypeEntryCPtr referencedType = basicReferencedTypeEntry(pte); + registerConverterInScopes(s, pte->qualifiedCppName(), converterObject(referencedType)); } s << '\n'; @@ -6344,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 : qAsConst(classesWithStaticFields)) - s << getSimpleClassStaticFieldsInitFunctionName(cls) << "();\n"; + for (const auto &cls : std::as_const(classesWithStaticFields)) { + ConfigurableScope configScope(s, cls->typeEntry()); + s << getSimpleClassStaticFieldsInitFunctionName(cls) << "(module);\n"; + } } - s << "\nif (PyErr_Occurred()) {\n" << indent + s << '\n' << initInheritanceFunction << "();\n" + << "\nif (" << shibokenErrorsOccurred << ") {\n" << indent << "PyErr_Print();\n" << "Py_FatalError(\"can't initialize module " << moduleName() << "\");\n" << outdent << "}\n"; // module inject-code target/end - if (!snips.isEmpty()) - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode); + writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode); // module inject-code native/end - if (!snips.isEmpty()) - writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode); + writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode); if (usePySideExtensions()) { - for (const AbstractMetaEnum &metaEnum : qAsConst(globalEnums)) + for (const AbstractMetaEnum &metaEnum : std::as_const(globalEnums)) if (!metaEnum.isAnonymous()) { - s << "qRegisterMetaType< ::" << metaEnum.typeEntry()->qualifiedCppName() + ConfigurableScope configScope(s, metaEnum.typeEntry()); + s << "qRegisterMetaType< " << getFullTypeName(metaEnum.typeEntry()) << " >(\"" << metaEnum.name() << "\");\n"; } @@ -6372,19 +6536,11 @@ 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"; - // Temporary hack to allow that the same module can be used both as - // `Shiboken` and `shiboken6`; this will go away in 6.1. - if (moduleName() == QLatin1String("Shiboken")) { - s << "\n// This function should be removed in version 6.2.\n" - << "extern \"C\" LIBSHIBOKEN_EXPORT PyObject *PyInit_shiboken6()\n{\n" << indent - << "return PyInit_Shiboken();\n" << outdent - << "}\n"; - } file.done(); return true; } @@ -6397,16 +6553,35 @@ static ArgumentOwner getArgumentOwner(const AbstractMetaFunctionCPtr &func, int return argOwner; } +// Whether to enable parent ownership heuristic for a function and its argument. +// Both must belong to the same class hierarchy and have the same +// type entry enabling parent management. +static bool useParentHeuristics(const ApiExtractorResult &api, + const AbstractMetaFunctionCPtr &func, + const AbstractMetaType &argType) +{ + if (!ComplexTypeEntry::isParentManagementEnabled()) // FIXME PYSIDE 7: Remove this + return true; + const auto owner = func->ownerClass(); + if (!owner) + return false; + auto ownerEntry = parentManagementEntry(owner); + if (!ownerEntry) + return false; + auto argTypeEntry = argType.typeEntry(); + if (!argTypeEntry->isComplex()) + return false; + const auto argClass = AbstractMetaClass::findClass(api.classes(), argTypeEntry); + return argClass && parentManagementEntry(argClass) == ownerEntry; +} + bool CppGenerator::writeParentChildManagement(TextStream &s, const AbstractMetaFunctionCPtr &func, - int argIndex, bool useHeuristicPolicy) const + int argIndex, + bool usePyArgs, bool useHeuristicPolicy) const { - const int numArgs = func->arguments().count(); + const int numArgs = func->arguments().size(); bool ctorHeuristicEnabled = func->isConstructor() && useCtorHeuristic() && useHeuristicPolicy; - - const auto &groups = func->implementingClass() - ? getFunctionGroups(func->implementingClass()) - : getGlobalFunctionGroups(); - bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(OverloadData(groups[func->name()], api())); + bool heuristicTriggered = false; ArgumentOwner argOwner = getArgumentOwner(func, argIndex); ArgumentOwner::Action action = argOwner.action; @@ -6414,10 +6589,12 @@ bool CppGenerator::writeParentChildManagement(TextStream &s, const AbstractMetaF int childIndex = argIndex; if (ctorHeuristicEnabled && argIndex > 0 && argIndex <= numArgs) { const AbstractMetaArgument &arg = func->arguments().at(argIndex-1); - if (arg.name() == QLatin1String("parent") && arg.type().isObjectType()) { + if (arg.name() == u"parent" && arg.type().isObjectType() + && useParentHeuristics(api(), func, arg.type())) { action = ArgumentOwner::Add; parentIndex = argIndex; childIndex = -1; + heuristicTriggered = true; } } @@ -6429,28 +6606,32 @@ bool CppGenerator::writeParentChildManagement(TextStream &s, const AbstractMetaF << "Argument index for parent tag out of bounds: " << func->signature(); if (action == ArgumentOwner::Remove) { - parentVariable = QLatin1String("Py_None"); + parentVariable = u"Py_None"_s; } else { if (parentIndex == 0) { - parentVariable = QLatin1String(PYTHON_RETURN_VAR); + parentVariable = PYTHON_RETURN_VAR; } else if (parentIndex == -1) { - parentVariable = QLatin1String("self"); + parentVariable = PYTHON_SELF_VAR; } else { parentVariable = usePyArgs - ? pythonArgsAt(parentIndex - 1) : QLatin1String(PYTHON_ARG); + ? pythonArgsAt(parentIndex - 1) : PYTHON_ARG; } } if (childIndex == 0) { - childVariable = QLatin1String(PYTHON_RETURN_VAR); + childVariable = PYTHON_RETURN_VAR; } else if (childIndex == -1) { - childVariable = QLatin1String("self"); + childVariable = PYTHON_SELF_VAR; } else { childVariable = usePyArgs - ? pythonArgsAt(childIndex - 1) : QLatin1String(PYTHON_ARG); + ? pythonArgsAt(childIndex - 1) : PYTHON_ARG; } - s << "Shiboken::Object::setParent(" << parentVariable << ", " << childVariable << ");\n"; + s << "// Ownership transferences"; + if (heuristicTriggered) + s << " (constructor heuristics)"; + s << ".\nShiboken::Object::setParent(" << parentVariable << ", " + << childVariable << ");\n"; return true; } @@ -6458,15 +6639,16 @@ bool CppGenerator::writeParentChildManagement(TextStream &s, const AbstractMetaF } void CppGenerator::writeParentChildManagement(TextStream &s, const AbstractMetaFunctionCPtr &func, + bool usesPyArgs, bool useHeuristicForReturn) const { - const int numArgs = func->arguments().count(); + const int numArgs = func->arguments().size(); // -1 = return value // 0 = self // 1..n = func. args. for (int i = -1; i <= numArgs; ++i) - writeParentChildManagement(s, func, i, useHeuristicForReturn); + writeParentChildManagement(s, func, i, usesPyArgs, useHeuristicForReturn); if (useHeuristicForReturn) writeReturnValueHeuristics(s, func); @@ -6480,27 +6662,40 @@ void CppGenerator::writeReturnValueHeuristics(TextStream &s, const AbstractMetaF || type.isVoid() || func->isStatic() || func->isConstructor() - || !func->typeReplaced(0).isEmpty()) { + || func->isTypeModified() + || !useParentHeuristics(api(), func, type) + // Something like parent(), parentWidget(): No child relationship here. + || (func->maybeAccessor() && func->name().startsWith(u"parent"))) { return; } ArgumentOwner argOwner = getArgumentOwner(func, ArgumentOwner::ReturnIndex); if (argOwner.action == ArgumentOwner::Invalid || argOwner.index != ArgumentOwner::ThisIndex) { - if (type.isPointerToWrapperType()) - s << "Shiboken::Object::setParent(self, " << PYTHON_RETURN_VAR << ");\n"; + if (type.isPointerToWrapperType()) { + s << "// Ownership transferences (return value heuristics).\n" + << "Shiboken::Object::setParent(self, " << PYTHON_RETURN_VAR << ");\n"; + } } } -void CppGenerator::writeHashFunction(TextStream &s, const GeneratorContext &context) const +void CppGenerator::writeHashFunction(TextStream &s, const GeneratorContext &context) { - const AbstractMetaClass *metaClass = context.metaClass(); + const auto metaClass = context.metaClass(); const char hashType[] = "Py_hash_t"; s << "static " << hashType << ' ' << cpythonBaseName(metaClass) - << "_HashFunc(PyObject *self) {\n" << indent; + << "_HashFunc(PyObject *self)\n{\n" << indent; writeCppSelfDefinition(s, context); - s << "return " << hashType << '(' - << metaClass->typeEntry()->hashFunction() << '('; - if (!metaClass->isObjectType()) + + bool deref = true; + QString name = metaClass->typeEntry()->hashFunction(); + if (name.isEmpty()) + name = metaClass->hashFunction(); + else + deref = !metaClass->isObjectType(); + Q_ASSERT(!name.isEmpty()); + + s << "return " << hashType << '(' << name << '('; + if (deref) s << '*'; s << CPP_SELF_VAR << "));\n" << outdent << "}\n\n"; @@ -6509,21 +6704,22 @@ void CppGenerator::writeHashFunction(TextStream &s, const GeneratorContext &cont void CppGenerator::writeDefaultSequenceMethods(TextStream &s, const GeneratorContext &context) const { - const AbstractMetaClass *metaClass = context.metaClass(); - ErrorCode errorCode(0); + const auto metaClass = context.metaClass(); + ErrorReturn errorReturn = ErrorReturn::Zero; // __len__ - s << "Py_ssize_t " << cpythonBaseName(metaClass->typeEntry()) + const QString namePrefix = cpythonBaseName(metaClass->typeEntry()); + s << "Py_ssize_t " << namePrefix << "__len__(PyObject *self)\n{\n" << indent; - writeCppSelfDefinition(s, context); + writeCppSelfDefinition(s, context, errorReturn); s << "return " << CPP_SELF_VAR << "->size();\n" << outdent << "}\n"; // __getitem__ - s << "PyObject *" << cpythonBaseName(metaClass->typeEntry()) + s << "PyObject *" << namePrefix << "__getitem__(PyObject *self, Py_ssize_t _i)\n{\n" << indent; - writeCppSelfDefinition(s, context); - writeIndexError(s, QLatin1String("index out of bounds")); + writeCppSelfDefinition(s, context, errorReturn); + writeIndexError(s, u"index out of bounds"_s, errorReturn); s << metaClass->qualifiedCppName() << "::const_iterator _item = " << CPP_SELF_VAR << "->begin();\n" @@ -6540,29 +6736,25 @@ void CppGenerator::writeDefaultSequenceMethods(TextStream &s, const AbstractMetaType &itemType = instantiations.constFirst(); s << "return "; - writeToPythonConversion(s, itemType, metaClass, QLatin1String("*_item")); + writeToPythonConversion(s, itemType, metaClass, u"*_item"_s); s << ";\n" << outdent << "}\n"; // __setitem__ - ErrorCode errorCode2(-1); - s << "int " << cpythonBaseName(metaClass->typeEntry()) + s << "int " << namePrefix << "__setitem__(PyObject *self, Py_ssize_t _i, PyObject *pyArg)\n{\n" << indent; - writeCppSelfDefinition(s, context); - writeIndexError(s, QLatin1String("list assignment index out of range")); + errorReturn = ErrorReturn::MinusOne; + writeCppSelfDefinition(s, context, errorReturn); + writeIndexError(s, u"list assignment index out of range"_s, errorReturn); - s << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ";\n" + s << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR << ";\n" << "if (!"; - writeTypeCheck(s, itemType, QLatin1String("pyArg"), isNumber(itemType.typeEntry())); - s << ") {\n"; - { - Indentation indent(s); - s << "PyErr_SetString(PyExc_TypeError, \"attributed value with wrong type, '" - << itemType.name() << "' or other convertible type expected\");\n" - << "return -1;\n"; - } - s << "}\n"; - writeArgumentConversion(s, itemType, QLatin1String("cppValue"), QLatin1String("pyArg"), metaClass); + writeTypeCheck(s, itemType, u"pyArg"_s, isNumber(itemType.typeEntry())); + s << ") {\n" << indent + << "Shiboken::Errors::setSequenceTypeError(\"" << itemType.name() << "\");\n" + << "return -1;\n" << outdent << "}\n"; + writeArgumentConversion(s, itemType, u"cppValue"_s, + u"pyArg"_s, errorReturn, metaClass); s << metaClass->qualifiedCppName() << "::iterator _item = " << CPP_SELF_VAR << "->begin();\n" @@ -6571,25 +6763,28 @@ void CppGenerator::writeDefaultSequenceMethods(TextStream &s, s << "return {};\n" << outdent << "}\n"; } -void CppGenerator::writeIndexError(TextStream &s, const QString &errorMsg) +void CppGenerator::writeIndexError(TextStream &s, const QString &errorMsg, + ErrorReturn errorReturn) { - s << "if (_i < 0 || _i >= (Py_ssize_t) " << CPP_SELF_VAR << "->size()) {\n"; - { - Indentation indent(s); - s << "PyErr_SetString(PyExc_IndexError, \"" << errorMsg << "\");\n" - << returnStatement(m_currentErrorCode) << '\n'; - } - s << "}\n"; + s << "if (_i < 0 || _i >= (Py_ssize_t) " << CPP_SELF_VAR << "->size()) {\n" + << indent << "PyErr_SetString(PyExc_IndexError, \"" << errorMsg << "\");\n" + << errorReturn << outdent << "}\n"; +} + +QString CppGenerator::writeReprFunctionHeader(TextStream &s, const GeneratorContext &context) +{ + QString funcName = cpythonBaseName(context.metaClass()) + REPR_FUNCTION; + s << "extern \"C\"\n{\n" + << "static PyObject *" << funcName << "(PyObject *self)\n{\n" << indent; + return funcName; } QString CppGenerator::writeReprFunction(TextStream &s, const GeneratorContext &context, - uint indirections) const + uint indirections) { - const AbstractMetaClass *metaClass = context.metaClass(); - QString funcName = cpythonBaseName(metaClass) + reprFunction(); - s << "extern \"C\"\n{\n" - << "static PyObject *" << funcName << "(PyObject *self)\n{\n" << indent; + const auto metaClass = context.metaClass(); + QString funcName = writeReprFunctionHeader(s, context); writeCppSelfDefinition(s, context); s << R"(QBuffer buffer; buffer.open(QBuffer::ReadWrite); @@ -6600,27 +6795,25 @@ dbg << )"; s << CPP_SELF_VAR << R"(; buffer.close(); QByteArray str = buffer.data(); -int idx = str.indexOf('('); +const auto idx = str.indexOf('('); +auto *typeName = Py_TYPE(self)->tp_name; if (idx >= 0) -)"; - { - Indentation indent(s); - s << "str.replace(0, idx, Py_TYPE(self)->tp_name);\n"; - } - s << "str = str.trimmed();\n" - << "PyObject *mod = PyDict_GetItem(Py_TYPE(self)->tp_dict, Shiboken::PyMagicName::module());\n"; +)" << indent << "str.replace(0, idx, typeName);\n" << outdent + << "str = str.trimmed();\n" + << "Shiboken::AutoDecRef tpDict(PepType_GetDict(Py_TYPE(self)));\n" + << "PyObject *mod = PyDict_GetItem(tpDict.object(), Shiboken::PyMagicName::module());\n"; // PYSIDE-595: The introduction of heap types has the side effect that the module name // is always prepended to the type name. Therefore the strchr check: - s << "if (mod && !strchr(str, '.'))\n"; - { - Indentation indent(s); - s << "return Shiboken::String::fromFormat(\"<%s.%s at %p>\", Shiboken::String::toCString(mod), str.constData(), self);\n"; - } - s << "else\n"; - { - Indentation indent(s); - s << "return Shiboken::String::fromFormat(\"<%s at %p>\", str.constData(), self);\n"; - } - s << outdent << "}\n} // extern C\n\n"; + s << "if (mod != nullptr && std::strchr(typeName, '.') == nullptr)\n" << indent + << "return Shiboken::String::fromFormat(\"<%s.%s at %p>\"," + " Shiboken::String::toCString(mod), str.constData(), self);\n" + << outdent + << "return Shiboken::String::fromFormat(\"<%s at %p>\", str.constData(), self);\n"; + writeReprFunctionFooter(s); return funcName; } + +void CppGenerator::writeReprFunctionFooter(TextStream &s) +{ + s << outdent << "}\n} // extern C\n\n"; +} diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.h b/sources/shiboken6/generator/shiboken/cppgenerator.h index 7795b4e11..5920c9a3a 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator.h +++ b/sources/shiboken6/generator/shiboken/cppgenerator.h @@ -1,36 +1,23 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef CPPGENERATOR_H #define CPPGENERATOR_H #include "shibokengenerator.h" -#include "abstractmetalang_enums.h" +#include "include.h" +#include "modifications_typedefs.h" + +#include <QtCore/QFlags> +#include <QtCore/QSet> +#include <QtCore/QHash> + +#include <memory> +#include <utility> + +class OverloadDataNode; +class OverloadDataRootNode; +struct PyMethodDefEntry; /** * The CppGenerator generate the implementations of C++ bindings classes. @@ -38,103 +25,200 @@ class CppGenerator : public ShibokenGenerator { public: + enum class ErrorReturn { + Default, // "{}" + Zero, + MinusOne, + Void + }; + + enum CppSelfDefinitionFlag { + HasStaticOverload = 0x1, + HasClassMethodOverload = 0x2, + CppSelfAsReference = 0x4 + }; + Q_DECLARE_FLAGS(CppSelfDefinitionFlags, CppSelfDefinitionFlag) + CppGenerator(); const char *name() const override { return "Source generator"; } protected: - QString fileNameSuffix() const override; 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: - static void writeInitFunc(TextStream &declStr, TextStream &callStr, - const QString &initFunctionName, - const TypeEntry *enclosingEntry = nullptr); + struct VirtualMethodReturn + { + QString statement; + bool needsReference = false; + }; + + + void generateSmartPointerClass(TextStream &s, const GeneratorContext &classContext); + void generateIncludes(TextStream &s, const GeneratorContext &classContext, + const IncludeGroupList &includes = {}, + const AbstractMetaClassCList &innerClasses = {}) const; + static void writeInitFuncCall(TextStream &callStr, + const QString &functionName, + const TypeEntryCPtr &enclosingEntry, + const QString &pythonName, bool lazy = true); static void writeCacheResetNative(TextStream &s, const GeneratorContext &classContext); void writeConstructorNative(TextStream &s, const GeneratorContext &classContext, const AbstractMetaFunctionCPtr &func) const; - void writeDestructorNative(TextStream &s, const GeneratorContext &classContext) const; + static void writeDestructorNative(TextStream &s, const GeneratorContext &classContext); QString getVirtualFunctionReturnTypeName(const AbstractMetaFunctionCPtr &func) const; - void writeVirtualMethodNative(TextStream &s, const AbstractMetaFunctionCPtr &func, + static std::pair<QString, QChar> virtualMethodNativeArg(const AbstractMetaFunctionCPtr &func, + const AbstractMetaArgument &arg); + static void writeVirtualMethodNativeVectorCallArgs(TextStream &s, + const AbstractMetaFunctionCPtr &func, + const AbstractMetaArgumentList &arguments, + const QList<int> &invalidateArgs); + static void writeVirtualMethodNativeArgs(TextStream &s, + const AbstractMetaFunctionCPtr &func, + const AbstractMetaArgumentList &arguments, + const QList<int> &invalidateArgs); + void writeVirtualMethodNative(TextStream &s, + const AbstractMetaFunctionCPtr &func, int cacheIndex) const; + void writeVirtualMethodPythonOverride(TextStream &s, + const AbstractMetaFunctionCPtr &func, + const CodeSnipList &snips, + const VirtualMethodReturn &returnStatement) const; + void writeUserAddedPythonOverride(TextStream &s, + const AbstractMetaFunctionCPtr &func) const; void writeVirtualMethodCppCall(TextStream &s, const AbstractMetaFunctionCPtr &func, - const QString &funcName, const CodeSnipList &snips, - const AbstractMetaArgument *lastArg, const TypeEntry *retType, - const QString &returnStatement) const; - static QString virtualMethodReturn(TextStream &s, const ApiExtractorResult &api, - const AbstractMetaFunctionCPtr &func, - const FunctionModificationList &functionModifications); + const QString &funcName, const QList<CodeSnip> &snips, + const AbstractMetaArgument *lastArg, const TypeEntryCPtr &retType, + const QString &returnStatement, bool hasGil) const; + + static VirtualMethodReturn virtualMethodReturn(const ApiExtractorResult &api, + const AbstractMetaFunctionCPtr &func, + const FunctionModificationList &functionModifications); void writeMetaObjectMethod(TextStream &s, const GeneratorContext &classContext) const; static void writeMetaCast(TextStream &s, const GeneratorContext &classContext); - void writeEnumConverterFunctions(TextStream &s, const TypeEntry *enumType) 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 CustomConversion *customConversion) const; - void writeConverterRegister(TextStream &s, const AbstractMetaClass *metaClass, + const CustomConversionPtr &customConversion) const; + void writeConverterRegister(TextStream &s, const AbstractMetaClassCPtr &metaClass, const GeneratorContext &classContext) const; - static void writeCustomConverterRegister(TextStream &s, const CustomConversion *customConversion, + static void writeCustomConverterRegister(TextStream &s, + const CustomConversionPtr &customConversion, const QString &converterVar); void writeContainerConverterFunctions(TextStream &s, const AbstractMetaType &containerType) const; + struct OpaqueContainerData + { + QString name; + QString checkFunctionName; + QString converterCheckFunctionName; + QString pythonToConverterFunctionName; + QString registrationCode; + }; + + OpaqueContainerData + writeOpaqueContainerConverterFunctions(TextStream &s, + const AbstractMetaType &containerType, + QSet<AbstractMetaType> *valueTypes) const; + void writeOpaqueContainerValueConverter(TextStream &s, + const AbstractMetaType &valueType) const; + void writeSmartPointerConverterFunctions(TextStream &s, const AbstractMetaType &smartPointerType) const; - void writeMethodWrapperPreamble(TextStream &s, OverloadData &overloadData, - const GeneratorContext &context) const; - void writeConstructorWrapper(TextStream &s, const AbstractMetaFunctionCList &overloads, + 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 AbstractMetaFunctionCList &overloads, + void writeMethodWrapper(TextStream &s, const OverloadData &overloadData, + const GeneratorContext &classContext) const; + void writeMethodWrapper(TextStream &s, TextStream &definitionStream, + TextStream &signatureStream, + const AbstractMetaFunctionCList &overloads, const GeneratorContext &classContext) const; - static void writeArgumentsInitializer(TextStream &s, OverloadData &overloadData) ; + static void writeArgumentsInitializer(TextStream &s, const OverloadData &overloadData, + ErrorReturn errorReturn = ErrorReturn::Default); static void writeCppSelfConversion(TextStream &s, const GeneratorContext &context, const QString &className, bool useWrapperClass); - void writeCppSelfDefinition(TextStream &s, - const AbstractMetaFunctionCPtr &func, - const GeneratorContext &context, - bool hasStaticOverload = false) const; - void writeCppSelfDefinition(TextStream &s, - const GeneratorContext &context, - bool hasStaticOverload = false, - bool cppSelfAsReference = false) const; - - static void writeErrorSection(TextStream &s, OverloadData &overloadData) ; - static void writeFunctionReturnErrorCheckSection(TextStream &s, bool hasReturnValue = true); + static void writeSmartPointerCppSelfConversion(TextStream &s, + const GeneratorContext &context); + + static void writeCppSelfVarDef(TextStream &s, CppSelfDefinitionFlags flags = {}); + static void writeSmartPointerCppSelfDefinition(TextStream &s, + const GeneratorContext &, + ErrorReturn errorReturn = ErrorReturn::Default, + CppSelfDefinitionFlags flags = {}); + static void writeCppSelfDefinition(TextStream &s, + const AbstractMetaFunctionCPtr &func, + const GeneratorContext &context, + ErrorReturn errorReturn = ErrorReturn::Default, + CppSelfDefinitionFlags flags = {}); + static void writeCppSelfDefinition(TextStream &s, + const GeneratorContext &context, + ErrorReturn errorReturn = ErrorReturn::Default, + CppSelfDefinitionFlags flags = {}); - /// Writes the check section for the validity of wrapped C++ objects. - static void writeInvalidPyObjectCheck(TextStream &s, const QString &pyObj); + static void writeErrorSection(TextStream &s, + const OverloadData &overloadData, + ErrorReturn errorReturn); - void writeTypeCheck(TextStream &s, AbstractMetaType argType, const QString &argumentName, - bool isNumber = false, const QString &customType = QString(), - bool rejectNull = false) const; - void writeTypeCheck(TextStream& s, const OverloadData *overloadData, - const QString &argumentName) const; + static QString returnErrorWrongArguments(const OverloadData &overloadData, + ErrorReturn errorReturn); - static void writeTypeDiscoveryFunction(TextStream &s, const AbstractMetaClass *metaClass); + static void writeFunctionReturnErrorCheckSection(TextStream &s, + ErrorReturn errorReturn, + bool hasReturnValue = true); - void writeSetattroDefinition(TextStream &s, const AbstractMetaClass *metaClass) const; + /// Writes the check section for the validity of wrapped C++ objects. + static void writeInvalidPyObjectCheck(TextStream &s, const QString &pyObj, + ErrorReturn errorReturn); + + static void writeTypeCheck(TextStream &s, const AbstractMetaType &argType, + const QString &argumentName, + bool isNumber = false, bool rejectNull = false); + static void writeTypeCheck(TextStream &s, const QString &customType, + const QString &argumentName); + static void writeTypeCheck(TextStream& s, const std::shared_ptr<OverloadDataNode> &overloadData, + const QString &argumentName); + + static void replacePolymorphicIdPlaceHolders(const AbstractMetaClassCPtr &metaClass, + QString *id); + static void writeTypeDiscoveryFunction(TextStream &s, + const AbstractMetaClassCPtr &metaClass); + + static void writeSetattroDefinition(TextStream &s, const AbstractMetaClassCPtr &metaClass); static void writeSetattroDefaultReturn(TextStream &s); - 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 writeSmartPointerGetattroFunction(TextStream &s, const GeneratorContext &context); + static void writeGetattroDefinition(TextStream &s, const AbstractMetaClassCPtr &metaClass); + static void writeSmartPointerGetattroFunction(TextStream &s, + const GeneratorContext &context, + const BoolCastFunctionOptional &boolCast); void writeGetattroFunction(TextStream &s, AttroCheck attroCheck, const GeneratorContext &context) const; QString qObjectGetAttroFunction() const; + static void writeNbBoolFunction(const GeneratorContext &context, + const BoolCastFunction &f, + TextStream &s); + static void writeNbBoolExpression(TextStream &s, const BoolCastFunction &f, bool invert = false); + /** * Writes Python to C++ conversions for arguments on Python wrappers. * If implicit conversions, and thus new object allocation, are needed, @@ -147,36 +231,37 @@ private: * \param defaultValue an optional default value to be used instead of the conversion result * \param castArgumentAsUnused if true the converted argument is cast as unused to avoid compiler warnings */ - void writeArgumentConversion(TextStream &s, const AbstractMetaType &argType, - const QString &argName, const QString &pyArgName, - const AbstractMetaClass *context = nullptr, - const QString &defaultValue = QString(), - bool castArgumentAsUnused = false) const; + qsizetype writeArgumentConversion(TextStream &s, const AbstractMetaType &argType, + const QString &argName, const QString &pyArgName, + ErrorReturn errorReturn, + const AbstractMetaClassCPtr &context = {}, + const QString &defaultValue = QString(), + bool castArgumentAsUnused = false) const; /** * Returns the AbstractMetaType for a function argument. * If the argument type was modified in the type system, this method will * try to build a new type based on the type name defined in the type system. * \param func The function which owns the argument. - * \param argPos Argument position in the function signature. - * Note that the position 0 represents the return value, and the function - * parameters start counting on 1. - * \param newType It is set to true if the type returned is a new object that must be deallocated. - * \return The type of the argument indicated by \p argPos. + * \param index Argument index in the function signature. + * \return The type of the argument indicated by \p index. */ - static std::optional<AbstractMetaType> - getArgumentType(const AbstractMetaFunctionCPtr &func, int argPos); + static AbstractMetaType + getArgumentType(const AbstractMetaFunctionCPtr &func, int index); - void writePythonToCppTypeConversion(TextStream &s, + /// Writes the Python to C++ Conversion for function arguments and return + /// values of virtual methods for wrappers. + /// \return The number of indirections in case of return types + qsizetype writePythonToCppTypeConversion(TextStream &s, const AbstractMetaType &type, const QString &pyIn, const QString &cppOut, - const AbstractMetaClass *context = nullptr, - const QString &defaultValue = QString()) const; + const AbstractMetaClassCPtr &context = {}, + const QString &defaultValue = {}) const; /// Writes the conversion rule for arguments of regular and virtual methods. void writeConversionRule(TextStream &s, const AbstractMetaFunctionCPtr &func, - TypeSystem::Language language) const; + TypeSystem::Language language, bool usesPyArgs) const; /// Writes the conversion rule for the return value of a method. void writeConversionRule(TextStream &s, const AbstractMetaFunctionCPtr &func, TypeSystem::Language language, const QString &outputVar) const; @@ -199,21 +284,25 @@ 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 *parentOverloadData) const; + const OverloadData &overloadData, + const OverloadDataRootNode *node) const; /// Writes calls to all the possible method/function overloads. void writeFunctionCalls(TextStream &s, const OverloadData &overloadData, - const GeneratorContext &context) const; + const GeneratorContext &context, + ErrorReturn errorReturn) const; /// Writes the call to a single function usually from a collection of overloads. void writeSingleFunctionCall(TextStream &s, const OverloadData &overloadData, const AbstractMetaFunctionCPtr &func, - const GeneratorContext &context) const; + const GeneratorContext &context, + ErrorReturn errorReturn) const; /// Returns the name of a C++ to Python conversion function. static QString cppToPythonFunctionName(const QString &sourceTypeName, QString targetTypeName = QString()); @@ -221,18 +310,22 @@ private: /// Returns the name of a Python to C++ conversion function. static QString pythonToCppFunctionName(const QString &sourceTypeName, const QString &targetTypeName); static QString pythonToCppFunctionName(const AbstractMetaType &sourceType, const AbstractMetaType &targetType); - static QString pythonToCppFunctionName(const CustomConversion::TargetToNativeConversion *toNative, const TypeEntry *targetType); + static QString pythonToCppFunctionName(const TargetToNativeConversion &toNative, + const TypeEntryCPtr &targetType); /// Returns the name of a Python to C++ convertible check function. static QString convertibleToCppFunctionName(const QString &sourceTypeName, const QString &targetTypeName); static QString convertibleToCppFunctionName(const AbstractMetaType &sourceType, const AbstractMetaType &targetType); - static QString convertibleToCppFunctionName(const CustomConversion::TargetToNativeConversion *toNative, const TypeEntry *targetType); + static QString convertibleToCppFunctionName(const TargetToNativeConversion &toNative, + const TypeEntryCPtr &targetType); /// Writes a C++ to Python conversion function. void writeCppToPythonFunction(TextStream &s, const QString &code, const QString &sourceTypeName, QString targetTypeName = QString()) const; - void writeCppToPythonFunction(TextStream &s, const CustomConversion *customConversion) 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, @@ -255,135 +348,153 @@ private: const QString &preConversion = QString()) const; /// Writes a pair of Python to C++ conversion and check functions for implicit conversions. void writePythonToCppConversionFunctions(TextStream &s, - const CustomConversion::TargetToNativeConversion *toNative, - const TypeEntry *targetType) const; + const TargetToNativeConversion &toNative, + const TypeEntryCPtr &targetType) const; /// Writes a pair of Python to C++ conversion and check functions for instantiated container types. void writePythonToCppConversionFunctions(TextStream &s, const AbstractMetaType &containerType) const; + void writePythonToCppConversionFunction(TextStream &s, + const AbstractMetaType &containerType, + const TargetToNativeConversion &conv) const; + static void writeAddPythonToCppConversion(TextStream &s, const QString &converterVar, - const QString &pythonToCppFunc, - const QString &isConvertibleFunc); + const QString &pythonToCppFunc, + const QString &isConvertibleFunc); + + static void writeSetPythonToCppPointerConversion(TextStream &s, const QString &converterVar, + 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, - const AbstractMetaClass **wrappedClass, - QString *errorMessage = nullptr); + const AbstractMetaFunctionCPtr &func, int argIndex); + /// Returns the class for an ownership modification of the argument. + /// Throws if the argument is not a class or cannot be found. + static AbstractMetaClassCPtr + argumentClassFromIndex(const ApiExtractorResult &api, + const AbstractMetaFunctionCPtr &func, int argIndex); + void writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr &func, - const GeneratorContext &context, int maxArgs = 0) const; + const GeneratorContext &context, bool usesPyArgs, + int maxArgs, const QList<qsizetype> &argumentIndirections, + 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); - QString methodDefinitionParameters(const OverloadData &overloadData) const; - void writeMethodDefinitionEntries(TextStream &s, - const AbstractMetaFunctionCList &overloads, - qsizetype maxEntries = -1) const; - void writeMethodDefinition(TextStream &s, const AbstractMetaFunctionCList &overloads) const; - void writeSignatureInfo(TextStream &s, const AbstractMetaFunctionCList &overloads) const; + 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; + 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; - 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; + static void writeEnumsInitialization(TextStream &s, AbstractMetaEnumList &enums); + static bool writeEnumInitialization(TextStream &s, const AbstractMetaEnum &metaEnum); - void writeRichCompareFunction(TextStream &s, const GeneratorContext &context) const; + static void writeSignalInitialization(TextStream &s, const AbstractMetaClassCPtr &metaClass); - void writeEnumsInitialization(TextStream &s, AbstractMetaEnumList &enums) const; - void writeEnumInitialization(TextStream &s, const AbstractMetaEnum &metaEnum) const; - - 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); + /// 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 CustomConversion *customConversion); - static void writeEnumConverterInitialization(TextStream &s, const TypeEntry *enumType); + const CustomConversionPtr &customConversion); static void writeEnumConverterInitialization(TextStream &s, const AbstractMetaEnum &metaEnum); - void 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 void writeExtendedConverterInitialization(TextStream &s, const TypeEntry *externalType, + + static QString typeInitStruct(const TypeEntryCPtr &te); + static void writeExtendedConverterInitialization(TextStream &s, + const TypeEntryCPtr &externalType, const AbstractMetaClassCList &conversions); void writeParentChildManagement(TextStream &s, const AbstractMetaFunctionCPtr &func, + bool usesPyArgs, bool userHeuristicForReturn) const; bool writeParentChildManagement(TextStream &s, const AbstractMetaFunctionCPtr &func, - int argIndex, bool userHeuristicPolicy) const; + int argIndex, + bool usePyArgs, + bool userHeuristicPolicy) const; void writeReturnValueHeuristics(TextStream &s, const AbstractMetaFunctionCPtr &func) const; static void writeInitQtMetaTypeFunctionBody(TextStream &s, const GeneratorContext &context); @@ -393,63 +504,62 @@ 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; /// Helper function for writeStdListWrapperMethods. - static void writeIndexError(TextStream &s, const QString &errorMsg); - - QString writeReprFunction(TextStream &s, const GeneratorContext &context, - uint indirections) const; - - AbstractMetaFunctionCPtr boolCast(const AbstractMetaClass *metaClass) const; - bool hasBoolCast(const AbstractMetaClass *metaClass) const - { return !boolCast(metaClass).isNull(); } - - std::optional<AbstractMetaType> - findSmartPointerInstantiation(const TypeEntry *entry) const; + static void writeIndexError(TextStream &s, const QString &errorMsg, + ErrorReturn errorReturn); + + static QString writeReprFunctionHeader(TextStream &s, const GeneratorContext &context); + static QString writeReprFunction(TextStream &s, + const GeneratorContext &context, + uint indirections); + static QString writeSmartPointerReprFunction(TextStream &s, + const GeneratorContext &context); + static QString writeSmartPointerDirFunction(TextStream &s, + TextStream &definitionStream, + TextStream &signatureStream, + const GeneratorContext &context); + static void writeReprFunctionFooter(TextStream &s); + static void writePyMethodDefs(TextStream &s, const QString &className, + const QString &methodsDefinitions); + + void writeModuleCodeSnips(TextStream &s, const CodeSnipList &codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language) const; + + static bool hasBoolCast(const AbstractMetaClassCPtr &metaClass) + { return boolCast(metaClass).has_value(); } void clearTpFuncs(); + static QString chopType(QString s); QHash<QString, QString> m_tpFuncs; - - static QString m_currentErrorCode; - - /// Helper class to set and restore the current error code. - class ErrorCode { - public: - explicit ErrorCode(QString errorCode) { - m_savedErrorCode = CppGenerator::m_currentErrorCode; - CppGenerator::m_currentErrorCode = errorCode; - } - explicit ErrorCode(int errorCode) { - m_savedErrorCode = CppGenerator::m_currentErrorCode; - CppGenerator::m_currentErrorCode = QString::number(errorCode); - } - ~ErrorCode() { - CppGenerator::m_currentErrorCode = m_savedErrorCode; - } - private: - QString m_savedErrorCode; - }; + QHash<QString, QString> m_nbFuncs; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(CppGenerator::CppSelfDefinitionFlags) + +TextStream &operator<<(TextStream &s, CppGenerator::ErrorReturn r); + #endif // CPPGENERATOR_H diff --git a/sources/shiboken6/generator/shiboken/cppgenerator_container.cpp b/sources/shiboken6/generator/shiboken/cppgenerator_container.cpp new file mode 100644 index 000000000..00e0cabea --- /dev/null +++ b/sources/shiboken6/generator/shiboken/cppgenerator_container.cpp @@ -0,0 +1,272 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "cppgenerator.h" +#include "generatorstrings.h" +#include <abstractmetalang.h> +#include "apiextractorresult.h" +#include "ctypenames.h" +#include "containertypeentry.h" +#include "textstream.h" +#include "typedatabase.h" + +#include <QtCore/QDebug> + +#include <algorithm> + +using namespace Qt::StringLiterals; + +// Write a PyMethodDef entry, allowing for registering C++ functions +// under different names for Python. +static void writeMethod(TextStream &s, const QString &privateObjType, + const char *cppName, const char *pythonName, + const char *flags) +{ + if (pythonName == nullptr) + pythonName = cppName; + s << "{\"" << pythonName << "\", reinterpret_cast<PyCFunction>(" + << privateObjType << "::" << cppName << "), "<< flags + << ", \"" << /* doc */ pythonName << "\"},\n"; +} + +static inline void writeMethod(TextStream &s, const QString &privateObjType, + const char *cppName, const char *pythonName = nullptr) +{ + writeMethod(s, privateObjType, cppName, pythonName, "METH_O"); +} + +static inline void writeNoArgsMethod(TextStream &s, const QString &privateObjType, + const char *cppName, const char *pythonName = nullptr) +{ + writeMethod(s, privateObjType, cppName, pythonName, "METH_NOARGS"); +} + +static void writeSlot(TextStream &s, const char *tpName, const char *value) +{ + s << '{' << tpName << ", reinterpret_cast<void *>(" << value << ")},\n"; +} + +static void writeSlot(TextStream &s, const QString &privateObjType, + const char *tpName, const char *methodName) +{ + s << '{' << tpName << ", reinterpret_cast<void *>(" << privateObjType + << "::" << methodName << ")},\n"; +} + +// Write creation function from C++ reference, used by field accessors +// and getters which are within extern "C" + +enum ContainerCreationFlag +{ + None = 0, + Const = 0x1, + Allocate = 0x2 +}; + +Q_DECLARE_FLAGS(ContainerCreationFlags, ContainerCreationFlag) +Q_DECLARE_OPERATORS_FOR_FLAGS(ContainerCreationFlags) + +static void writeContainerCreationFunc(TextStream &s, + const QString &funcName, + const QString &typeFName, + const QString &containerSignature, + ContainerCreationFlags flags = {}) +{ + + // creation function from C++ reference, used by field accessors + // which are within extern "C" + s << "extern \"C\" PyObject *" << funcName << '('; + if (flags.testFlag(ContainerCreationFlag::Const)) + s << "const "; + s << containerSignature << "* ct)\n{\n" << indent + << "auto *container = PyObject_New(ShibokenContainer, " << typeFName << "());\n" + << "auto *d = new ShibokenSequenceContainerPrivate<" + << containerSignature << ">();\n"; + if (flags.testFlag(ContainerCreationFlag::Allocate)) { + s << "d->m_list = new " << containerSignature << "(*ct);\n" + << "d->m_ownsList = true;\n"; + } else if (flags.testFlag(ContainerCreationFlag::Const)) { + s << "d->m_list = const_cast<" << containerSignature << " *>(ct);\n" + << "d->m_const = true;\n"; + } else { + s << "d->m_list = ct;\n"; + } + s << "container->d = d;\n"; + s << "return reinterpret_cast<PyObject *>(container);\n" << outdent + << "}\n\n"; +} + +// Generate template specialization of value converter helper +void CppGenerator::writeOpaqueContainerValueConverter(TextStream &s, + const AbstractMetaType &valueType) const +{ + // Generate template specialization of value converter helper unless it is already there + const QString valueTypeName = valueType.cppSignature(); + const QString checkFunction = cpythonCheckFunction(valueType); + + s << "template <>\nstruct ShibokenContainerValueConverter<" + << valueTypeName << ">\n{\n"; + // Type check + s << indent << "static bool checkValue(PyObject *" << PYTHON_ARG << ")\n{\n" + << indent << "return " << checkFunction; + if (!checkFunction.contains(u'(')) + s << '('; + s << PYTHON_ARG << ");\n" + << outdent << "}\n\n"; + + // C++ to Python + const bool passByConstRef = valueType.indirectionsV().isEmpty() + && !valueType.isCppPrimitive(); + s << "static PyObject *convertValueToPython("; + if (passByConstRef) + s << "const "; + s << valueTypeName << ' '; + if (passByConstRef) + s << '&'; + s << CPP_ARG << ")\n{\n" << indent << "return "; + writeToPythonConversion(s, valueType, nullptr, CPP_ARG); + s << ";\n" << outdent << "}\n\n"; + + // Python to C++ + s << "static std::optional<" << valueTypeName << "> convertValueToCpp(PyObject *" + << PYTHON_ARG << ")\n{\n" << indent; + s << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR << ";\n" + << "if (!("; + writeTypeCheck(s, valueType, PYTHON_ARG), isNumber(valueType.typeEntry()); + s << ")) {\n" << indent + << "Shiboken::Errors::setWrongContainerType();\n" + << "return {};\n" << outdent << "}\n"; + writePythonToCppTypeConversion(s, valueType, PYTHON_ARG, CPP_ARG, nullptr, {}); + s << "return " << CPP_ARG << ";\n" << outdent << "}\n" << outdent << "};\n\n"; +} + +// Generate code for a type wrapping a C++ container instantiation +CppGenerator::OpaqueContainerData + CppGenerator::writeOpaqueContainerConverterFunctions(TextStream &s, + const AbstractMetaType &containerType, + QSet<AbstractMetaType> *valueTypes) const +{ + OpaqueContainerData result; + const auto &valueType = containerType.instantiations().constFirst(); + const auto containerTypeEntry = std::static_pointer_cast<const ContainerTypeEntry>(containerType.typeEntry()); + result.name = + containerTypeEntry->opaqueContainerName(containerType.instantiationCppSignatures()); + + const auto cppSignature = containerType.cppSignature(); + s << "\n// Binding for " << cppSignature << "\n\n"; + + if (!valueTypes->contains(valueType)) { + valueTypes->insert(valueType); + writeOpaqueContainerValueConverter(s, valueType); + } + + const QString privateObjType = u"ShibokenSequenceContainerPrivate<"_s + + cppSignature + u'>'; + + // methods + const QString &containerName = containerType.name(); + const bool isStdVector = containerName == u"std::vector"; + const auto kind = containerTypeEntry->containerKind(); + const bool isFixed = kind == ContainerTypeEntry::SpanContainer || containerName == u"std::array"; + const QString methods = result.name + u"_methods"_s; + s << "static PyMethodDef " << methods << "[] = {\n" << indent; + if (!isFixed) { + writeMethod(s, privateObjType, "push_back"); + writeMethod(s, privateObjType, "push_back", "append"); // Qt convention + writeNoArgsMethod(s, privateObjType, "clear"); + writeNoArgsMethod(s, privateObjType, "pop_back"); + writeNoArgsMethod(s, privateObjType, "pop_back", "removeLast"); // Qt convention + if (!isStdVector) { + writeMethod(s, privateObjType, "push_front"); + writeMethod(s, privateObjType, "push_front", "prepend"); // Qt convention + writeNoArgsMethod(s, privateObjType, "pop_front"); + writeMethod(s, privateObjType, "pop_front", "removeFirst"); // Qt convention + } + writeMethod(s, privateObjType, "reserve"); // SFINAE'd out for list + writeNoArgsMethod(s, privateObjType, "capacity"); + } + writeNoArgsMethod(s, privateObjType, "data"); + writeNoArgsMethod(s, privateObjType, "constData"); + s << "{nullptr, nullptr, 0, nullptr} // Sentinel\n" + << outdent << "};\n\n"; + + // slots + const QString slotsList = result.name + u"_slots"_s; + s << "static PyType_Slot " << slotsList << "[] = {\n" << indent; + writeSlot(s, privateObjType, "Py_tp_init", "tpInit"); + const auto *tpNew = containerTypeEntry->viewOn() == nullptr ? "tpNew" : "tpNewInvalid"; + writeSlot(s, privateObjType, "Py_tp_new", tpNew); + writeSlot(s, privateObjType, "Py_tp_free", "tpFree"); + writeSlot(s, "Py_tp_dealloc", "Sbk_object_dealloc"); // FIXME? + writeSlot(s, "Py_tp_methods", methods.toUtf8().constData()); + writeSlot(s, privateObjType, "Py_sq_ass_item", "sqSetItem"); + writeSlot(s, privateObjType, "Py_sq_length", "sqLen"); + writeSlot(s, privateObjType, "Py_sq_item", "sqGetItem"); + s << "{0, nullptr}\n" << outdent << "};\n\n"; + + // spec + const QString specName = result.name + u"_spec"_s; + const QString name = TypeDatabase::instance()->defaultPackageName() + + u'.' + result.name; + s << "static PyType_Spec " << specName << " = {\n" << indent + << "\"" << name.count(u'.') << ':' << name << "\",\n" + << "sizeof(ShibokenContainer),\n0,\nPy_TPFLAGS_DEFAULT,\n" + << slotsList << outdent << "\n};\n\n"; + + // type creation function that sets a key in the type dict. + const QString typeCreationFName = u"create"_s + result.name + u"Type"_s; + s << "static inline PyTypeObject *" << typeCreationFName << "()\n{\n" << indent + << "auto *result = reinterpret_cast<PyTypeObject *>(SbkType_FromSpec(&" + << specName << "));\nPy_INCREF(Py_True);\n" + << "Shiboken::AutoDecRef tpDict(PepType_GetDict(result));\n" + << "PyDict_SetItem(tpDict.object(), " + "Shiboken::PyMagicName::opaque_container(), Py_True);\n" + << "return result;\n" << outdent << "}\n\n"; + + // _TypeF() function + const QString typeFName = result.name + u"_TypeF"_s; + s << "static PyTypeObject *" << typeFName << "()\n{\n" << indent + << "static PyTypeObject *type = " << typeCreationFName + << "();\nreturn type;\n" << outdent << "}\n\n"; + + // creation functions from C++ references + ContainerCreationFlags flags; + if (kind == ContainerTypeEntry::SpanContainer) + flags.setFlag(ContainerCreationFlag::Allocate); + + writeContainerCreationFunc(s, u"create"_s + result.name, typeFName, + containerType.cppSignature(), flags); + flags.setFlag(ContainerCreationFlag::Const); + writeContainerCreationFunc(s, u"createConst"_s + result.name, typeFName, + containerType.cppSignature(), flags); + + // Check function + result.checkFunctionName = result.name + u"_Check"_s; + s << "extern \"C\" int " << result.checkFunctionName << "(PyObject *" << PYTHON_ARG + << ")\n{\n" << indent << "return " << PYTHON_ARG << " != nullptr && " + << PYTHON_ARG << " != Py_None && " << PYTHON_ARG << "->ob_type == " + << typeFName << "();\n" << outdent << "}\n\n"; + + // SBK converter Python to C++ + result.pythonToConverterFunctionName = u"PythonToCpp"_s + result.name; + s << "extern \"C\" void " << result.pythonToConverterFunctionName + << "(PyObject *" << PYTHON_ARG << ", void *cppOut)\n{\n" << indent + << "auto *d = ShibokenSequenceContainerPrivate<" << cppSignature + << ">::get(" << PYTHON_ARG << ");\n" + << "*reinterpret_cast<" << cppSignature << "**>(cppOut) = d->m_list;\n" + << outdent << "}\n\n"; + + // SBK check function for converting Python to C++ that returns the converter + result.converterCheckFunctionName = u"is"_s + result.name + u"PythonToCppConvertible"_s; + s << "extern \"C\" PythonToCppFunc " << result.converterCheckFunctionName + << "(PyObject *" << PYTHON_ARG << ")\n{\n" << indent << "if (" + << result.checkFunctionName << '(' << PYTHON_ARG << "))\n" << indent + << "return " << result.pythonToConverterFunctionName << ";\n" + << outdent << "return {};\n" << outdent << "}\n\n"; + + QTextStream(&result.registrationCode) << "ob_type = reinterpret_cast<PyObject *>(" + << typeFName + << "());\nPy_XINCREF(ob_type);\nPyModule_AddObject(module, \"" + << result.name << "\", ob_type);\n"; + return result; +} diff --git a/sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp b/sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp new file mode 100644 index 000000000..44b76f181 --- /dev/null +++ b/sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp @@ -0,0 +1,476 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "cppgenerator.h" +#include "generatorstrings.h" +#include "generatorcontext.h" +#include <apiextractorresult.h> +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <codesnip.h> +#include <exception.h> +#include <messages.h> +#include <textstream.h> +#include <overloaddata.h> +#include <smartpointertypeentry.h> + +#include <QtCore/QDebug> + +using namespace Qt::StringLiterals; + +static const char smartPtrComment[] = + "// Try to find the 'name' attribute, by retrieving the PyObject for " + "the corresponding C++ object held by the smart pointer.\n"; + +static QString smartPointerGetter(const GeneratorContext &context) +{ + const auto te = context.metaClass()->typeEntry(); + Q_ASSERT(te->isSmartPointer()); + return std::static_pointer_cast<const SmartPointerTypeEntry>(te)->getter(); +} + +struct callGetter +{ + explicit callGetter(const GeneratorContext &context) : m_context(context) {} + + const GeneratorContext &m_context; +}; + +TextStream &operator<<(TextStream &str, const callGetter &c) +{ + str << "PyObject_CallMethod(self, \"" << smartPointerGetter(c.m_context) << "\", 0)"; + return str; +} + +// Helpers to collect all smart pointer pointee base classes +static AbstractMetaClassCList + findSmartPointeeBaseClasses(const ApiExtractorResult &api, + const AbstractMetaType &smartPointerType) +{ + AbstractMetaClassCList result; + auto instantiationsTe = smartPointerType.instantiations().at(0).typeEntry(); + auto targetClass = AbstractMetaClass::findClass(api.classes(), instantiationsTe); + if (targetClass != nullptr) + result = targetClass->allTypeSystemAncestors(); + return result; +} + +using ComparisonOperatorList = QList<AbstractMetaFunction::ComparisonOperatorType>; + +// Return the available comparison operators for smart pointers +static ComparisonOperatorList smartPointeeComparisons(const GeneratorContext &context) +{ + Q_ASSERT(context.forSmartPointer()); + auto te = context.preciseType().instantiations().constFirst().typeEntry(); + if (isExtendedCppPrimitive(te)) { // Primitive pointee types have all + return {AbstractMetaFunction::OperatorEqual, + AbstractMetaFunction::OperatorNotEqual, + AbstractMetaFunction::OperatorLess, + AbstractMetaFunction::OperatorLessEqual, + AbstractMetaFunction::OperatorGreater, + AbstractMetaFunction::OperatorGreaterEqual}; + } + + const auto pointeeClass = context.pointeeClass(); + if (!pointeeClass) + return {}; + + ComparisonOperatorList result; + const auto &comparisons = + pointeeClass->operatorOverloads(OperatorQueryOption::SymmetricalComparisonOp); + for (const auto &f : comparisons) { + const auto ct = f->comparisonOperatorType().value(); + if (!result.contains(ct)) + result.append(ct); + } + return result; +} + +static bool hasParameterPredicate(const AbstractMetaFunctionCPtr &f) +{ + return !f->arguments().isEmpty(); +} + +void CppGenerator::generateSmartPointerClass(TextStream &s, const GeneratorContext &classContext) +{ + s.setLanguage(TextStream::Language::Cpp); + AbstractMetaClassCPtr metaClass = classContext.metaClass(); + const auto typeEntry = std::static_pointer_cast<const SmartPointerTypeEntry>(metaClass->typeEntry()); + const bool hasPointeeClass = classContext.pointeeClass() != nullptr; + const auto smartPointerType = typeEntry->smartPointerType(); + const bool isValueHandle = smartPointerType ==TypeSystem::SmartPointerType::ValueHandle; + + IncludeGroup includes{u"Extra includes"_s, typeEntry->extraIncludes()}; + if (hasPointeeClass) + includes.append(classContext.pointeeClass()->typeEntry()->include()); + includes.includes.append({Include::IncludePath, u"sbksmartpointer.h"_s}); + generateIncludes(s, classContext, {includes}); + + s << '\n'; + + // class inject-code native/beginning + if (!typeEntry->codeSnips().isEmpty()) { + writeClassCodeSnips(s, typeEntry->codeSnips(), + TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode, + classContext); + s << '\n'; + } + + StringStream smd(TextStream::Language::Cpp); + StringStream md(TextStream::Language::Cpp); + StringStream signatureStream(TextStream::Language::Cpp); + + s << openTargetExternC; + + const auto &functionGroups = getFunctionGroups(metaClass); + + // Skip all public methods of the smart pointer except for the special + // methods declared in the type entry. + + auto ctors = metaClass->queryFunctions(FunctionQueryOption::Constructors); + if (!hasPointeeClass && !isValueHandle) { // Cannot generate "int*" + auto end = std::remove_if(ctors.begin(), ctors.end(), hasParameterPredicate); + ctors.erase(end, ctors.end()); + } + + if (!ctors.isEmpty()) { + OverloadData overloadData(ctors, api()); + writeConstructorWrapper(s, overloadData, classContext); + writeSignatureInfo(signatureStream, overloadData); + } + + if (!typeEntry->resetMethod().isEmpty()) { + auto it = functionGroups.constFind(typeEntry->resetMethod()); + if (it == functionGroups.cend()) + throw Exception(msgCannotFindSmartPointerMethod(typeEntry, typeEntry->resetMethod())); + AbstractMetaFunctionCList resets = it.value(); + if (!hasPointeeClass && !isValueHandle) { // Cannot generate "int*" + auto end = std::remove_if(resets.begin(), resets.end(), hasParameterPredicate); + resets.erase(end, resets.end()); + } + if (!resets.isEmpty()) + writeMethodWrapper(s, md, signatureStream, resets, classContext); + } + + auto it = functionGroups.constFind(typeEntry->getter()); + if (it == functionGroups.cend() || it.value().size() != 1) + throw Exception(msgCannotFindSmartPointerGetter(typeEntry)); + + writeMethodWrapper(s, md, signatureStream, it.value(), classContext); + + QStringList optionalMethods; + if (!typeEntry->refCountMethodName().isEmpty()) + optionalMethods.append(typeEntry->refCountMethodName()); + const QString valueCheckMethod = typeEntry->valueCheckMethod(); + if (!valueCheckMethod.isEmpty() && !valueCheckMethod.startsWith(u"operator")) + optionalMethods.append(valueCheckMethod); + if (!typeEntry->nullCheckMethod().isEmpty()) + optionalMethods.append(typeEntry->nullCheckMethod()); + + for (const QString &optionalMethod : optionalMethods) { + auto it = functionGroups.constFind(optionalMethod); + if (it == functionGroups.cend() || it.value().size() != 1) + throw Exception(msgCannotFindSmartPointerMethod(typeEntry, optionalMethod)); + writeMethodWrapper(s, md, signatureStream, it.value(), classContext); + } + + writeCopyFunction(s, md, signatureStream, classContext); + writeSmartPointerDirFunction(s, md, signatureStream, classContext); + + const QString methodsDefinitions = md.toString(); + const QString singleMethodDefinitions = smd.toString(); + + const QString className = chopType(cpythonTypeName(typeEntry)); + + // Write single method definitions + s << singleMethodDefinitions; + + // Write methods definition + writePyMethodDefs(s, className, methodsDefinitions); + + // Write tp_s/getattro function + const auto boolCastOpt = boolCast(metaClass); + writeSmartPointerGetattroFunction(s, classContext, boolCastOpt); + writeSmartPointerSetattroFunction(s, classContext); + + if (boolCastOpt.has_value()) + writeNbBoolFunction(classContext, boolCastOpt.value(), s); + + if (smartPointerType == TypeSystem::SmartPointerType::Shared) + writeSmartPointerRichCompareFunction(s, classContext); + + s << closeExternC; + + if (hasHashFunction(metaClass)) + writeHashFunction(s, classContext); + + // Write tp_traverse and tp_clear functions. + writeTpTraverseFunction(s, metaClass); + writeTpClearFunction(s, metaClass); + + writeClassDefinition(s, metaClass, classContext); + + s << '\n'; + + writeConverterFunctions(s, metaClass, classContext); + // Implicit smart pointers conversions + writeSmartPointerConverterFunctions(s, classContext.preciseType()); + writeClassRegister(s, metaClass, classContext, signatureStream); + + // class inject-code native/end + if (!typeEntry->codeSnips().isEmpty()) { + writeClassCodeSnips(s, typeEntry->codeSnips(), + TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode, + classContext); + s << '\n'; + } +} + +void CppGenerator::writeSmartPointerConverterFunctions(TextStream &s, + const AbstractMetaType &smartPointerType) const +{ + const auto baseClasses = findSmartPointeeBaseClasses(api(), smartPointerType); + if (baseClasses.isEmpty()) + return; + + auto smartPointerTypeEntry = + std::static_pointer_cast<const SmartPointerTypeEntry>(smartPointerType.typeEntry()); + + // TODO: Missing conversion to smart pointer pointer type: + + s << "// Register smartpointer conversion for all derived classes\n"; + for (const auto &base : baseClasses) { + auto baseTe = base->typeEntry(); + if (smartPointerTypeEntry->matchesInstantiation(baseTe)) { + if (auto opt = api().findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) { + const auto &smartTargetType = opt.value().type; + s << "// SmartPointer derived class: " + << smartTargetType.cppSignature() << "\n"; + writePythonToCppConversionFunctions(s, smartPointerType, + smartTargetType, {}, {}, {}); + } + } + } +} + +void CppGenerator::writeSmartPointerCppSelfConversion(TextStream &s, + const GeneratorContext &context) +{ + Q_ASSERT(context.forSmartPointer()); + s << cpythonWrapperCPtr(context.preciseType(), u"self"_s); +} + +void CppGenerator::writeSmartPointerCppSelfDefinition(TextStream &s, + const GeneratorContext &context, + ErrorReturn errorReturn, + CppSelfDefinitionFlags flags) +{ + Q_ASSERT(context.forSmartPointer()); + writeInvalidPyObjectCheck(s, u"self"_s, errorReturn); + writeCppSelfVarDef(s, flags); + writeSmartPointerCppSelfConversion(s, context); + s << ";\n"; +} + +void CppGenerator::writeSmartPointerConverterInitialization(TextStream &s, + const AbstractMetaType &type) const +{ + const QByteArray cppSignature = type.cppSignature().toUtf8(); + auto writeConversionRegister = [&s](const AbstractMetaType &sourceType, + const QString &targetTypeName, + const QString &targetConverter) + { + const QString sourceTypeName = fixedCppTypeName(sourceType); + const QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); + const QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName); + + writeAddPythonToCppConversion(s, targetConverter, toCpp, isConv); + }; + + const auto classes = findSmartPointeeBaseClasses(api(), type); + if (classes.isEmpty()) + return; + + auto smartPointerTypeEntry = std::static_pointer_cast<const SmartPointerTypeEntry>(type.typeEntry()); + + s << "// Register SmartPointer converter for type '" << cppSignature << "'." << '\n' + << "///////////////////////////////////////////////////////////////////////////////////////\n\n"; + + for (const auto &base : classes) { + auto baseTe = base->typeEntry(); + if (auto opt = api().findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) { + const auto &smartTargetType = opt.value().type; + s << "// Convert to SmartPointer derived class: [" + << smartTargetType.cppSignature() << "]\n"; + const QString converter = u"Shiboken::Conversions::getConverter(\""_s + + smartTargetType.cppSignature() + u"\")"_s; + writeConversionRegister(type, fixedCppTypeName(smartTargetType), converter); + } else { + s << "// Class not found:" << type.instantiations().at(0).cppSignature(); + } + } + + s << "///////////////////////////////////////////////////////////////////////////////////////" << '\n' << '\n'; +} + +void CppGenerator::writeSmartPointerRichCompareFunction(TextStream &s, + const GeneratorContext &context) const +{ + static const char selfPointeeVar[] = "cppSelfPointee"; + static const char cppArg0PointeeVar[] = "cppArg0Pointee"; + + const auto metaClass = context.metaClass(); + QString baseName = cpythonBaseName(metaClass); + writeRichCompareFunctionHeader(s, baseName, context); + + s << "if ("; + writeTypeCheck(s, context.preciseType(), PYTHON_ARG); + s << ") {\n" << indent; + writeArgumentConversion(s, context.preciseType(), CPP_ARG0, + PYTHON_ARG, ErrorReturn::Default, metaClass); + + const auto te = context.preciseType().typeEntry(); + Q_ASSERT(te->isSmartPointer()); + const auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(te); + + s << "const auto *" << selfPointeeVar << " = " << CPP_SELF_VAR + << '.' << ste->getter() << "();\n"; + s << "const auto *" << cppArg0PointeeVar << " = " << CPP_ARG0 + << '.' << ste->getter() << "();\n"; + + // If we have an object without any comparisons, only generate a simple + // equality check by pointee address + auto availableOps = smartPointeeComparisons(context); + const bool comparePointeeAddressOnly = availableOps.isEmpty(); + if (comparePointeeAddressOnly) { + availableOps << AbstractMetaFunction::OperatorEqual + << AbstractMetaFunction::OperatorNotEqual; + } else { + // For value types with operators, we complain about nullptr + s << "if (" << selfPointeeVar << " == nullptr || " << cppArg0PointeeVar + << " == nullptr) {\n" << indent + << "PyErr_SetString(PyExc_NotImplementedError, \"nullptr passed to comparison.\");\n" + << ErrorReturn::Default << '\n' << outdent << "}\n"; + } + + s << "bool " << CPP_RETURN_VAR << "= false;\n" + << "switch (op) {\n"; + for (auto op : availableOps) { + s << "case " << AbstractMetaFunction::pythonRichCompareOpCode(op) << ":\n" + << indent << CPP_RETURN_VAR << " = "; + if (comparePointeeAddressOnly) { + s << selfPointeeVar << ' ' << AbstractMetaFunction::cppComparisonOperator(op) + << ' ' << cppArg0PointeeVar << ";\n"; + } else { + // Shortcut for equality: Check pointee address + if (op == AbstractMetaFunction::OperatorEqual + || op == AbstractMetaFunction::OperatorLessEqual + || op == AbstractMetaFunction::OperatorGreaterEqual) { + s << selfPointeeVar << " == " << cppArg0PointeeVar << " || "; + } + // Generate object's comparison + s << "*" << selfPointeeVar << ' ' + << AbstractMetaFunction::cppComparisonOperator(op) << " *" + << cppArg0PointeeVar << ";\n"; + } + s << "break;\n" << outdent; + + } + if (availableOps.size() < 6) { + s << "default:\n" << indent + << richCompareComment + << "return FallbackRichCompare(self, " << PYTHON_ARG << ", op);\n" << outdent; + } + s << "}\n" << PYTHON_RETURN_VAR << " = " << CPP_RETURN_VAR + << " ? Py_True : Py_False;\n" + << "Py_INCREF(" << PYTHON_RETURN_VAR << ");\n" + << outdent << "}\n" + << "return Shiboken::returnFromRichCompare(" << PYTHON_RETURN_VAR << ");\n" + << outdent << "}\n\n"; +} + +void CppGenerator::writeSmartPointerSetattroFunction(TextStream &s, + const GeneratorContext &context) +{ + Q_ASSERT(context.forSmartPointer()); + writeSetattroDefinition(s, context.metaClass()); + s << smartPtrComment + << "if (auto *rawObj = " << callGetter(context) << ") {\n" << indent + << "if (PyObject_HasAttr(rawObj, name) != 0)\n" << indent + << "return PyObject_GenericSetAttr(rawObj, name, value);\n" << outdent + << "Py_DECREF(rawObj);\n" << outdent + << "}\n"; + writeSetattroDefaultReturn(s); +} + +void CppGenerator::writeSmartPointerGetattroFunction(TextStream &s, + const GeneratorContext &context, + const BoolCastFunctionOptional &boolCast) +{ + Q_ASSERT(context.forSmartPointer()); + const auto metaClass = context.metaClass(); + writeGetattroDefinition(s, metaClass); + s << "PyObject *tmp = PyObject_GenericGetAttr(self, name);\n" + << "if (tmp)\n" << indent << "return tmp;\n" << outdent + << "if (PyErr_ExceptionMatches(PyExc_AttributeError) == 0)\n" + << indent << "return nullptr;\n" << outdent + << "PyErr_Clear();\n"; + + if (boolCast.has_value()) { + writeSmartPointerCppSelfDefinition(s, context); + s << "if ("; + writeNbBoolExpression(s, boolCast.value(), true /* invert */); + s << ") {\n" << indent + << R"(PyTypeObject *tp = Py_TYPE(self); +PyErr_Format(PyExc_AttributeError, "Attempt to retrieve '%s' from null object '%s'.", + Shiboken::String::toCString(name), tp->tp_name); +return nullptr; +)" << outdent << "}\n"; + } + + // This generates the code which dispatches access to member functions + // and fields from the smart pointer to its pointee. + s << smartPtrComment + << "if (auto *rawObj = " << callGetter(context) << ") {\n" << indent + << "if (auto *attribute = PyObject_GetAttr(rawObj, name))\n" + << indent << "tmp = attribute;\n" << outdent + << "Py_DECREF(rawObj);\n" << outdent + << "}\n" + << "if (!tmp) {\n" << indent + << R"(PyTypeObject *tp = Py_TYPE(self); +PyErr_Format(PyExc_AttributeError, + "'%.50s' object has no attribute '%.400s'", + tp->tp_name, Shiboken::String::toCString(name)); +)" << outdent + << "}\n" + << "return tmp;\n" << outdent << "}\n\n"; +} + +QString CppGenerator::writeSmartPointerReprFunction(TextStream &s, + const GeneratorContext &context) +{ + const auto metaClass = context.metaClass(); + QString funcName = writeReprFunctionHeader(s, context); + s << "Shiboken::AutoDecRef pointee(" << callGetter(context) << ");\n" + << "return Shiboken::SmartPointer::repr(self, pointee);\n"; + writeReprFunctionFooter(s); + return funcName; +} + +QString CppGenerator::writeSmartPointerDirFunction(TextStream &s, TextStream &definitionStream, + TextStream &signatureStream, + const GeneratorContext &context) +{ + QString funcName = cpythonBaseName(context.metaClass()) + u"__dir__"_s; + + signatureStream << fullPythonClassName(context.metaClass()) << ".__dir__()\n"; + definitionStream << PyMethodDefEntry{u"__dir__"_s, funcName, {"METH_NOARGS"_ba}, {}} + << ",\n"; + + s << "extern \"C\"\n{\n" + << "static PyObject *" << funcName << "(PyObject *self)\n{\n" << indent + << "Shiboken::AutoDecRef pointee(" << callGetter(context) << ");\n" + << "return Shiboken::SmartPointer::dir(self, pointee);\n" + << outdent << "}\n} // extern C\n\n"; + return funcName; +} diff --git a/sources/shiboken6/generator/shiboken/ctypenames.h b/sources/shiboken6/generator/shiboken/ctypenames.h index abac261d5..f665b30ff 100644 --- a/sources/shiboken6/generator/shiboken/ctypenames.h +++ b/sources/shiboken6/generator/shiboken/ctypenames.h @@ -1,56 +1,31 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef CTYPENAMES_H #define CTYPENAMES_H #include <QtCore/QString> -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/generatorargument.cpp b/sources/shiboken6/generator/shiboken/generatorargument.cpp new file mode 100644 index 000000000..e81ad0797 --- /dev/null +++ b/sources/shiboken6/generator/shiboken/generatorargument.cpp @@ -0,0 +1,110 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "generatorargument.h" +#include <abstractmetatype.h> +#include <messages.h> +#include <typesystem.h> + +#include <QtCore/QDebug> +#include <QtCore/QSet> + +static bool isCppPrimitiveString(const AbstractMetaType &type) +{ + return type.referenceType() == NoReference && type.indirections() == 1 + && AbstractMetaType::cppSignedCharTypes().contains(type.name()); +} + +GeneratorArgument GeneratorArgument::fromMetaType(const AbstractMetaType &type) +{ + GeneratorArgument result; + + const auto typeEntry = type.typeEntry(); + if (typeEntry->isCustom() || typeEntry->isVarargs()) + return result; + + result.indirections = -type.indirectionsV().size(); + if (isCppPrimitiveString(type) + || type.isVoidPointer() + || type.typeUsagePattern() == AbstractMetaType::NativePointerAsArrayPattern) { + result.indirections += 1; + } + + if (typeEntry->isEnum()) { + result.type = Type::Enum; + } else if (typeEntry->isFlags()) { + result.type = Type::Flags; + } else if (typeEntry->isContainer()) { + result.type = Type::Container; + } else { + if (typeEntry->isPrimitive()) + result.type = Type::Primitive; + + const AbstractMetaTypeList &nestedArrayTypes = type.nestedArrayTypes(); + if (!nestedArrayTypes.isEmpty()) { + if (nestedArrayTypes.constLast().isCppPrimitive()) { + result.type = Type::CppPrimitiveArray; + } else { + static QSet<QString> warnedTypes; + const QString &signature = type.cppSignature(); + if (!warnedTypes.contains(signature)) { + warnedTypes.insert(signature); + qWarning("%s", qPrintable(msgUnknownArrayPointerConversion(signature))); + } + result.indirections -= 1; + } + } + } + + if (result.type == Type::Other || result.type == Type::Primitive) { + if (type.valueTypeWithCopyConstructorOnlyPassed()) { + result.flags.setFlag(Flag::TreatAsPointer); + } else if ((type.isObjectType() || type.isPointer()) + && !type.isUserPrimitive() && !type.isExtendedCppPrimitive()) { + result.flags.setFlag(Flag::PointerOrObjectType); + } else if (type.referenceType() == LValueReference + && !type.isUserPrimitive() + && !type.isExtendedCppPrimitive()) { + result.flags.setFlag(Flag::MayHaveImplicitConversion); + } + } + + // For implicit conversions or containers, either value or pointer conversion + // may occur. An implicit conversion uses value conversion whereas the object + // itself uses pointer conversion. For containers, the PyList/container + // conversion is by value whereas opaque containers use pointer conversion. + // For a container passed by pointer, a local variable is also needed. + if (result.flags.testFlag(Flag::MayHaveImplicitConversion) + || type.generateOpaqueContainer() + || (result.type == Type::Container && result.indirections != 0)) { + result.flags.setFlag(Flag::ValueOrPointer); + } + + if (result.type == Type::CppPrimitiveArray) { + result.conversion = Conversion::CppPrimitiveArray; + } else if (result.flags.testFlag(Flag::ValueOrPointer)) { + result.conversion = Conversion::ValueOrPointer; + ++result.indirections; + } else if (result.flags.testAnyFlags(Flag::TreatAsPointer | Flag::PointerOrObjectType)) { + result.conversion = Conversion::Pointer; + ++result.indirections; + } + + return result; +} + +QDebug operator<<(QDebug debug, const GeneratorArgument &a) +{ + QDebugStateSaver saver(debug); + debug.noquote(); + debug.nospace(); + debug << "GeneratorArgument(" << a.type; + if (a.conversion != GeneratorArgument::Conversion::Default) + debug << ", conversion=" << a.conversion; + if (a.flags) + debug << ", flags(" << a.flags; + if (a.indirections != 0) + debug << ", indirections=" << a.indirections; + debug << ')'; + return debug; +} diff --git a/sources/shiboken6/generator/shiboken/generatorargument.h b/sources/shiboken6/generator/shiboken/generatorargument.h new file mode 100644 index 000000000..385ad0f63 --- /dev/null +++ b/sources/shiboken6/generator/shiboken/generatorargument.h @@ -0,0 +1,60 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef GENERATORARGUMENT_H +#define GENERATORARGUMENT_H + +#include <QtCore/QFlags> +#include <QtCore/qobjectdefs.h> + +QT_FORWARD_DECLARE_CLASS(QDebug) + +class AbstractMetaType; + +/// A struct containing information on how the generator processes a function argument. +struct GeneratorArgument +{ + Q_GADGET + +public: + enum class Type { + Other, + Primitive, + Enum, + Flags, + Container, + CppPrimitiveArray + }; + Q_ENUM(Type) + + enum class Conversion { + Default, + CppPrimitiveArray, // Similar to Default except default values + Pointer, + ValueOrPointer + }; + Q_ENUM(Conversion) + + enum class Flag { + TreatAsPointer = 0x1, + PointerOrObjectType = 0x2, + MayHaveImplicitConversion = 0x4, + ValueOrPointer = 0x8, + }; + Q_ENUM(Flag) + Q_DECLARE_FLAGS(Flags, Flag) + + static GeneratorArgument fromMetaType(const AbstractMetaType &type); + + Flags flags; + /// Indirections from generated "cppArg<n>" variable to function argument. + qsizetype indirections = 0; + Type type = Type::Other; + Conversion conversion = Conversion::Default; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(GeneratorArgument::Flags) + +QDebug operator<<(QDebug debug, const GeneratorArgument &a); + +#endif // GENERATORARGUMENT_H diff --git a/sources/shiboken6/generator/shiboken/generatorstrings.h b/sources/shiboken6/generator/shiboken/generatorstrings.h new file mode 100644 index 000000000..9ce91e599 --- /dev/null +++ b/sources/shiboken6/generator/shiboken/generatorstrings.h @@ -0,0 +1,39 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef GENERATORSTRINGS_H +#define GENERATORSTRINGS_H + +#include <QtCore/QString> + +QString CPP_ARG_N(int i); +QString CPP_ARG_REMOVED(int i); + +constexpr auto CPP_RETURN_VAR = QLatin1StringView("cppResult"); +constexpr auto CPP_SELF_VAR = QLatin1StringView("cppSelf"); +constexpr auto CPP_ARG = QLatin1StringView("cppArg"); +constexpr auto NULL_PTR = QLatin1StringView("nullptr"); +constexpr auto PYTHON_ARG = QLatin1StringView("pyArg"); +constexpr auto PYTHON_ARGS = QLatin1StringView("pyArgs"); +constexpr auto PYTHON_OVERRIDE_VAR = QLatin1StringView("pyOverride"); +constexpr auto PYTHON_RETURN_VAR = QLatin1StringView("pyResult"); +constexpr auto PYTHON_SELF_VAR = QLatin1StringView("self"); +constexpr auto PYTHON_TO_CPP_VAR = QLatin1StringView("pythonToCpp"); + +constexpr auto CONV_RULE_OUT_VAR_SUFFIX = QLatin1StringView("_out"); +constexpr auto BEGIN_ALLOW_THREADS + = QLatin1StringView("PyThreadState *_save = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS"); +constexpr auto END_ALLOW_THREADS + = QLatin1StringView("PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS"); + +constexpr auto REPR_FUNCTION = QLatin1StringView("__repr__"); + +constexpr auto CPP_ARG0 = QLatin1StringView("cppArg0"); + +extern const char *const METHOD_DEF_SENTINEL; +extern const char *const PYTHON_TO_CPPCONVERSION_STRUCT; +extern const char *const openTargetExternC; +extern const char *const closeExternC; +extern const char *const richCompareComment; + +#endif // GENERATORSTRINGS_H diff --git a/sources/shiboken6/generator/shiboken/headergenerator.cpp b/sources/shiboken6/generator/shiboken/headergenerator.cpp index 2b9074fd8..7cec9c38e 100644 --- a/sources/shiboken6/generator/shiboken/headergenerator.cpp +++ b/sources/shiboken6/generator/shiboken/headergenerator.cpp @@ -1,76 +1,106 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "headergenerator.h" +#include "configurablescope.h" +#include "generatorcontext.h" #include <apiextractorresult.h> +#include <abstractmetaargument.h> #include <abstractmetaenum.h> #include <abstractmetafield.h> #include <abstractmetafunction.h> #include <abstractmetalang.h> #include <abstractmetalang_helpers.h> -#include <modifications.h> +#include <codesnip.h> +#include <clangparser/compilersupport.h> +#include <exception.h> #include <typedatabase.h> #include <reporthandler.h> #include <textstream.h> #include <fileout.h> -#include "parser/codemodel.h" +#include "containertypeentry.h" +#include "enumtypeentry.h" +#include "flagstypeentry.h" +#include <messages.h> +#include "namespacetypeentry.h" +#include "primitivetypeentry.h" +#include "typedefentry.h" +#include "typesystemtypeentry.h" + +#include "qtcompat.h" #include <algorithm> +#include <set> #include <QtCore/QDir> #include <QtCore/QTextStream> #include <QtCore/QVariant> #include <QtCore/QDebug> -QString HeaderGenerator::fileNameSuffix() const +using namespace Qt::StringLiterals; + +struct IndexValue +{ + QString name; // "SBK_..." + int value; + QString comment; +}; + +TextStream &operator<<(TextStream &s, const IndexValue &iv) +{ + s << " " << AlignedField(iv.name, 56) << " = " << iv.value << ','; + if (!iv.comment.isEmpty()) + s << " // " << iv.comment; + s << '\n'; + return s; +} + +// PYSIDE-504: Handling the "protected hack" +// The problem: Creating wrappers when the class has private destructors. +// You can see an example on Windows in qclipboard_wrapper.h and others. +// Simply search for the text "// C++11: need to declare (unimplemented) destructor". +// The protected hack is the definition "#define protected public". +// For most compilers, this "hack" is enabled, because the problem of private +// destructors simply vanishes. +// +// If one does not want to use this hack, then a new problem arises: +// C++11 requires that a destructor is declared in a wrapper class when it is +// private in the base class. There is no implementation allowed! +// +// Unfortunately, MSVC in recent versions supports C++11, and due to restrictive +// rules, it is impossible to use the hack with this compiler. +// More unfortunate: Clang, when C++11 is enabled, also enforces a declaration +// of a private destructor, but it falsely then creates a linker error! +// +// Originally, we wanted to remove the protected hack. But due to the Clang +// problem, we gave up on removal of the protected hack and use it always +// when we can. This might change again when the Clang problem is solved. + +static bool alwaysGenerateDestructorDeclaration() { - return QLatin1String("_wrapper.h"); + return clang::compiler() == Compiler::Msvc; } +const char *HeaderGenerator::protectedHackDefine = R"(// Workaround to access protected functions +#ifndef protected +# define protected public +#endif + +)"; + QString HeaderGenerator::fileNameForContext(const GeneratorContext &context) const { - const AbstractMetaClass *metaClass = context.metaClass(); - if (!context.forSmartPointer()) { - QString fileNameBase = metaClass->qualifiedCppName().toLower(); - fileNameBase.replace(QLatin1String("::"), QLatin1String("_")); - return fileNameBase + fileNameSuffix(); - } - QString fileNameBase = getFileNameBaseForSmartPointer(context.preciseType(), metaClass); - return fileNameBase + fileNameSuffix(); + 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()) { @@ -79,145 +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; - if (!classContext.forSmartPointer()) { - wrapperName = classContext.useWrapper() - ? classContext.wrapperName() : metaClass->qualifiedCppName(); - } else { - wrapperName = classContext.smartPointerWrapperName(); - } - QString outerHeaderGuard = getFilteredCppSignatureString(wrapperName).toUpper(); - QString innerHeaderGuard; + QString wrapperName = classContext.effectiveClassName(); + QString outerHeaderGuard = getFilteredCppSignatureString(wrapperName); // Header s << "#ifndef SBK_" << outerHeaderGuard << "_H\n"; s << "#define SBK_" << outerHeaderGuard << "_H\n\n"; if (!avoidProtectedHack()) - s << "#define protected public\n\n"; + s << protectedHackDefine; - //Includes - auto typeEntry = metaClass->typeEntry(); - s << typeEntry->include() << '\n'; - if (classContext.useWrapper() && !typeEntry->extraIncludes().isEmpty()) { - s << "\n// Extra includes\n"; - for (const Include &inc : typeEntry->extraIncludes()) - s << inc.toString() << '\n'; + // Includes + s << metaClass->typeEntry()->include() << '\n'; + for (auto &inst : metaClass->templateBaseClassInstantiations()) + s << inst.typeEntry()->include(); + + if (classContext.useWrapper()) + writeWrapperClass(s, wrapperName, classContext); + + s << "#endif // SBK_" << outerHeaderGuard << "_H\n\n"; +} + +void HeaderGenerator::writeWrapperClass(TextStream &s, + const QString &wrapperName, + const GeneratorContext &classContext) const +{ + const auto metaClass = classContext.metaClass(); + + if (avoidProtectedHack()) { + const auto includeGroups = classIncludes(metaClass); + for( const auto &includeGroup : includeGroups) + s << includeGroup; } - if (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"; - const auto &funcs = filterFunctions(metaClass); - int maxOverrides = 0; - for (const auto &func : funcs) { - if (!func->attributes().testFlag(AbstractMetaFunction::FinalCppMethod)) { - writeFunction(s, func); - // PYSIDE-803: Build a boolean cache for unused overrides. - if (shouldWriteVirtualMethodNative(func)) - maxOverrides++; - } - } - if (!maxOverrides) - maxOverrides = 1; - - //destructor - // PYSIDE-504: When C++ 11 is used, then the destructor must always be declared. - // See abstractmetalang.cpp, determineCppWrapper() and generator.h for further - // reference. - if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor() || alwaysGenerateDestructor) { - 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); + + s << "# endif // SBK_" << innerHeaderGuard << "_H\n\n"; +} + +void HeaderGenerator::writeWrapperClassDeclaration(TextStream &s, + const QString &wrapperName, + const GeneratorContext &classContext) const +{ + const AbstractMetaClassCPtr metaClass = classContext.metaClass(); + const auto typeEntry = metaClass->typeEntry(); + InheritedOverloadSet inheritedOverloads; + + // Class + s << "class " << wrapperName + << " : public " << metaClass->qualifiedCppName() + << "\n{\npublic:\n" << indent + << wrapperName << "(const " << wrapperName << " &) = delete;\n" + << wrapperName << "& operator=(const " << wrapperName << " &) = delete;\n" + << wrapperName << '(' << wrapperName << " &&) = delete;\n" + << wrapperName << "& operator=(" << wrapperName << " &&) = delete;\n\n"; + + // Make protected enums accessible + if (avoidProtectedHack()) { + recurseClassHierarchy(metaClass, [&s] (const AbstractMetaClassCPtr &metaClass) { + writeProtectedEnums(s, metaClass); + return false; + }); + } + + if (avoidProtectedHack() && metaClass->hasProtectedFields()) { + s << "\n// Make protected fields accessible\n"; + const QString name = metaClass->qualifiedCppName(); + for (const auto &f : metaClass->fields()) { + if (f.isProtected()) + s << "using " << name << "::" << f.originalName() << ";\n"; } + s << '\n'; + } - writeClassCodeSnips(s, metaClass->typeEntry()->codeSnips(), - TypeSystem::CodeSnipPositionDeclaration, TypeSystem::NativeCode, - classContext); + 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 ((!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) - && usePySideExtensions() && metaClass->isQObject()) { - s << outdent << "public:\n" << indent << -R"(int qt_metacall(QMetaObject::Call call, int id, void **args) override; + 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 : qAsConst(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"; - - 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"; - - // PYSIDE-500: Use also includes for inherited wrapper classes, because - // without the protected hack, we sometimes need to cast inherited wrappers. - // But we don't use multiple include files. Instead, they are inserted as recursive - // headers. This keeps the file structure as simple as before the enhanced inheritance. - metaClass = metaClass->baseClass(); - if (!metaClass || !avoidProtectedHack()) - break; - classContext = contextForClass(metaClass); - if (!classContext.forSmartPointer()) { - wrapperName = classContext.useWrapper() - ? classContext.wrapperName() : metaClass->qualifiedCppName(); - } else { - wrapperName = classContext.smartPointerWrapperName(); - } - innerHeaderGuard = getFilteredCppSignatureString(wrapperName).toUpper(); + if (usePySideExtensions()) + s << "static void pysideInitQtMetaTypes();\n"; + + s << "void resetPyMethodCache();\n" + << outdent << "private:\n" << indent; + + if (!metaClass->userAddedPythonOverrides().isEmpty()) { + for (const auto &f : metaClass->userAddedPythonOverrides()) + s << functionSignature(f, {}, {}, Generator::OriginalTypeDescription) << ";\n"; + s << '\n'; } - s << "#endif // SBK_" << outerHeaderGuard << "_H\n\n"; + s << "mutable bool m_PyMethodCache[" << maxOverrides << "];\n" + << outdent << "};\n\n"; } // Write an inline wrapper around a function @@ -227,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,58 +309,52 @@ void HeaderGenerator::writeMemberFunctionWrapper(TextStream &s, if (i > 0) s << ", "; const AbstractMetaArgument &arg = arguments.at(i); - const TypeEntry *enumTypeEntry = nullptr; - if (arg.type().isFlags()) - enumTypeEntry = static_cast<const FlagsTypeEntry *>(arg.type().typeEntry())->originator(); - else if (arg.type().isEnum()) - enumTypeEntry = arg.type().typeEntry(); - if (enumTypeEntry) - s << arg.type().cppSignature() << '(' << arg.name() << ')'; - else + const auto &type = arg.type(); + TypeEntryCPtr enumTypeEntry; + if (type.isFlags()) + enumTypeEntry = std::static_pointer_cast<const FlagsTypeEntry>(type.typeEntry())->originator(); + else if (type.isEnum()) + enumTypeEntry = type.typeEntry(); + if (enumTypeEntry) { + s << type.cppSignature() << '(' << arg.name() << ')'; + } else if (type.passByValue() && type.isUniquePointer()) { + s << stdMove(arg.name()); + } else { s << arg.name(); + } } s << "); }\n"; } -void HeaderGenerator::writeFunction(TextStream &s, const AbstractMetaFunctionCPtr &func) +void HeaderGenerator::writeFunction(TextStream &s, const AbstractMetaFunctionCPtr &func, + InheritedOverloadSet *inheritedOverloads, + FunctionGeneration generation) const { // do not write copy ctors here. - if (!func->isPrivate() && func->functionType() == AbstractMetaFunction::CopyConstructorFunction) { + if (generation.testFlag(FunctionGenerationFlag::WrapperSpecialCopyConstructor)) { writeCopyCtor(s, func->ownerClass()); return; } - if (func->isUserAdded()) - return; - if (avoidProtectedHack() && func->isProtected() && !func->isConstructor() - && !func->isOperatorOverload()) { - writeMemberFunctionWrapper(s, func, QLatin1String("_protected")); - } + if (generation.testFlag(FunctionGenerationFlag::ProtectedWrapper)) + writeMemberFunctionWrapper(s, func, u"_protected"_s); - // pure virtual functions need a default implementation - const bool notAbstract = !func->isAbstract(); - if ((func->isPrivate() && notAbstract && !func->isVisibilityModifiedToPrivate()) - || (func->isModifiedRemoved() && notAbstract)) + if (generation.testFlag(FunctionGenerationFlag::WrapperConstructor)) { + Options option = func->hasSignatureModifications() + ? Generator::OriginalTypeDescription : Generator::NoOption; + s << functionSignature(func, {}, {}, option) << ";\n"; return; + } - if (avoidProtectedHack() && func->ownerClass()->hasPrivateDestructor() - && (func->isAbstract() || func->isVirtual())) - return; - - if (func->isConstructor() || func->isAbstract() || func->isVirtual()) { - Options virtualOption = Generator::OriginalTypeDescription; - - const bool virtualFunc = func->isVirtual() || func->isAbstract(); - if (!virtualFunc && !func->hasSignatureModifications()) - virtualOption = Generator::NoOption; - - s << functionSignature(func, QString(), QString(), virtualOption); + const bool isVirtual = generation.testFlag(FunctionGenerationFlag::VirtualMethod); + if (isVirtual) { + s << functionSignature(func, {}, {}, Generator::OriginalTypeDescription) + << " override;\n"; + } - if (virtualFunc) - s << " override"; - s << ";\n"; - // Check if this method hide other methods in base classes + // Check if this method hide other methods in base classes + if (isVirtual) { for (const auto &f : func->ownerClass()->functions()) { if (f != func && !f->isConstructor() @@ -308,7 +363,7 @@ void HeaderGenerator::writeFunction(TextStream &s, const AbstractMetaFunctionCPt && !f->isAbstract() && !f->isStatic() && f->name() == func->name()) { - m_inheritedOverloads << f; + inheritedOverloads->insert(f); } } @@ -318,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) { @@ -349,19 +390,20 @@ static const AbstractMetaClass * return nullptr; } -void HeaderGenerator::writeTypeIndexValueLine(TextStream &s, const ApiExtractorResult &api, - const TypeEntry *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 = static_cast<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); @@ -371,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 = static_cast<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()) @@ -392,16 +435,16 @@ 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, metaClass->typeEntry()); + collectTypeEntryTypeIndexes(api, typeEntry, indexValues); } // Format the typedefs for the typedef entries to be generated static void formatTypeDefEntries(TextStream &s) { - QList<const TypedefEntry *> entries; + QList<TypedefEntryCPtr> entries; const auto typeDbEntries = TypeDatabase::instance()->typedefEntries(); for (auto it = typeDbEntries.cbegin(), end = typeDbEntries.cend(); it != end; ++it) { if (it.value()->generateCode() != 0) @@ -410,76 +453,262 @@ static void formatTypeDefEntries(TextStream &s) if (entries.isEmpty()) return; s << "\n// typedef entries\n"; - for (const auto e : entries) { + for (const auto &e : entries) { const QString name = e->qualifiedCppName(); // Fixme: simplify by using nested namespaces in C++ 17. const auto components = QStringView{name}.split(u"::"); - const int nameSpaceCount = components.size() - 1; - for (int n = 0; n < nameSpaceCount; ++n) + const auto nameSpaceCount = components.size() - 1; + for (qsizetype n = 0; n < nameSpaceCount; ++n) s << "namespace " << components.at(n) << " {\n"; s << "using " << components.constLast() << " = " << e->sourceType() << ";\n"; - for (int n = 0; n < nameSpaceCount; ++n) + for (qsizetype n = 0; n < nameSpaceCount; ++n) s << "}\n"; } s << '\n'; } +// Helpers for forward-declaring classes in the module header for the +// specialization of the SbkType template functions. This is possible if the +// class does not have inner types or enums which need to be known. +static bool canForwardDeclare(const AbstractMetaClassCPtr &c) +{ + if (c->isNamespace() || !c->enums().isEmpty() + || !c->innerClasses().isEmpty() || c->isTypeDef()) { + return false; + } + if (auto encl = c->enclosingClass()) + return encl->isNamespace(); + return true; +} -bool HeaderGenerator::finishGeneration() +static void writeForwardDeclaration(TextStream &s, const AbstractMetaClassCPtr &c) { - // Generate the main header for this module. - // This header should be included by binding modules - // extendind on top of this one. - QSet<Include> includes; - QSet<Include> privateIncludes; - StringStream macrosStream(TextStream::Language::Cpp); + Q_ASSERT(!c->isNamespace()); + const bool isStruct = c->attributes().testFlag(AbstractMetaClass::Struct); + s << (isStruct ? "struct " : "class "); + // Do not use name as this can be modified/renamed for target lang. + const QString qualifiedCppName = c->qualifiedCppName(); + const auto lastQualifier = qualifiedCppName.lastIndexOf(u':'); + if (lastQualifier != -1) + s << QStringView{qualifiedCppName}.mid(lastQualifier + 1); + else + s << qualifiedCppName; + s << ";\n"; +} - const auto snips = TypeDatabase::instance()->defaultTypeSystemType()->codeSnips(); - if (!snips.isEmpty()) { - writeCodeSnips(macrosStream, snips, TypeSystem::CodeSnipPositionDeclaration, - TypeSystem::TargetLangCode); +// Helpers for writing out namespaces hierarchically when writing class +// forward declarations to the module header. Ensure inline namespaces +// are marked as such (else clang complains) and namespaces are ordered. +struct NameSpace { + AbstractMetaClassCPtr nameSpace; + AbstractMetaClassCList classes; +}; + +static bool operator<(const NameSpace &n1, const NameSpace &n2) +{ + return n1.nameSpace->name() < n2.nameSpace->name(); +} + +using NameSpaces = QList<NameSpace>; + +static qsizetype indexOf(const NameSpaces &nsps, const AbstractMetaClassCPtr &needle) +{ + for (qsizetype i = 0, count = nsps.size(); i < count; ++i) { + if (nsps.at(i).nameSpace == needle) + return i; } + return -1; +} - macrosStream << "// Type indices\nenum : int {\n"; - AbstractMetaClassCList classList = api().classes(); +static void writeNamespaceForwardDeclarationRecursion(TextStream &s, qsizetype idx, + const NameSpaces &nameSpaces) +{ + auto &root = nameSpaces.at(idx); + s << '\n'; + if (root.nameSpace->isInlineNamespace()) + s << "inline "; + s << "namespace " << root.nameSpace->name() << " {\n" << indent; + for (const auto &c : root.classes) + writeForwardDeclaration(s, c); + + for (qsizetype i = 0, count = nameSpaces.size(); i < count; ++i) { + if (i != idx && nameSpaces.at(i).nameSpace->enclosingClass() == root.nameSpace) + writeNamespaceForwardDeclarationRecursion(s, i, nameSpaces); + } + s << outdent << "}\n"; +} - std::sort(classList.begin(), classList.end(), [](const AbstractMetaClass *a, const AbstractMetaClass *b) { - return a->typeEntry()->sbkIndex() < b->typeEntry()->sbkIndex(); - }); +static void writeForwardDeclarations(TextStream &s, + const AbstractMetaClassCList &classList) +{ + NameSpaces nameSpaces; + + s << '\n'; + auto typeSystemEntry = TypeDatabase::instance()->defaultTypeSystemType(); + if (!typeSystemEntry->namespaceBegin().isEmpty()) + s << typeSystemEntry->namespaceBegin() << '\n'; + + for (const auto &c : classList) { + if (auto encl = c->enclosingClass()) { + Q_ASSERT(encl->isNamespace()); + auto idx = indexOf(nameSpaces, encl); + if (idx != -1) { + nameSpaces[idx].classes.append(c); + } else { + nameSpaces.append(NameSpace{encl, {c}}); + for (auto enclNsp = encl->enclosingClass(); enclNsp; + enclNsp = enclNsp->enclosingClass()) { + idx = indexOf(nameSpaces, enclNsp); + if (idx == -1) + nameSpaces.append(NameSpace{enclNsp, {}}); + } + } + } else { + writeForwardDeclaration(s, c); + } + } + + std::sort(nameSpaces.begin(), nameSpaces.end()); + + // Recursively write out namespaces starting at the root elements. + for (qsizetype i = 0, count = nameSpaces.size(); i < count; ++i) { + const auto &nsp = nameSpaces.at(i); + if (nsp.nameSpace->enclosingClass() == nullptr) + writeNamespaceForwardDeclarationRecursion(s, i, nameSpaces); + } + + if (!typeSystemEntry->namespaceEnd().isEmpty()) + s << typeSystemEntry->namespaceEnd() << '\n'; +} - for (const AbstractMetaClass *metaClass : classList) - writeTypeIndexValueLines(macrosStream, api(), metaClass); +// Include parameters required for the module/private module header + +using ConditionalIncludeMap = QMap<QString, IncludeGroup>; + +static TextStream &operator<<(TextStream &s, const ConditionalIncludeMap &m) +{ + for (auto it = m.cbegin(), end = m.cend(); it != end; ++it) + s << it.key() << '\n' << it.value() << "#endif\n"; + return s; +} + +struct ModuleHeaderParameters +{ + AbstractMetaClassCList forwardDeclarations; + std::set<Include> includes; + ConditionalIncludeMap conditionalIncludes; + QString typeFunctions; +}; + +HeaderGenerator::IndexValues + HeaderGenerator::collectTypeIndexes(const AbstractMetaClassCList &classList) +{ + IndexValues result; + + for (const auto &metaClass : classList) + collectClassTypeIndexes(api(), metaClass, &result); for (const AbstractMetaEnum &metaEnum : api().globalEnums()) - writeTypeIndexValueLine(macrosStream, api(), metaEnum.typeEntry()); + collectTypeEntryTypeIndexes(api(), metaEnum.typeEntry(), &result); // Write the smart pointer define indexes. int smartPointerCountIndex = getMaxTypeIndex(); int smartPointerCount = 0; - const AbstractMetaTypeList &instantiatedSmartPtrs = instantiatedSmartPointers(); - for (const AbstractMetaType &metaType : instantiatedSmartPtrs) { - QString indexName = getTypeIndexVariableName(metaType); - _writeTypeIndexValue(macrosStream, indexName, smartPointerCountIndex); - macrosStream << ", // " << metaType.cppSignature() << '\n'; + for (const auto &smp : api().instantiatedSmartPointers()) { + QString indexName = getTypeIndexVariableName(smp.type); + result.append({indexName, smartPointerCountIndex, smp.type.cppSignature()}); // Add a the same value for const pointees (shared_ptr<const Foo>). - const auto ptrName = metaType.typeEntry()->entryName(); - int pos = indexName.indexOf(ptrName, 0, Qt::CaseInsensitive); + const auto ptrName = smp.type.typeEntry()->entryName(); + const auto pos = indexName.indexOf(ptrName, 0, Qt::CaseInsensitive); if (pos >= 0) { - indexName.insert(pos + ptrName.size() + 1, QLatin1String("CONST")); - _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; +} + +HeaderGenerator::IndexValues HeaderGenerator::collectConverterIndexes() const +{ + IndexValues result; + const auto &primitives = primitiveTypes(); + int pCount = 0; + for (const auto &ptype : primitives) { + // Note: do not generate indices for typedef'd primitive types as + // they'll use the primitive type converters instead, so we + // don't need to create any other. + if (ptype->generateCode() && ptype->customConversion() != nullptr) + result.append({getTypeIndexVariableName(ptype), pCount++, {}}); + } + + for (const AbstractMetaType &container : api().instantiatedContainers()) { + result.append({getTypeIndexVariableName(container), + pCount++, container.cppSignature()}); + } + + // Because on win32 the compiler will not accept a zero length array. + if (pCount == 0) + pCount++; + result.append({"SBK_"_L1 + moduleName() + "_CONVERTERS_IDX_COUNT"_L1, + pCount, {}}); + return result; +} - _writeTypeIndexValue(macrosStream, - QLatin1String("SBK_") + moduleName() + QLatin1String("_IDX_COUNT"), - getMaxTypeIndex() + smartPointerCount); - macrosStream << "\n};\n"; +// 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 PyTypeObject **" << cppApiVariableName() << ";\n\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"; @@ -487,33 +716,16 @@ bool HeaderGenerator::finishGeneration() // TODO-CONVERTER ------------------------------------------------------------------------------ // Using a counter would not do, a fix must be made to APIExtractor's getTypeIndex(). - macrosStream << "// Converter indices\nenum : int {\n"; - const PrimitiveTypeEntryList &primitives = primitiveTypes(); - int pCount = 0; - for (const PrimitiveTypeEntry *ptype : primitives) { - /* Note: do not generate indices for typedef'd primitive types - * as they'll use the primitive type converters instead, so we - * don't need to create any other. - */ - if (!ptype->generateCode() || !ptype->customConversion()) - continue; - - _writeTypeIndexValueLine(macrosStream, getTypeIndexVariableName(ptype), pCount++); - } - - const AbstractMetaTypeList &containers = instantiatedContainers(); - for (const AbstractMetaType &container : containers) { - _writeTypeIndexValue(macrosStream, getTypeIndexVariableName(container), pCount); - macrosStream << ", // " << container.cppSignature() << '\n'; - pCount++; - } + const auto converterIndexes = collectConverterIndexes(); + macrosStream << "// Converter indices\nenum [[deprecated]] : int {\n"; + for (const auto &ci : converterIndexes) + macrosStream << typeIndexUpper(ci); + macrosStream << "};\n\n"; - // 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"; + macrosStream << "// Converter indices\nenum : int {\n"; + for (const auto &ci : converterIndexes) + macrosStream << ci; + macrosStream << "};\n"; formatTypeDefEntries(macrosStream); @@ -521,36 +733,46 @@ bool HeaderGenerator::finishGeneration() macrosStream << "// Macros for type check\n"; - StringStream typeFunctions(TextStream::Language::Cpp); - StringStream privateTypeFunctions(TextStream::Language::Cpp); - if (usePySideExtensions()) { - typeFunctions << "QT_WARNING_PUSH\n"; - typeFunctions << "QT_WARNING_DISABLE_DEPRECATED\n"; - } + TextStream typeFunctions(¶meters.typeFunctions, TextStream::Language::Cpp); + TextStream privateTypeFunctions(&privateParameters.typeFunctions, TextStream::Language::Cpp); + for (const AbstractMetaEnum &cppEnum : api().globalEnums()) { if (!cppEnum.isAnonymous()) { - includes << 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) { - if (!shouldGenerate(metaClass)) + for (const auto &metaClass : classList) { + const auto classType = metaClass->typeEntry(); + if (!shouldGenerate(classType)) continue; - //Includes - const TypeEntry *classType = metaClass->typeEntry(); + // Includes const bool isPrivate = classType->isPrivate(); - auto &includeList = isPrivate ? privateIncludes : includes; - includeList << classType->include(); + 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; - EnumTypeEntry *enumType = cppEnum.typeEntry(); - includeList << enumType->include(); + if (const auto inc = cppEnum.typeEntry()->include(); inc != classInclude) + par.includes.insert(inc); writeProtectedEnumSurrogate(protEnumsSurrogates, cppEnum); writeSbkTypeFunction(typeFunctionsStr, cppEnum); } @@ -559,19 +781,16 @@ bool HeaderGenerator::finishGeneration() writeSbkTypeFunction(typeFunctionsStr, metaClass); } - for (const AbstractMetaType &metaType : instantiatedSmartPtrs) { - const TypeEntry *classType = metaType.typeEntry(); - includes << classType->include(); - writeSbkTypeFunction(typeFunctions, metaType); + for (const auto &smp : api().instantiatedSmartPointers()) { + parameters.includes.insert(smp.type.typeEntry()->include()); + writeSbkTypeFunction(typeFunctions, smp.type); } - if (usePySideExtensions()) - typeFunctions << "QT_WARNING_POP\n"; - const QString moduleHeaderDir = outputDirectory() + QLatin1Char('/') - + subDirectoryForPackage(packageName()) + QLatin1Char('/'); + const QString moduleHeaderDir = outputDirectory() + u'/' + + subDirectoryForPackage(packageName()) + u'/'; const QString moduleHeaderFileName(moduleHeaderDir + getModuleHeaderFileName()); - QString includeShield(QLatin1String("SBK_") + moduleName().toUpper() + QLatin1String("_PYTHON_H")); + QString includeShield(u"SBK_"_s + moduleName().toUpper() + u"_PYTHON_H"_s); FileOut file(moduleHeaderFileName); TextStream &s = file.stream; @@ -588,34 +807,40 @@ bool HeaderGenerator::finishGeneration() } s << "#include <sbkpython.h>\n"; + s << "#include <sbkmodule.h>\n"; s << "#include <sbkconverter.h>\n"; QStringList requiredTargetImports = TypeDatabase::instance()->requiredTargetImports(); if (!requiredTargetImports.isEmpty()) { s << "// Module Includes\n"; - for (const QString &requiredModule : qAsConst(requiredTargetImports)) + for (const QString &requiredModule : std::as_const(requiredTargetImports)) s << "#include <" << getModuleHeaderFileName(requiredModule) << ">\n"; s<< '\n'; } s << "// Bound library includes\n"; - for (const Include &include : qAsConst(includes)) + for (const Include &include : parameters.includes) s << include; + s << parameters.conditionalIncludes; - if (!primitiveTypes().isEmpty()) { - s << "// Conversion Includes - Primitive Types\n"; - const PrimitiveTypeEntryList &primitiveTypeList = primitiveTypes(); - for (const PrimitiveTypeEntry *ptype : primitiveTypeList) - s << ptype->include(); - s<< '\n'; - } + if (leanHeaders()) { + writeForwardDeclarations(s, parameters.forwardDeclarations); + } else { + if (!primitiveTypes().isEmpty()) { + s << "// Conversion Includes - Primitive Types\n"; + const auto &primitiveTypeList = primitiveTypes(); + for (const auto &ptype : primitiveTypeList) + s << ptype->include(); + s<< '\n'; + } - if (!containerTypes().isEmpty()) { - s << "// Conversion Includes - Container Types\n"; - const ContainerTypeEntryList &containerTypeList = containerTypes(); - for (const ContainerTypeEntry *ctype : containerTypeList) - s << ctype->include(); - s<< '\n'; + if (!containerTypes().isEmpty()) { + s << "// Conversion Includes - Container Types\n"; + const ContainerTypeEntryCList &containerTypeList = containerTypes(); + for (const auto &ctype : containerTypeList) + s << ctype->include(); + s<< '\n'; + } } s << macrosStream.toString() << '\n'; @@ -625,25 +850,21 @@ bool HeaderGenerator::finishGeneration() << protEnumsSurrogates.toString() << '\n'; } - s << "namespace Shiboken\n{\n\n" - << "// PyType functions, to get the PyObjectType for a type T\n" - << typeFunctions.toString() << '\n' - << "} // namespace Shiboken\n\n" - << "#endif // " << includeShield << "\n\n"; + writeTypeFunctions(s, parameters.typeFunctions); + + s << "#endif // " << includeShield << "\n\n"; file.done(); - if (hasPrivateClasses()) { - writePrivateHeader(moduleHeaderDir, includeShield, - privateIncludes, privateTypeFunctions.toString()); - } + if (hasPrivateClasses()) + writePrivateHeader(moduleHeaderDir, includeShield, privateParameters); + return true; } void HeaderGenerator::writePrivateHeader(const QString &moduleHeaderDir, const QString &publicIncludeShield, - const QSet<Include> &privateIncludes, - const QString &privateTypeFunctions) + const ModuleHeaderParameters ¶meters) { // Write includes and type functions of private classes @@ -651,63 +872,90 @@ 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"; ps << "#ifndef " << privateIncludeShield << '\n'; ps << "#define " << privateIncludeShield << "\n\n"; - for (const Include &include : qAsConst(privateIncludes)) + for (const Include &include : parameters.includes) ps << include; + ps << parameters.conditionalIncludes; ps << '\n'; + if (leanHeaders()) + writeForwardDeclarations(ps, parameters.forwardDeclarations); + + writeTypeFunctions(ps, parameters.typeFunctions); + + ps << "#endif\n"; + privateFile.done(); +} + +void HeaderGenerator::writeTypeFunctions(TextStream &s, const QString &typeFunctions) +{ + if (typeFunctions.isEmpty()) + return; + if (usePySideExtensions()) - ps << "QT_WARNING_PUSH\nQT_WARNING_DISABLE_DEPRECATED\n"; + s << "QT_WARNING_PUSH\nQT_WARNING_DISABLE_DEPRECATED\n"; - ps << "namespace Shiboken\n{\n\n" + s << "namespace Shiboken\n{\n\n" << "// PyType functions, to get the PyObjectType for a type T\n" - << privateTypeFunctions << '\n' + << typeFunctions << '\n' << "} // namespace Shiboken\n\n"; if (usePySideExtensions()) - ps << "QT_WARNING_POP\n"; - - ps << "#endif\n"; - privateFile.done(); + 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(); + const auto te = cppEnum.typeEntry(); + ConfigurableScope configScope(s, te); + s << "template<> inline PyTypeObject *SbkType< " << m_gsp << enumName << " >() "; + s << "{ return " << cpythonTypeNameExt(te) << "; }\n"; - s << "template<> inline PyTypeObject *SbkType< ::" << enumName << " >() "; - s << "{ return " << cpythonTypeNameExt(cppEnum.typeEntry()) << "; }\n"; - - FlagsTypeEntry *flag = cppEnum.typeEntry()->flags(); + const auto flag = cppEnum.typeEntry()->flags(); if (flag) { - s << "template<> inline PyTypeObject *SbkType< ::" << flag->name() << " >() " + 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() << " >() " - << "{ return reinterpret_cast<PyTypeObject *>(" << cpythonTypeNameExt(metaType) << "); }\n"; + 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 3d64158e7..03b98e743 100644 --- a/sources/shiboken6/generator/shiboken/headergenerator.h +++ b/sources/shiboken6/generator/shiboken/headergenerator.h @@ -1,39 +1,19 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef HEADERGENERATOR_H #define HEADERGENERATOR_H #include "shibokengenerator.h" +#include "include.h" +#include "modifications_typedefs.h" +#include <QtCore/QList> #include <QtCore/QSet> +struct IndexValue; class AbstractMetaFunction; +struct ModuleHeaderParameters; /** * The HeaderGenerator generate the declarations of C++ bindings classes. @@ -41,36 +21,54 @@ class AbstractMetaFunction; class HeaderGenerator : public ShibokenGenerator { public: - OptionDescriptions options() const override { return OptionDescriptions(); } - const char *name() const override { return "Header generator"; } + static const char *protectedHackDefine; + protected: - QString fileNameSuffix() const override; QString fileNameForContext(const GeneratorContext &context) const override; void generateClass(TextStream &s, const GeneratorContext &classContext) override; bool finishGeneration() override; private: - void writeCopyCtor(TextStream &s, const AbstractMetaClass *metaClass) const; - void writeFunction(TextStream &s, const AbstractMetaFunctionCPtr &func); - 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 TypeEntry *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 QSet<Include> &privateIncludes, - const QString &privateTypeFunctions); + const ModuleHeaderParameters ¶meters); + 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 d30335a4a..c28fcdc1a 100644 --- a/sources/shiboken6/generator/shiboken/overloaddata.cpp +++ b/sources/shiboken6/generator/shiboken/overloaddata.cpp @@ -1,79 +1,52 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include <abstractmetafunction.h> #include <apiextractorresult.h> #include <abstractmetalang.h> +#include <dotview.h> #include <reporthandler.h> -#include <typesystem.h> +#include <complextypeentry.h> +#include <containertypeentry.h> +#include <primitivetypeentry.h> #include <graph.h> #include "overloaddata.h" +#include "messages.h" #include "ctypenames.h" #include "pytypenames.h" #include "textstream.h" +#include "exception.h" + +#include "qtcompat.h" #include <QtCore/QDir> #include <QtCore/QFile> #include <QtCore/QTemporaryFile> #include <algorithm> +#include <utility> -static const TypeEntry *getReferencedTypeEntry(const TypeEntry *typeEntry) -{ - if (typeEntry->isPrimitive()) { - auto pte = dynamic_cast<const PrimitiveTypeEntry *>(typeEntry); - while (pte->referencedTypeEntry()) - pte = pte->referencedTypeEntry(); - typeEntry = pte; - } - return typeEntry; -} +using namespace Qt::StringLiterals; static QString getTypeName(const AbstractMetaType &type) { - const TypeEntry *typeEntry = getReferencedTypeEntry(type.typeEntry()); + TypeEntryCPtr typeEntry = type.typeEntry(); + if (typeEntry->isPrimitive()) + typeEntry = basicReferencedTypeEntry(typeEntry); QString typeName = typeEntry->name(); if (typeEntry->isContainer()) { QStringList types; for (const auto &cType : type.instantiations()) { - const TypeEntry *typeEntry = getReferencedTypeEntry(cType.typeEntry()); + TypeEntryCPtr typeEntry = cType.typeEntry(); + if (typeEntry->isPrimitive()) + typeEntry = basicReferencedTypeEntry(typeEntry); types << typeEntry->name(); } - typeName += QLatin1Char('<') + types.join(QLatin1Char(',')) + QLatin1String(" >"); + typeName += u'<' + types.join(u',') + u" >"_s; } return typeName; } -static QString getTypeName(const OverloadData *ov) -{ - return ov->hasArgumentTypeReplace() ? ov->argumentTypeReplaced() : getTypeName(ov->argType()); -} - static bool typesAreEqual(const AbstractMetaType &typeA, const AbstractMetaType &typeB) { if (typeA.typeEntry() == typeB.typeEntry()) { @@ -81,7 +54,7 @@ static bool typesAreEqual(const AbstractMetaType &typeA, const AbstractMetaType if (typeA.instantiations().size() != typeB.instantiations().size()) return false; - for (int i = 0; i < typeA.instantiations().size(); ++i) { + for (qsizetype i = 0; i < typeA.instantiations().size(); ++i) { if (!typesAreEqual(typeA.instantiations().at(i), typeB.instantiations().at(i))) return false; } @@ -115,48 +88,23 @@ static QString getImplicitConversionTypeName(const AbstractMetaType &containerTy for (const auto &otherType : containerType.instantiations()) types << (otherType == instantiation ? impConv : getTypeName(otherType)); - return containerType.typeEntry()->qualifiedCppName() + QLatin1Char('<') - + types.join(QLatin1String(", ")) + QLatin1String(" >"); + return containerType.typeEntry()->qualifiedCppName() + u'<' + + types.join(u", "_s) + u" >"_s; } -// overloaddata.cpp -static QString msgCyclicDependency(const QString &funcName, const QString &graphName, - const AbstractMetaFunctionCList &cyclic, - const AbstractMetaFunctionCList &involvedConversions) -{ - QString result; - QTextStream str(&result); - str << "Cyclic dependency found on overloaddata for \"" << funcName - << "\" method! The graph boy saved the graph at \"" << QDir::toNativeSeparators(graphName) - << "\". Cyclic functions:"; - for (const auto &c : cyclic) - str << ' ' << c->signature(); - if (const int count = involvedConversions.size()) { - str << " Implicit conversions (" << count << "): "; - for (int i = 0; i < count; ++i) { - if (i) - str << ", \""; - str << involvedConversions.at(i)->signature() << '"'; - if (const AbstractMetaClass *c = involvedConversions.at(i)->implementingClass()) - str << '(' << c->name() << ')'; - } - } - return result; -} - -static inline int overloadNumber(const OverloadData *o) +static inline int overloadNumber(const OverloadDataNodePtr &o) { return o->referenceFunction()->overloadNumber(); } -bool OverloadData::sortByOverloadNumberModification() +static bool sortByOverloadNumberModification(OverloadDataList &list) { - if (std::all_of(m_nextOverloadData.cbegin(), m_nextOverloadData.cend(), - [](const OverloadData *o) { return overloadNumber(o) == TypeSystem::OverloadNumberDefault; })) { + if (std::all_of(list.cbegin(), list.cend(), + [](const OverloadDataNodePtr &o) { return overloadNumber(o) == TypeSystem::OverloadNumberDefault; })) { return false; } - std::stable_sort(m_nextOverloadData.begin(), m_nextOverloadData.end(), - [] (const OverloadData *o1, const OverloadData *o2) { + std::stable_sort(list.begin(), list.end(), + [] (const OverloadDataNodePtr &o1, const OverloadDataNodePtr &o2) { return overloadNumber(o1) < overloadNumber(o2); }); return true; @@ -174,9 +122,10 @@ using OverloadGraph = Graph<QString>; * * Side effects: Modifies m_nextOverloadData */ -void OverloadData::sortNextOverloads() +void OverloadDataRootNode::sortNextOverloads(const ApiExtractorResult &api) { QHash<QString, OverloadDataList> typeToOverloads; + using Edge = std::pair<QString, QString>; bool checkPyObject = false; bool checkPySequence = false; @@ -186,16 +135,16 @@ void OverloadData::sortNextOverloads() // 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 (OverloadData *ov : qAsConst(m_nextOverloadData)) - ov->sortNextOverloads(); + for (const auto &ov : std::as_const(m_children)) + ov->sortNextOverloads(api); - if (m_nextOverloadData.size() <= 1 || sortByOverloadNumberModification()) + if (m_children.size() <= 1 || sortByOverloadNumberModification(m_children)) return; // Populates the OverloadSortData object containing map and reverseMap, to map type names to ids, @@ -203,8 +152,8 @@ void OverloadData::sortNextOverloads() // with graph sorting using integers. OverloadGraph graph; - for (OverloadData *ov : qAsConst(m_nextOverloadData)) { - const QString typeName = getTypeName(ov); + for (const auto &ov : std::as_const(m_children)) { + const QString typeName = getTypeName(ov->modifiedArgType()); auto it = typeToOverloads.find(typeName); if (it == typeToOverloads.end()) { typeToOverloads.insert(typeName, {ov}); @@ -213,15 +162,15 @@ void OverloadData::sortNextOverloads() 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()) { @@ -234,10 +183,10 @@ void OverloadData::sortNextOverloads() // as Point must come before the PointF instantiation, or else list<Point> will never // be called. In the case of primitive types, list<double> must come before list<int>. if (instantiation.isPrimitive() && (signedIntegerPrimitives.contains(instantiation.name()))) { - for (const QString &primitive : qAsConst(nonIntegerPrimitives)) + for (const QString &primitive : std::as_const(nonIntegerPrimitives)) graph.addNode(getImplicitConversionTypeName(ov->argType(), instantiation, nullptr, primitive)); } else { - const auto &funcs = m_api.implicitConversions(instantiation); + const auto &funcs = api.implicitConversions(instantiation); for (const auto &function : funcs) graph.addNode(getImplicitConversionTypeName(ov->argType(), instantiation, function)); } @@ -247,9 +196,9 @@ void OverloadData::sortNextOverloads() // 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) { @@ -258,18 +207,18 @@ void OverloadData::sortNextOverloads() } if (checkPySequence && checkPyObject) - graph.addEdge(cPySequenceT(), cPyObjectT()); + graph.addEdge(cPySequenceT, cPyObjectT); QStringList classesWithIntegerImplicitConversion; AbstractMetaFunctionCList involvedConversions; - for (OverloadData *ov : qAsConst(m_nextOverloadData)) { + for (const auto &ov : std::as_const(m_children)) { const AbstractMetaType &targetType = ov->argType(); - const QString targetTypeEntryName = getTypeName(ov); + const QString targetTypeEntryName = getTypeName(ov->modifiedArgType()); // Process implicit conversions - const auto &functions = m_api.implicitConversions(targetType); + const auto &functions = api.implicitConversions(targetType); for (const auto &function : functions) { QString convertibleType; if (function->isConversionOperator()) @@ -277,7 +226,7 @@ void OverloadData::sortNextOverloads() else convertibleType = getTypeName(function->arguments().constFirst().type()); - if (convertibleType == intT() || convertibleType == unsignedIntT()) + if (convertibleType == intT || convertibleType == unsignedIntT) classesWithIntegerImplicitConversion << targetTypeEntryName; if (!graph.hasNode(convertibleType)) @@ -292,10 +241,12 @@ void OverloadData::sortNextOverloads() // Process inheritance relationships if (targetType.isValue() || targetType.isObject()) { - auto metaClass = AbstractMetaClass::findClass(m_api.classes(), - targetType.typeEntry()); - const AbstractMetaClassList &ancestors = metaClass->allTypeSystemAncestors(); - for (const AbstractMetaClass *ancestor : ancestors) { + const auto te = targetType.typeEntry(); + auto metaClass = AbstractMetaClass::findClass(api.classes(), te); + if (!metaClass) + throw Exception(msgArgumentClassNotFound(m_overloads.constFirst(), te)); + const auto &ancestors = metaClass->allTypeSystemAncestors(); + for (const auto &ancestor : ancestors) { QString ancestorTypeName = ancestor->typeEntry()->name(); if (!graph.hasNode(ancestorTypeName)) continue; @@ -312,7 +263,7 @@ void OverloadData::sortNextOverloads() graph.addEdge(convertible, targetTypeEntryName); if (instantiation.isPrimitive() && (signedIntegerPrimitives.contains(instantiation.name()))) { - for (const QString &primitive : qAsConst(nonIntegerPrimitives)) { + for (const QString &primitive : std::as_const(nonIntegerPrimitives)) { QString convertibleTypeName = getImplicitConversionTypeName(ov->argType(), instantiation, nullptr, primitive); // Avoid cyclic dependency. @@ -321,7 +272,7 @@ void OverloadData::sortNextOverloads() } } else { - const auto &funcs = m_api.implicitConversions(instantiation); + const auto &funcs = api.implicitConversions(instantiation); for (const auto &function : funcs) { QString convertibleTypeName = getImplicitConversionTypeName(ov->argType(), instantiation, function); @@ -337,28 +288,28 @@ void OverloadData::sortNextOverloads() 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()) { @@ -369,25 +320,36 @@ void OverloadData::sortNextOverloads() } // 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 (OverloadData *ov : qAsConst(m_nextOverloadData)) { + for (const auto &ov : std::as_const(m_children)) { const AbstractMetaType &targetType = ov->argType(); if (!targetType.isEnum()) continue; QString targetTypeEntryName = getTypeName(targetType); // Enum values must precede types implicitly convertible from "int" or "unsigned int". - for (const QString &implicitFromInt : qAsConst(classesWithIntegerImplicitConversion)) + for (const QString &implicitFromInt : std::as_const(classesWithIntegerImplicitConversion)) graph.addEdge(targetTypeEntryName, implicitFromInt); } // Special case for double(int i) (not tracked by m_generator->implicitConversions - for (const QString &signedIntegerName : qAsConst(signedIntegerPrimitives)) { + for (const QString &signedIntegerName : std::as_const(signedIntegerPrimitives)) { if (graph.hasNode(signedIntegerName)) { - for (const QString &nonIntegerName : qAsConst(nonIntegerPrimitives)) { + for (const QString &nonIntegerName : std::as_const(nonIntegerPrimitives)) { if (graph.hasNode(nonIntegerName)) graph.addEdge(nonIntegerName, signedIntegerName); } @@ -398,11 +360,11 @@ void OverloadData::sortNextOverloads() const auto unmappedResult = graph.topologicalSort(); if (!unmappedResult.isValid()) { QString funcName = referenceFunction()->name(); - if (referenceFunction()->ownerClass()) - funcName.prepend(referenceFunction()->ownerClass()->name() + QLatin1Char('.')); + if (auto owner = referenceFunction()->ownerClass()) + funcName.prepend(owner->name() + u'.'); // Dump overload graph - QString graphName = QDir::tempPath() + QLatin1Char('/') + funcName + QLatin1String(".dot"); + QString graphName = QDir::tempPath() + u'/' + funcName + u".dot"_s; graph.dumpDot(graphName, [] (const QString &n) { return n; }); AbstractMetaFunctionCList cyclic; for (const auto &typeName : unmappedResult.cyclic) { @@ -413,21 +375,45 @@ void OverloadData::sortNextOverloads() qCWarning(lcShiboken, "%s", qPrintable(msgCyclicDependency(funcName, graphName, cyclic, involvedConversions))); } - m_nextOverloadData.clear(); + m_children.clear(); for (const auto &typeName : unmappedResult.result) { const auto oit = typeToOverloads.constFind(typeName); if (oit != typeToOverloads.cend()) { std::copy(oit.value().crbegin(), oit.value().crend(), - std::back_inserter(m_nextOverloadData)); + std::back_inserter(m_children)); + } + } +} + +// Determine the minimum (first default argument)/maximum arguments (size) +// of a function (taking into account the removed arguments). +static std::pair<int, int> getMinMaxArgs(const AbstractMetaFunctionCPtr &func) +{ + int defaultValueIndex = -1; + const auto &arguments = func->arguments(); + int argIndex = 0; + for (const auto &arg : arguments) { + if (!arg.isModifiedRemoved()) { + if (defaultValueIndex < 0 && arg.hasDefaultValueExpression()) + defaultValueIndex = argIndex; + ++argIndex; } } + const int maxArgs = argIndex; + const int minArgs = defaultValueIndex >= 0 ? defaultValueIndex : maxArgs; + return {minArgs, maxArgs}; +} + +const OverloadDataRootNode *OverloadDataNode::parent() const +{ + return m_parent; } /** * Root constructor for OverloadData * * This constructor receives the list of overloads for a given function and iterates generating - * the graph of OverloadData instances. Each OverloadData instance references an argument/type + * the graph of OverloadData instances. Each OverloadDataNode instance references an argument/type * combination. * * Example: @@ -441,86 +427,58 @@ void OverloadData::sortNextOverloads() * */ OverloadData::OverloadData(const AbstractMetaFunctionCList &overloads, - const ApiExtractorResult &api) - : m_minArgs(256), m_maxArgs(0), m_argPos(-1), m_argType(nullptr), - m_headOverloadData(this), m_previousOverloadData(nullptr), - m_api(api) + const ApiExtractorResult &api) : + OverloadDataRootNode(overloads) { for (const auto &func : overloads) { - m_overloads.append(func); - int argSize = func->arguments().size() - numberOfRemovedArguments(func); - if (m_minArgs > argSize) - m_minArgs = argSize; - else if (m_maxArgs < argSize) - m_maxArgs = argSize; - OverloadData *currentOverloadData = this; + const auto minMaxArgs = getMinMaxArgs(func); + if (minMaxArgs.first < m_minArgs) + m_minArgs = minMaxArgs.first; + if (minMaxArgs.second > m_maxArgs) + m_maxArgs = minMaxArgs.second; + OverloadDataRootNode *currentOverloadData = this; const AbstractMetaArgumentList &arguments = func->arguments(); for (const AbstractMetaArgument &arg : arguments) { - if (func->argumentRemoved(arg.argumentIndex() + 1)) - continue; - currentOverloadData = currentOverloadData->addOverloadData(func, arg); + if (!arg.isModifiedRemoved()) + currentOverloadData = currentOverloadData->addOverloadDataNode(func, arg); } } // Sort the overload possibilities so that the overload decisor code goes for the most // important cases first, based on the topological order of the implicit conversions - sortNextOverloads(); - - // Fix minArgs - if (minArgs() > maxArgs()) - m_headOverloadData->m_minArgs = maxArgs(); + sortNextOverloads(api); } -OverloadData::OverloadData(OverloadData *headOverloadData, const AbstractMetaFunctionCPtr &func, - const AbstractMetaType &argType, int argPos, - const ApiExtractorResult &api) : - m_minArgs(256), m_maxArgs(0), m_argPos(argPos), m_argType(argType), - m_headOverloadData(headOverloadData), m_previousOverloadData(nullptr), m_api(api) +OverloadDataNode::OverloadDataNode(const AbstractMetaFunctionCPtr &func, + OverloadDataRootNode *parent, + const AbstractMetaArgument &argument, + int argPos, + const QString argTypeReplaced) : + m_argument(argument), + m_argTypeReplaced(argTypeReplaced), + m_parent(parent), + m_argPos(argPos) { if (func) this->addOverload(func); } -void OverloadData::addOverload(const AbstractMetaFunctionCPtr &func) +void OverloadDataNode::addOverload(const AbstractMetaFunctionCPtr &func) { - int origNumArgs = func->arguments().size(); - int removed = numberOfRemovedArguments(func); - int numArgs = origNumArgs - removed; - - if (numArgs > m_headOverloadData->m_maxArgs) - m_headOverloadData->m_maxArgs = numArgs; - - if (numArgs < m_headOverloadData->m_minArgs) - m_headOverloadData->m_minArgs = numArgs; - - for (int i = 0; m_headOverloadData->m_minArgs > 0 && i < origNumArgs; i++) { - if (func->argumentRemoved(i + 1)) - continue; - if (func->arguments().at(i).hasDefaultValueExpression()) { - int fixedArgIndex = i - removed; - if (fixedArgIndex < m_headOverloadData->m_minArgs) - m_headOverloadData->m_minArgs = fixedArgIndex; - } - } - m_overloads.append(func); } -OverloadData *OverloadData::addOverloadData(const AbstractMetaFunctionCPtr &func, - const AbstractMetaArgument &arg) +OverloadDataNode *OverloadDataRootNode::addOverloadDataNode(const AbstractMetaFunctionCPtr &func, + const AbstractMetaArgument &arg) { - const AbstractMetaType &argType = arg.type(); - OverloadData *overloadData = nullptr; + OverloadDataNodePtr overloadData; if (!func->isOperatorOverload()) { - for (OverloadData *tmp : qAsConst(m_nextOverloadData)) { + for (const auto &tmp : std::as_const(m_children)) { // TODO: 'const char *', 'char *' and 'char' will have the same TypeEntry? // If an argument have a type replacement, then we should create a new overloaddata // for it, unless the next argument also have a identical type replacement. - QString replacedArg = func->typeReplaced(tmp->m_argPos + 1); - bool argsReplaced = !replacedArg.isEmpty() || !tmp->m_argTypeReplaced.isEmpty(); - if ((!argsReplaced && typesAreEqual(tmp->m_argType, argType)) - || (argsReplaced && replacedArg == tmp->argumentTypeReplaced())) { + if (typesAreEqual(tmp->modifiedArgType(), arg.modifiedType())) { tmp->addOverload(func); overloadData = tmp; } @@ -528,34 +486,26 @@ OverloadData *OverloadData::addOverloadData(const AbstractMetaFunctionCPtr &func } if (!overloadData) { - overloadData = new OverloadData(m_headOverloadData, func, argType, m_argPos + 1, m_api); - overloadData->m_previousOverloadData = this; - QString typeReplaced = func->typeReplaced(arg.argumentIndex() + 1); - - if (!typeReplaced.isEmpty()) - overloadData->m_argTypeReplaced = typeReplaced; - m_nextOverloadData.append(overloadData); + const int argpos = argPos() + 1; + overloadData.reset(new OverloadDataNode(func, this, arg, argpos)); + m_children.append(overloadData); } - return overloadData; + return overloadData.get(); } -QStringList OverloadData::returnTypes() const +bool OverloadData::hasNonVoidReturnType() const { - QSet<QString> retTypes; for (const auto &func : m_overloads) { - if (!func->typeReplaced(0).isEmpty()) - retTypes << func->typeReplaced(0); - else if (!func->argumentRemoved(0)) - retTypes << func->type().cppSignature(); + if (func->isTypeModified()) { + if (func->modifiedTypeName() != u"void") + return true; + } else { + if (!func->argumentRemoved(0) && !func->type().isVoid()) + return true; + } } - return retTypes.values(); -} - -bool OverloadData::hasNonVoidReturnType() const -{ - QStringList retTypes = returnTypes(); - return !retTypes.contains(QLatin1String("void")) || retTypes.size() > 1; + return false; } bool OverloadData::hasVarargs() const @@ -568,28 +518,37 @@ bool OverloadData::hasVarargs() const return false; } -bool OverloadData::hasAllowThread() const +bool OverloadData::hasStaticFunction(const AbstractMetaFunctionCList &overloads) +{ + for (const auto &func : overloads) { + if (func->isStatic()) + return true; + } + return false; +} + +bool OverloadData::hasStaticFunction() const { for (const auto &func : m_overloads) { - if (func->allowThread()) + if (func->isStatic()) return true; } return false; } -bool OverloadData::hasStaticFunction(const AbstractMetaFunctionCList &overloads) +bool OverloadData::hasClassMethod(const AbstractMetaFunctionCList &overloads) { for (const auto &func : overloads) { - if (func->isStatic()) + if (func->isClassMethod()) return true; } return false; } -bool OverloadData::hasStaticFunction() const +bool OverloadData::hasClassMethod() const { for (const auto &func : m_overloads) { - if (func->isStatic()) + if (func->isClassMethod()) return true; } return false; @@ -623,20 +582,27 @@ bool OverloadData::hasStaticAndInstanceFunctions() const return OverloadData::hasStaticFunction() && OverloadData::hasInstanceFunction(); } -AbstractMetaFunctionCPtr OverloadData::referenceFunction() const +OverloadDataRootNode::OverloadDataRootNode(const AbstractMetaFunctionCList &o) : + m_overloads(o) +{ +} + +OverloadDataRootNode::~OverloadDataRootNode() = default; + +AbstractMetaFunctionCPtr OverloadDataRootNode::referenceFunction() const { return m_overloads.constFirst(); } -const AbstractMetaArgument *OverloadData::argument(const AbstractMetaFunctionCPtr &func) const +const AbstractMetaArgument *OverloadDataNode::overloadArgument(const AbstractMetaFunctionCPtr &func) const { - if (isHeadOverloadData() || !m_overloads.contains(func)) + if (isRoot() || !m_overloads.contains(func)) return nullptr; int argPos = 0; int removed = 0; for (int i = 0; argPos <= m_argPos; i++) { - if (func->argumentRemoved(i + 1)) + if (func->arguments().at(i).isModifiedRemoved()) removed++; else argPos++; @@ -645,89 +611,54 @@ const AbstractMetaArgument *OverloadData::argument(const AbstractMetaFunctionCPt return &func->arguments().at(m_argPos + removed); } -OverloadDataList OverloadData::overloadDataOnPosition(OverloadData *overloadData, int argPos) const -{ - OverloadDataList overloadDataList; - if (overloadData->argPos() == argPos) { - overloadDataList.append(overloadData); - } else if (overloadData->argPos() < argPos) { - const OverloadDataList &data = overloadData->nextOverloadData(); - for (OverloadData *pd : data) - overloadDataList += overloadDataOnPosition(pd, argPos); - } - return overloadDataList; -} - -OverloadDataList OverloadData::overloadDataOnPosition(int argPos) const +bool OverloadDataRootNode::nextArgumentHasDefaultValue() const { - OverloadDataList overloadDataList; - overloadDataList += overloadDataOnPosition(m_headOverloadData, argPos); - return overloadDataList; -} - -bool OverloadData::nextArgumentHasDefaultValue() const -{ - for (OverloadData *overloadData : m_nextOverloadData) { - if (!overloadData->getFunctionWithDefaultValue().isNull()) + for (const auto &overloadData : m_children) { + if (overloadData->getFunctionWithDefaultValue()) return true; } return false; } -static OverloadData *_findNextArgWithDefault(OverloadData *overloadData) +static const OverloadDataRootNode *_findNextArgWithDefault(const OverloadDataRootNode *overloadData) { - if (!overloadData->getFunctionWithDefaultValue().isNull()) + if (overloadData->getFunctionWithDefaultValue()) return overloadData; - OverloadData *result = nullptr; - const OverloadDataList &data = overloadData->nextOverloadData(); - for (OverloadData *odata : data) { - OverloadData *tmp = _findNextArgWithDefault(odata); + const OverloadDataRootNode *result = nullptr; + const OverloadDataList &data = overloadData->children(); + for (const auto &odata : data) { + const auto *tmp = _findNextArgWithDefault(odata.get()); if (!result || (tmp && result->argPos() > tmp->argPos())) result = tmp; } return result; } -OverloadData *OverloadData::findNextArgWithDefault() +const OverloadDataRootNode *OverloadDataRootNode::findNextArgWithDefault() const { return _findNextArgWithDefault(this); } -bool OverloadData::isFinalOccurrence(const AbstractMetaFunctionCPtr &func) const +bool OverloadDataRootNode::isFinalOccurrence(const AbstractMetaFunctionCPtr &func) const { - for (const OverloadData *pd : m_nextOverloadData) { + for (const auto &pd : m_children) { if (pd->overloads().contains(func)) return false; } return true; } -AbstractMetaFunctionCList OverloadData::overloadsWithoutRepetition() const -{ - AbstractMetaFunctionCList overloads = m_overloads; - for (const auto &func : m_overloads) { - if (func->minimalSignature().endsWith(QLatin1String("const"))) - continue; - for (const auto &f : qAsConst(overloads)) { - if ((func->minimalSignature() + QLatin1String("const")) == f->minimalSignature()) { - overloads.removeOne(f); - break; - } - } - } - return overloads; -} - -AbstractMetaFunctionCPtr OverloadData::getFunctionWithDefaultValue() const +AbstractMetaFunctionCPtr OverloadDataRootNode::getFunctionWithDefaultValue() const { + const qsizetype argpos = argPos(); for (const auto &func : m_overloads) { - int removedArgs = 0; - for (int i = 0; i <= m_argPos + removedArgs; i++) { - if (func->argumentRemoved(i + 1)) + qsizetype removedArgs = 0; + for (qsizetype i = 0; i <= argpos + removedArgs; i++) { + if (func->arguments().at(i).isModifiedRemoved()) removedArgs++; } - if (func->arguments().at(m_argPos + removedArgs).hasDefaultValueExpression()) + if (func->arguments().at(argpos + removedArgs).hasDefaultValueExpression()) return func; } return {}; @@ -737,11 +668,11 @@ QList<int> OverloadData::invalidArgumentLengths() const { QSet<int> validArgLengths; - for (const auto &func : qAsConst(m_headOverloadData->m_overloads)) { + for (const auto &func : m_overloads) { const AbstractMetaArgumentList args = func->arguments(); int offset = 0; - for (int i = 0; i < args.size(); ++i) { - if (func->argumentRemoved(i+1)) { + for (qsizetype i = 0; i < args.size(); ++i) { + if (func->arguments().at(i).isModifiedRemoved()) { offset++; } else { if (args.at(i).hasDefaultValueExpression()) @@ -752,7 +683,7 @@ QList<int> OverloadData::invalidArgumentLengths() const } QList<int> invalidArgLengths; - for (int i = minArgs() + 1; i < maxArgs(); i++) { + for (int i = m_minArgs + 1; i < m_maxArgs; i++) { if (!validArgLengths.contains(i)) invalidArgLengths.append(i); } @@ -760,196 +691,204 @@ QList<int> OverloadData::invalidArgumentLengths() const return invalidArgLengths; } -int OverloadData::numberOfRemovedArguments(const AbstractMetaFunctionCPtr &func, int finalArgPos) +int OverloadData::numberOfRemovedArguments(const AbstractMetaFunctionCPtr &func) { - int removed = 0; - if (finalArgPos < 0) { - for (int i = 0; i < func->arguments().size(); i++) { - if (func->argumentRemoved(i + 1)) - removed++; - } - } else { - for (int i = 0; i < finalArgPos + removed; i++) { - if (func->argumentRemoved(i + 1)) - removed++; - } - } - return removed; + return std::count_if(func->arguments().cbegin(), func->arguments().cend(), + [](const AbstractMetaArgument &a) { return a.isModifiedRemoved(); }); } -bool OverloadData::isSingleArgument(const AbstractMetaFunctionCList &overloads) +int OverloadData::numberOfRemovedArguments(const AbstractMetaFunctionCPtr &func, int finalArgPos) { - bool singleArgument = true; - for (const auto &func : overloads) { - if (func->arguments().size() - numberOfRemovedArguments(func) != 1) { - singleArgument = false; - break; - } + Q_ASSERT(finalArgPos >= 0); + int removed = 0; + const auto size = func->arguments().size(); + for (qsizetype i = 0; i < qMin(size, qsizetype(finalArgPos + removed)); ++i) { + if (func->arguments().at(i).isModifiedRemoved()) + ++removed; } - return singleArgument; + return removed; } void OverloadData::dumpGraph(const QString &filename) const { QFile file(filename); if (file.open(QFile::WriteOnly)) { - TextStream s(&file); - s << m_headOverloadData->dumpGraph(); + QTextStream s(&file); + dumpRootGraph(s, m_minArgs, m_maxArgs); } } +QString OverloadData::dumpGraph() const +{ + QString result; + QTextStream s(&result); + dumpRootGraph(s, m_minArgs, m_maxArgs); + return result; +} + +bool OverloadData::showGraph() const +{ + return showDotGraph(referenceFunction()->name(), dumpGraph()); +} + static inline QString toHtml(QString s) { - s.replace(QLatin1Char('<'), QLatin1String("<")); - s.replace(QLatin1Char('>'), QLatin1String(">")); - s.replace(QLatin1Char('&'), QLatin1String("&")); + s.replace(u'<', u"<"_s); + s.replace(u'>', u">"_s); + s.replace(u'&', u"&"_s); return s; } -QString OverloadData::dumpGraph() const +void OverloadDataRootNode::dumpRootGraph(QTextStream &s, int minArgs, int maxArgs) const { - QString result; - QTextStream s(&result); - if (m_argPos == -1) { - const auto rfunc = referenceFunction(); - s << "digraph OverloadedFunction {\n"; - s << " graph [fontsize=12 fontname=freemono labelloc=t splines=true overlap=false rankdir=LR];\n"; - - // Shows all function signatures - s << "legend [fontsize=9 fontname=freemono shape=rect label=\""; - for (const auto &func : m_overloads) { - s << "f" << functionNumber(func) << " : " - << toHtml(func->type().cppSignature()) - << ' ' << toHtml(func->minimalSignature()) << "\\l"; - } - s << "\"];\n"; - - // Function box title - s << " \"" << rfunc->name() << "\" [shape=plaintext style=\"filled,bold\" margin=0 fontname=freemono fillcolor=white penwidth=1 "; - s << "label=<<table border=\"0\" cellborder=\"0\" cellpadding=\"3\" bgcolor=\"white\">"; - s << "<tr><td bgcolor=\"black\" align=\"center\" cellpadding=\"6\" colspan=\"2\"><font color=\"white\">"; - if (rfunc->ownerClass()) - s << rfunc->ownerClass()->name() << "::"; - s << toHtml(rfunc->name()) << "</font>"; - if (rfunc->isVirtual()) { - s << "<br/><font color=\"white\" point-size=\"10\"><<"; - if (rfunc->isAbstract()) - s << "pure "; - s << "virtual>></font>"; - } - s << "</td></tr>"; + const auto rfunc = referenceFunction(); + s << "digraph OverloadedFunction {\n"; + s << " graph [fontsize=12 fontname=freemono labelloc=t splines=true overlap=false rankdir=LR];\n"; - // Function return type - s << "<tr><td bgcolor=\"gray\" align=\"right\">original type</td><td bgcolor=\"gray\" align=\"left\">" - << toHtml(rfunc->type().cppSignature()) - << "</td></tr>"; + // Shows all function signatures + s << "legend [fontsize=9 fontname=freemono shape=rect label=\""; + for (const auto &func : m_overloads) { + s << "f" << functionNumber(func) << " : " + << toHtml(func->type().cppSignature()) + << ' ' << toHtml(func->minimalSignature()) << "\\l"; + } + s << "\"];\n"; + + // Function box title + s << " \"" << rfunc->name() << "\" [shape=plaintext style=\"filled,bold\" margin=0 fontname=freemono fillcolor=white penwidth=1 "; + s << "label=<<table border=\"0\" cellborder=\"0\" cellpadding=\"3\" bgcolor=\"white\">"; + s << "<tr><td bgcolor=\"black\" align=\"center\" cellpadding=\"6\" colspan=\"2\"><font color=\"white\">"; + if (rfunc->ownerClass()) + s << rfunc->ownerClass()->name() << "::"; + s << toHtml(rfunc->name()) << "</font>"; + if (rfunc->isVirtual()) { + s << "<br/><font color=\"white\" point-size=\"10\"><<"; + if (rfunc->isAbstract()) + s << "pure "; + s << "virtual>></font>"; + } + s << "</td></tr>"; + + // Function return type + s << "<tr><td bgcolor=\"gray\" align=\"right\">original type</td><td bgcolor=\"gray\" align=\"left\">" + << toHtml(rfunc->type().cppSignature()) + << "</td></tr>"; + + // Shows type changes for all function signatures + for (const auto &func : m_overloads) { + if (!func->isTypeModified()) + continue; + s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << functionNumber(func); + s << "-type</td><td bgcolor=\"gray\" align=\"left\">"; + s << toHtml(func->modifiedTypeName()) << "</td></tr>"; + } - // Shows type changes for all function signatures - for (const auto &func : m_overloads) { - if (func->typeReplaced(0).isEmpty()) - continue; - s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << functionNumber(func); - s << "-type</td><td bgcolor=\"gray\" align=\"left\">"; - s << toHtml(func->typeReplaced(0)) << "</td></tr>"; - } + // Minimum and maximum number of arguments + s << "<tr><td bgcolor=\"gray\" align=\"right\">minArgs</td><td bgcolor=\"gray\" align=\"left\">"; + s << minArgs << "</td></tr>"; + s << "<tr><td bgcolor=\"gray\" align=\"right\">maxArgs</td><td bgcolor=\"gray\" align=\"left\">"; + s << maxArgs << "</td></tr>"; - // Minimum and maximum number of arguments - s << "<tr><td bgcolor=\"gray\" align=\"right\">minArgs</td><td bgcolor=\"gray\" align=\"left\">"; - s << minArgs() << "</td></tr>"; - s << "<tr><td bgcolor=\"gray\" align=\"right\">maxArgs</td><td bgcolor=\"gray\" align=\"left\">"; - s << maxArgs() << "</td></tr>"; - - if (rfunc->ownerClass()) { - if (rfunc->implementingClass() != rfunc->ownerClass()) - s << "<tr><td align=\"right\">implementor</td><td align=\"left\">" << rfunc->implementingClass()->name() << "</td></tr>"; - if (rfunc->declaringClass() != rfunc->ownerClass() && rfunc->declaringClass() != rfunc->implementingClass()) - s << "<tr><td align=\"right\">declarator</td><td align=\"left\">" << rfunc->declaringClass()->name() << "</td></tr>"; - } + if (rfunc->ownerClass()) { + if (rfunc->implementingClass() != rfunc->ownerClass()) + s << "<tr><td align=\"right\">implementor</td><td align=\"left\">" << rfunc->implementingClass()->name() << "</td></tr>"; + if (rfunc->declaringClass() != rfunc->ownerClass() && rfunc->declaringClass() != rfunc->implementingClass()) + s << "<tr><td align=\"right\">declarator</td><td align=\"left\">" << rfunc->declaringClass()->name() << "</td></tr>"; + } - // Overloads for the signature to present point - s << "<tr><td bgcolor=\"gray\" align=\"right\">overloads</td><td bgcolor=\"gray\" align=\"left\">"; - for (const auto &func : m_overloads) - s << 'f' << functionNumber(func) << ' '; - s << "</td></tr>"; + // Overloads for the signature to present point + s << "<tr><td bgcolor=\"gray\" align=\"right\">overloads</td><td bgcolor=\"gray\" align=\"left\">"; + for (const auto &func : m_overloads) + s << 'f' << functionNumber(func) << ' '; + s << "</td></tr>"; - s << "</table>> ];\n"; + s << "</table>> ];\n"; - for (const OverloadData *pd : m_nextOverloadData) - s << " \"" << rfunc->name() << "\" -> " << pd->dumpGraph(); + for (const auto &pd : m_children) { + s << " \"" << rfunc->name() << "\" -> "; + pd->dumpNodeGraph(s); + } - s << "}\n"; - } else { - QString argId = QLatin1String("arg_") + QString::number(quintptr(this)); - s << argId << ";\n"; + s << "}\n"; +} - s << " \"" << argId << "\" [shape=\"plaintext\" style=\"filled,bold\" margin=\"0\" fontname=\"freemono\" fillcolor=\"white\" penwidth=1 "; - s << "label=<<table border=\"0\" cellborder=\"0\" cellpadding=\"3\" bgcolor=\"white\">"; +void OverloadDataNode::dumpNodeGraph(QTextStream &s) const +{ + QString argId = u"arg_"_s + QString::number(quintptr(this)); + s << argId << ";\n"; - // Argument box title - s << "<tr><td bgcolor=\"black\" align=\"left\" cellpadding=\"2\" colspan=\"2\">"; - s << "<font color=\"white\" point-size=\"11\">arg #" << argPos() << "</font></td></tr>"; + s << " \"" << argId << "\" [shape=\"plaintext\" style=\"filled,bold\" margin=\"0\" fontname=\"freemono\" fillcolor=\"white\" penwidth=1 "; + s << "label=<<table border=\"0\" cellborder=\"0\" cellpadding=\"3\" bgcolor=\"white\">"; - // Argument type information - QString type = hasArgumentTypeReplace() ? argumentTypeReplaced() : argType().cppSignature(); - s << "<tr><td bgcolor=\"gray\" align=\"right\">type</td><td bgcolor=\"gray\" align=\"left\">"; - s << toHtml(type) << "</td></tr>"; - if (hasArgumentTypeReplace()) { - s << "<tr><td bgcolor=\"gray\" align=\"right\">orig. type</td><td bgcolor=\"gray\" align=\"left\">"; - s << toHtml(argType().cppSignature()) << "</td></tr>"; - } + // Argument box title + s << "<tr><td bgcolor=\"black\" align=\"left\" cellpadding=\"2\" colspan=\"2\">"; + s << "<font color=\"white\" point-size=\"11\">arg #" << argPos() << "</font></td></tr>"; - // Overloads for the signature to present point - s << "<tr><td bgcolor=\"gray\" align=\"right\">overloads</td><td bgcolor=\"gray\" align=\"left\">"; - for (const auto &func : m_overloads) - s << 'f' << functionNumber(func) << ' '; - s << "</td></tr>"; + // Argument type information + const QString type = modifiedArgType().cppSignature(); + s << "<tr><td bgcolor=\"gray\" align=\"right\">type</td><td bgcolor=\"gray\" align=\"left\">"; + s << toHtml(type) << "</td></tr>"; + if (isTypeModified()) { + s << "<tr><td bgcolor=\"gray\" align=\"right\">orig. type</td><td bgcolor=\"gray\" align=\"left\">"; + s << toHtml(argType().cppSignature()) << "</td></tr>"; + } - // Show default values (original and modified) for various functions - for (const auto &func : m_overloads) { - const AbstractMetaArgument *arg = argument(func); - if (!arg) - continue; - QString argDefault = arg->defaultValueExpression(); - if (!argDefault.isEmpty() || - argDefault != arg->originalDefaultValueExpression()) { - s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << functionNumber(func); - s << "-default</td><td bgcolor=\"gray\" align=\"left\">"; - s << argDefault << "</td></tr>"; - } - if (argDefault != arg->originalDefaultValueExpression()) { - s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << functionNumber(func); - s << "-orig-default</td><td bgcolor=\"gray\" align=\"left\">"; - s << arg->originalDefaultValueExpression() << "</td></tr>"; - } - } + const OverloadDataRootNode *root = this; + while (!root->isRoot()) + root = root->parent(); - s << "</table>>];\n"; + // Overloads for the signature to present point + s << "<tr><td bgcolor=\"gray\" align=\"right\">overloads</td><td bgcolor=\"gray\" align=\"left\">"; + for (const auto &func : m_overloads) + s << 'f' << root->functionNumber(func) << ' '; + s << "</td></tr>"; - for (const OverloadData *pd : m_nextOverloadData) - s << " " << argId << " -> " << pd->dumpGraph(); + // Show default values (original and modified) for various functions + for (const auto &func : m_overloads) { + const AbstractMetaArgument *arg = overloadArgument(func); + if (!arg) + continue; + const int n = root->functionNumber(func); + QString argDefault = arg->defaultValueExpression(); + if (!argDefault.isEmpty() || + argDefault != arg->originalDefaultValueExpression()) { + s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << n; + s << "-default</td><td bgcolor=\"gray\" align=\"left\">"; + s << argDefault << "</td></tr>"; + } + if (argDefault != arg->originalDefaultValueExpression()) { + s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << n; + s << "-orig-default</td><td bgcolor=\"gray\" align=\"left\">"; + s << arg->originalDefaultValueExpression() << "</td></tr>"; + } } - return result; -} -int OverloadData::functionNumber(const AbstractMetaFunctionCPtr &func) const -{ - return m_headOverloadData->m_overloads.indexOf(func); -} + s << "</table>>];\n"; -OverloadData::~OverloadData() -{ - while (!m_nextOverloadData.isEmpty()) - delete m_nextOverloadData.takeLast(); + for (const auto &pd : m_children) { + s << " " << argId << " -> "; + pd->dumpNodeGraph(s); + } } -bool OverloadData::hasArgumentTypeReplace() const +int OverloadDataRootNode::functionNumber(const AbstractMetaFunctionCPtr &func) const { - return !m_argTypeReplaced.isEmpty(); + return m_overloads.indexOf(func); } -QString OverloadData::argumentTypeReplaced() const +bool OverloadData::pythonFunctionWrapperUsesListOfArguments() const { - return m_argTypeReplaced; + auto referenceFunction = m_overloads.constFirst(); + if (referenceFunction->isCallOperator()) + return true; + if (referenceFunction->isOperatorOverload()) + return false; + const int maxArgs = this->maxArgs(); + const int minArgs = this->minArgs(); + return (minArgs != maxArgs) + || (maxArgs > 1) + || referenceFunction->isConstructor() + || hasArgumentWithDefaultValue(); } bool OverloadData::hasArgumentWithDefaultValue() const @@ -967,9 +906,7 @@ bool OverloadData::hasArgumentWithDefaultValue(const AbstractMetaFunctionCPtr &f { const AbstractMetaArgumentList &arguments = func->arguments(); for (const AbstractMetaArgument &arg : arguments) { - if (func->argumentRemoved(arg.argumentIndex() + 1)) - continue; - if (arg.hasDefaultValueExpression()) + if (!arg.isModifiedRemoved() && arg.hasDefaultValueExpression()) return true; } return false; @@ -981,7 +918,7 @@ AbstractMetaArgumentList OverloadData::getArgumentsWithDefaultValues(const Abstr const AbstractMetaArgumentList &arguments = func->arguments(); for (const AbstractMetaArgument &arg : arguments) { if (!arg.hasDefaultValueExpression() - || func->argumentRemoved(arg.argumentIndex() + 1)) + || arg.isModifiedRemoved()) continue; args << arg; } @@ -989,9 +926,9 @@ AbstractMetaArgumentList OverloadData::getArgumentsWithDefaultValues(const Abstr } #ifndef QT_NO_DEBUG_STREAM -void OverloadData::formatDebug(QDebug &d) const + +void OverloadDataRootNode::formatReferenceFunction(QDebug &d) const { - const qsizetype count = m_overloads.size(); auto refFunc = referenceFunction(); d << '"'; if (auto owner = refFunc->ownerClass()) @@ -999,36 +936,76 @@ void OverloadData::formatDebug(QDebug &d) const d << refFunc->minimalSignature() << '"'; if (m_overloads.constFirst()->isReverseOperator()) d << " [reverseop]"; - d << ", argType=" << m_argType << ", minArgs=" << m_minArgs << ", maxArgs=" << m_maxArgs - << ", argPos=" << m_argPos; - if (!m_argTypeReplaced.isEmpty()) - d << ", argTypeReplaced=\"" << m_argTypeReplaced << '"'; +} +void OverloadDataRootNode::formatOverloads(QDebug &d) const +{ + const qsizetype count = m_overloads.size(); + d << ", overloads[" << count << ']'; if (count < 2) return; - d << "\", overloads[" << count << "]=("; - const int oldVerbosity = d.verbosity(); - d.setVerbosity(3); - for (int i = 0; i < count; ++i) { - if (i) - d << '\n'; - d << m_overloads.at(i); - } - d.setVerbosity(oldVerbosity); - d << ')'; + d << "=("; + for (qsizetype i = 0; i < count; ++i) { + if (i) + d << '\n'; + d << m_overloads.at(i)->signature(); + } + d << ')'; } -QDebug operator<<(QDebug d, const OverloadData *od) +void OverloadDataRootNode::formatNextOverloadData(QDebug &d) const +{ + const qsizetype count = m_children.size(); + d << ", next[" << count << ']'; + if (d.verbosity() >= 3) { + d << "=("; + for (qsizetype i = 0; i < count; ++i) { + if (i) + d << '\n'; + m_children.at(i)->formatDebug(d); + } + d << ')'; + } +} + +void OverloadDataRootNode::formatDebug(QDebug &d) const +{ + formatReferenceFunction(d); + formatOverloads(d); + formatNextOverloadData(d); +} + +void OverloadDataNode::formatDebug(QDebug &d) const +{ + d << "OverloadDataNode("; + formatReferenceFunction(d); + d << ", argPos=" << m_argPos; + if (m_argument.argumentIndex() != m_argPos) + d << ", argIndex=" << m_argument.argumentIndex(); + d << ", argType=\"" << m_argument.type().cppSignature() << '"'; + if (isTypeModified()) + d << ", modifiedArgType=\"" << modifiedArgType().cppSignature() << '"'; + formatOverloads(d); + formatNextOverloadData(d); + d << ')'; +} + +void OverloadData::formatDebug(QDebug &d) const +{ + d << "OverloadData("; + formatReferenceFunction(d); + d << ", minArgs=" << m_minArgs << ", maxArgs=" << m_maxArgs; + formatOverloads(d); + formatNextOverloadData(d); + d << ')'; +} + +QDebug operator<<(QDebug d, const OverloadData &od) { QDebugStateSaver saver(d); d.noquote(); d.nospace(); - d << "OverloadData("; - if (od) - od->formatDebug(d); - else - d << '0'; - d << ')'; + od.formatDebug(d); return d; } #endif // !QT_NO_DEBUG_STREAM diff --git a/sources/shiboken6/generator/shiboken/overloaddata.h b/sources/shiboken6/generator/shiboken/overloaddata.h index ecd7a5596..875a5a8b5 100644 --- a/sources/shiboken6/generator/shiboken/overloaddata.h +++ b/sources/shiboken6/generator/shiboken/overloaddata.h @@ -1,59 +1,123 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef OVERLOADDATA_H #define OVERLOADDATA_H #include <apiextractorresult.h> +#include <abstractmetaargument.h> #include <QtCore/QBitArray> #include <QtCore/QList> +#include <memory> + QT_FORWARD_DECLARE_CLASS(QDebug) +QT_FORWARD_DECLARE_CLASS(QTextStream) -class OverloadData; -using OverloadDataList = QList<OverloadData *>; +class OverloadDataNode; +using OverloadDataNodePtr = std::shared_ptr<OverloadDataNode>; +using OverloadDataList = QList<OverloadDataNodePtr>; -class OverloadData +/// The root node of OverloadData. It contains all functions +class OverloadDataRootNode { public: - OverloadData(const AbstractMetaFunctionCList &overloads, - const ApiExtractorResult &api); - ~OverloadData(); + virtual ~OverloadDataRootNode(); + + OverloadDataRootNode(const OverloadDataRootNode &) = delete; + OverloadDataRootNode &operator=(const OverloadDataRootNode &) = delete; + OverloadDataRootNode(OverloadDataRootNode &&) = delete; + OverloadDataRootNode &operator=(OverloadDataRootNode &&) = delete; + + virtual int argPos() const { return -1; } + virtual const OverloadDataRootNode *parent() const { return nullptr; } + + bool isRoot() const { return parent() == nullptr; } + + AbstractMetaFunctionCPtr referenceFunction() const; - int minArgs() const { return m_headOverloadData->m_minArgs; } - int maxArgs() const { return m_headOverloadData->m_maxArgs; } - int argPos() const { return m_argPos; } + const AbstractMetaFunctionCList &overloads() const { return m_overloads; } + const OverloadDataList &children() const { return m_children; } - const AbstractMetaType &argType() const { return m_argType; } + bool nextArgumentHasDefaultValue() const; + + /// Returns the function that has a default value at the current OverloadData argument position, otherwise returns null. + AbstractMetaFunctionCPtr getFunctionWithDefaultValue() const; + + /// Returns the nearest occurrence, including this instance, of an argument with a default value. + const OverloadDataRootNode *findNextArgWithDefault() const; + bool isFinalOccurrence(const AbstractMetaFunctionCPtr &func) const; + + int functionNumber(const AbstractMetaFunctionCPtr &func) const; + +#ifndef QT_NO_DEBUG_STREAM + virtual void formatDebug(QDebug &d) const; +#endif - /// Returns a string list containing all the possible return types (including void) for the current OverloadData. - QStringList returnTypes() const; + OverloadDataNode *addOverloadDataNode(const AbstractMetaFunctionCPtr &func, + const AbstractMetaArgument &arg); + +protected: + OverloadDataRootNode(const AbstractMetaFunctionCList &o= {}); + + void dumpRootGraph(QTextStream &s, int minArgs, int maxArgs) const; + void sortNextOverloads(const ApiExtractorResult &api); + + +#ifndef QT_NO_DEBUG_STREAM + void formatReferenceFunction(QDebug &d) const; + void formatOverloads(QDebug &d) const; + void formatNextOverloadData(QDebug &d) const; +#endif + + AbstractMetaFunctionCList m_overloads; + OverloadDataList m_children; +}; + +/// OverloadDataNode references an argument/type combination. +class OverloadDataNode : public OverloadDataRootNode +{ +public: + explicit OverloadDataNode(const AbstractMetaFunctionCPtr &func, + OverloadDataRootNode *parent, + const AbstractMetaArgument &arg, int argPos, + const QString argTypeReplaced = {}); + void addOverload(const AbstractMetaFunctionCPtr &func); + + int argPos() const override { return m_argPos; } + const OverloadDataRootNode *parent() const override; + void dumpNodeGraph(QTextStream &s) const; + + const AbstractMetaArgument &argument() const + { return m_argument; } + const AbstractMetaType &argType() const { return m_argument.type(); } + const AbstractMetaType &modifiedArgType() const { return m_argument.modifiedType(); } + + bool isTypeModified() const { return m_argument.isTypeModified(); } + + const AbstractMetaArgument *overloadArgument(const AbstractMetaFunctionCPtr &func) const; + +#ifndef QT_NO_DEBUG_STREAM + void formatDebug(QDebug &d) const override; +#endif + +private: + AbstractMetaArgument m_argument; + QString m_argTypeReplaced; + OverloadDataRootNode *m_parent = nullptr; + + int m_argPos = -1; // Position excluding modified/removed arguments. +}; + +class OverloadData : public OverloadDataRootNode +{ +public: + explicit OverloadData(const AbstractMetaFunctionCList &overloads, + const ApiExtractorResult &api); + + int minArgs() const { return m_minArgs; } + int maxArgs() const { return m_maxArgs; } /// Returns true if any of the overloads for the current OverloadData has a return type different from void. bool hasNonVoidReturnType() const; @@ -61,15 +125,18 @@ public: /// Returns true if any of the overloads for the current OverloadData has a varargs argument. bool hasVarargs() const; - /// Returns true if any of the overloads for the current OverloadData allows threads when called. - bool hasAllowThread() const; - /// Returns true if any of the overloads for the current OverloadData is static. bool hasStaticFunction() const; /// Returns true if any of the overloads passed as argument is static. static bool hasStaticFunction(const AbstractMetaFunctionCList &overloads); + /// Returns true if any of the overloads for the current OverloadData is classmethod. + bool hasClassMethod() const; + + /// Returns true if any of the overloads passed as argument is classmethod. + static bool hasClassMethod(const AbstractMetaFunctionCList &overloads); + /// Returns true if any of the overloads for the current OverloadData is not static. bool hasInstanceFunction() const; @@ -82,40 +149,17 @@ public: /// Returns true if among the overloads passed as argument there are static and non-static methods altogether. static bool hasStaticAndInstanceFunctions(const AbstractMetaFunctionCList &overloads); - AbstractMetaFunctionCPtr referenceFunction() const; - const AbstractMetaArgument *argument(const AbstractMetaFunctionCPtr &func) const; - OverloadDataList overloadDataOnPosition(int argPos) const; - - bool isHeadOverloadData() const { return this == m_headOverloadData; } - - /// Returns the root OverloadData object that represents all the overloads. - OverloadData *headOverloadData() const { return m_headOverloadData; } - - /// Returns the function that has a default value at the current OverloadData argument position, otherwise returns null. - AbstractMetaFunctionCPtr getFunctionWithDefaultValue() const; - - bool nextArgumentHasDefaultValue() const; - /// Returns the nearest occurrence, including this instance, of an argument with a default value. - OverloadData *findNextArgWithDefault(); - bool isFinalOccurrence(const AbstractMetaFunctionCPtr &func) const; - - /// Returns the list of overloads removing repeated constant functions (ex.: "foo()" and "foo()const", the second is removed). - AbstractMetaFunctionCList overloadsWithoutRepetition() const; - const AbstractMetaFunctionCList &overloads() const { return m_overloads; } - OverloadDataList nextOverloadData() const { return m_nextOverloadData; } - OverloadData *previousOverloadData() const { return m_previousOverloadData; } - QList<int> invalidArgumentLengths() const; - static int numberOfRemovedArguments(const AbstractMetaFunctionCPtr &func, int finalArgPos = -1); - /// Returns true if all overloads have no more than one argument. - static bool isSingleArgument(const AbstractMetaFunctionCList &overloads); + static int numberOfRemovedArguments(const AbstractMetaFunctionCPtr &func); + static int numberOfRemovedArguments(const AbstractMetaFunctionCPtr &func, int finalArgPos); void dumpGraph(const QString &filename) const; QString dumpGraph() const; + bool showGraph() const; - bool hasArgumentTypeReplace() const; - QString argumentTypeReplaced() const; + /// Returns true if a list of arguments is used (METH_VARARGS) + bool pythonFunctionWrapperUsesListOfArguments() const; bool hasArgumentWithDefaultValue() const; static bool hasArgumentWithDefaultValue(const AbstractMetaFunctionCPtr &func); @@ -124,38 +168,16 @@ public: static AbstractMetaArgumentList getArgumentsWithDefaultValues(const AbstractMetaFunctionCPtr &func); #ifndef QT_NO_DEBUG_STREAM - void formatDebug(QDebug &) const; + void formatDebug(QDebug &) const override; #endif private: - OverloadData(OverloadData *headOverloadData, const AbstractMetaFunctionCPtr &func, - const AbstractMetaType &argType, int argPos, - const ApiExtractorResult &api); - - void addOverload(const AbstractMetaFunctionCPtr &func); - OverloadData *addOverloadData(const AbstractMetaFunctionCPtr &func, const AbstractMetaArgument &arg); - - void sortNextOverloads(); - bool sortByOverloadNumberModification(); - - int functionNumber(const AbstractMetaFunctionCPtr &func) const; - OverloadDataList overloadDataOnPosition(OverloadData *overloadData, int argPos) const; - - int m_minArgs; - int m_maxArgs; - int m_argPos; - AbstractMetaType m_argType; - QString m_argTypeReplaced; - AbstractMetaFunctionCList m_overloads; - - OverloadData *m_headOverloadData; - OverloadDataList m_nextOverloadData; - OverloadData *m_previousOverloadData; - const ApiExtractorResult m_api; + int m_minArgs = 256; + int m_maxArgs = 0; }; #ifndef QT_NO_DEBUG_STREAM -QDebug operator<<(QDebug, const OverloadData *); +QDebug operator<<(QDebug, const OverloadData &); #endif #endif // OVERLOADDATA_H diff --git a/sources/shiboken6/generator/shiboken/pytypenames.h b/sources/shiboken6/generator/shiboken/pytypenames.h index cec7054a0..6c7658ff6 100644 --- a/sources/shiboken6/generator/shiboken/pytypenames.h +++ b/sources/shiboken6/generator/shiboken/pytypenames.h @@ -1,55 +1,29 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef PYTYPENAMES_H #define PYTYPENAMES_H #include <QtCore/QString> -static inline QString pyBoolT() { return QStringLiteral("PyBool"); } -static inline QString pyFloatT() { return QStringLiteral("PyFloat"); } -static inline QString pyIntT() { return QStringLiteral("PyInt"); } -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 8935fa162..67fd9c994 100644 --- a/sources/shiboken6/generator/shiboken/shibokengenerator.cpp +++ b/sources/shiboken6/generator/shiboken/shibokengenerator.cpp @@ -1,135 +1,119 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "shibokengenerator.h" +#include "generatorstrings.h" +#include "generatorargument.h" +#include "defaultvalue.h" +#include "generatorcontext.h" #include "apiextractorresult.h" +#include "codesnip.h" +#include "customconversion.h" #include "ctypenames.h" +#include <abstractmetabuilder.h> #include <abstractmetaenum.h> #include <abstractmetafield.h> #include <abstractmetafunction.h> #include <abstractmetalang.h> +#include <abstractmetalang_helpers.h> +#include <usingmember.h> #include <exception.h> #include <messages.h> #include <modifications.h> #include "overloaddata.h" +#include <optionsparser.h> #include "propertyspec.h" #include "pytypenames.h" #include <reporthandler.h> #include <textstream.h> #include <typedatabase.h> -#include <abstractmetabuilder.h> +#include <containertypeentry.h> +#include <customtypenentry.h> +#include <enumtypeentry.h> +#include <flagstypeentry.h> +#include <namespacetypeentry.h> +#include <primitivetypeentry.h> +#include <pythontypeentry.h> +#include <smartpointertypeentry.h> +#include <valuetypeentry.h> + #include <iostream> +#include "qtcompat.h" + #include <QtCore/QDir> #include <QtCore/QDebug> #include <QtCore/QRegularExpression> + +#include <algorithm> #include <limits> #include <memory> +#include <utility> -static const char AVOID_PROTECTED_HACK[] = "avoid-protected-hack"; -static const char PARENT_CTOR_HEURISTIC[] = "enable-parent-ctor-heuristic"; -static const char RETURN_VALUE_HEURISTIC[] = "enable-return-value-heuristic"; -static const char ENABLE_PYSIDE_EXTENSIONS[] = "enable-pyside-extensions"; -static const char DISABLE_VERBOSE_ERROR_MESSAGES[] = "disable-verbose-error-messages"; -static const char USE_ISNULL_AS_NB_NONZERO[] = "use-isnull-as-nb_nonzero"; -static const char WRAPPER_DIAGNOSTICS[] = "wrapper-diagnostics"; - -const char *CPP_ARG = "cppArg"; -const char *CPP_ARG_REMOVED = "removed_cppArg"; -const char *CPP_RETURN_VAR = "cppResult"; -const char *CPP_SELF_VAR = "cppSelf"; -const char *NULL_PTR = "nullptr"; -const char *PYTHON_ARG = "pyArg"; -const char *PYTHON_ARGS = "pyArgs"; -const char *PYTHON_OVERRIDE_VAR = "pyOverride"; -const char *PYTHON_RETURN_VAR = "pyResult"; -const char *PYTHON_TO_CPP_VAR = "pythonToCpp"; -const char *SMART_POINTER_GETTER = "kSmartPointerGetter"; - -const char *CONV_RULE_OUT_VAR_SUFFIX = "_out"; -const char *BEGIN_ALLOW_THREADS = - "PyThreadState *_save = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS"; -const char *END_ALLOW_THREADS = "PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS"; - -// Return a prefix to fully qualify value, eg: -// resolveScopePrefix("Class::NestedClass::Enum::Value1", "Enum::Value1") -// -> "Class::NestedClass::") -static QString resolveScopePrefix(const QStringList &scopeList, const QString &value) -{ - QString name; - for (int i = scopeList.size() - 1 ; i >= 0; --i) { - const QString prefix = scopeList.at(i) + QLatin1String("::"); - if (value.startsWith(prefix)) - name.clear(); - else - name.prepend(prefix); - } - return name; -} +using namespace Qt::StringLiterals; + +static constexpr auto PARENT_CTOR_HEURISTIC = "enable-parent-ctor-heuristic"_L1; +static constexpr auto RETURN_VALUE_HEURISTIC = "enable-return-value-heuristic"_L1; +static constexpr auto DISABLE_VERBOSE_ERROR_MESSAGES = "disable-verbose-error-messages"_L1; +static constexpr auto USE_ISNULL_AS_NB_BOOL = "use-isnull-as-nb-bool"_L1; +// FIXME PYSIDE 7: Remove USE_ISNULL_AS_NB_NONZERO/USE_OPERATOR_BOOL_AS_NB_NONZERO +static constexpr auto USE_ISNULL_AS_NB_NONZERO = "use-isnull-as-nb_nonzero"_L1; +static constexpr auto USE_OPERATOR_BOOL_AS_NB_BOOL = "use-operator-bool-as-nb-bool"_L1; +static constexpr auto USE_OPERATOR_BOOL_AS_NB_NONZERO = "use-operator-bool-as-nb-nonzero"_L1; +static constexpr auto WRAPPER_DIAGNOSTICS = "wrapper-diagnostics"_L1; +static constexpr auto NO_IMPLICIT_CONVERSIONS = "no-implicit-conversions"_L1; +static constexpr auto LEAN_HEADERS = "lean-headers"_L1; -static inline QStringList splitClassScope(const AbstractMetaClass *scope) +QString CPP_ARG_N(int i) { - return scope->qualifiedCppName().split(QLatin1String("::"), Qt::SkipEmptyParts); + return CPP_ARG + QString::number(i); } -static QString resolveScopePrefix(const AbstractMetaClass *scope, const QString &value) +constexpr auto CPP_ARG_REMOVED_PREFIX = "removed_cppArg"_L1; + +QString CPP_ARG_REMOVED(int i) { - return scope - ? resolveScopePrefix(splitClassScope(scope), value) - : QString(); + return CPP_ARG_REMOVED_PREFIX + QString::number(i); } -static QString resolveScopePrefix(const AbstractMetaEnum &metaEnum, - const QString &value) +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 { - QStringList parts; - if (const AbstractMetaClass *scope = metaEnum.enclosingClass()) - parts.append(splitClassScope(scope)); - // Fully qualify the value which is required for C++ 11 enum classes. - if (!metaEnum.isAnonymous()) - parts.append(metaEnum.name()); - return resolveScopePrefix(parts, value); -} + 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) -using AbstractMetaTypeCache = QHash<QString, AbstractMetaType>; - -Q_GLOBAL_STATIC(AbstractMetaTypeCache, metaTypeFromStringCache) - static const char CHECKTYPE_REGEX[] = R"(%CHECKTYPE\[([^\[]*)\]\()"; static const char ISCONVERTIBLE_REGEX[] = R"(%ISCONVERTIBLE\[([^\[]*)\]\()"; static const char CONVERTTOPYTHON_REGEX[] = R"(%CONVERTTOPYTHON\[([^\[]*)\]\()"; @@ -142,14 +126,18 @@ const ShibokenGenerator::TypeSystemConverterRegExps & ShibokenGenerator::typeSystemConvRegExps() { static const TypeSystemConverterRegExps result = { - QRegularExpression(QLatin1String(CHECKTYPE_REGEX)), - QRegularExpression(QLatin1String(ISCONVERTIBLE_REGEX)), - QRegularExpression(QLatin1String(CONVERTTOCPP_REGEX)), - QRegularExpression(QLatin1String(CONVERTTOPYTHON_REGEX)) + QRegularExpression(QLatin1StringView(CHECKTYPE_REGEX)), + QRegularExpression(QLatin1StringView(ISCONVERTIBLE_REGEX)), + QRegularExpression(QLatin1StringView(CONVERTTOCPP_REGEX)), + QRegularExpression(QLatin1StringView(CONVERTTOPYTHON_REGEX)) }; return result; } +// Options are static to avoid duplicated handling since ShibokenGenerator +// is instantiated for HeaderGenerator and CppGenerator. +ShibokenGeneratorOptions ShibokenGenerator::m_options; + ShibokenGenerator::ShibokenGenerator() = default; ShibokenGenerator::~ShibokenGenerator() = default; @@ -158,64 +146,65 @@ ShibokenGenerator::~ShibokenGenerator() = default; static const QHash<QString, QString> &primitiveTypesCorrespondences() { static const QHash<QString, QString> result = { - {QLatin1String("bool"), pyBoolT()}, - {QLatin1String("char"), sbkCharT()}, - {QLatin1String("signed char"), sbkCharT()}, - {QLatin1String("unsigned char"), sbkCharT()}, - {intT(), pyIntT()}, - {QLatin1String("signed int"), pyIntT()}, - {QLatin1String("uint"), pyIntT()}, - {QLatin1String("unsigned int"), pyIntT()}, - {shortT(), pyIntT()}, - {QLatin1String("ushort"), pyIntT()}, - {QLatin1String("signed short"), pyIntT()}, - {QLatin1String("signed short int"), pyIntT()}, - {unsignedShortT(), pyIntT()}, - {QLatin1String("unsigned short int"), pyIntT()}, - {longT(), pyIntT()}, - {doubleT(), pyFloatT()}, - {floatT(), pyFloatT()}, - {QLatin1String("unsigned long"), pyLongT()}, - {QLatin1String("signed long"), pyLongT()}, - {QLatin1String("ulong"), pyLongT()}, - {QLatin1String("unsigned long int"), pyLongT()}, - {QLatin1String("long long"), pyLongT()}, - {QLatin1String("__int64"), pyLongT()}, - {QLatin1String("unsigned long long"), pyLongT()}, - {QLatin1String("unsigned __int64"), pyLongT()}, - {QLatin1String("size_t"), 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; } -// Format units for C++->Python->C++ conversion -const QHash<QString, QString> &ShibokenGenerator::formatUnits() -{ - static const QHash<QString, QString> result = { - {QLatin1String("char"), QLatin1String("b")}, - {QLatin1String("unsigned char"), QLatin1String("B")}, - {intT(), QLatin1String("i")}, - {QLatin1String("unsigned int"), QLatin1String("I")}, - {shortT(), QLatin1String("h")}, - {unsignedShortT(), QLatin1String("H")}, - {longT(), QLatin1String("l")}, - {unsignedLongLongT(), QLatin1String("k")}, - {longLongT(), QLatin1String("L")}, - {QLatin1String("__int64"), QLatin1String("L")}, - {unsignedLongLongT(), QLatin1String("K")}, - {QLatin1String("unsigned __int64"), QLatin1String("K")}, - {doubleT(), QLatin1String("d")}, - {floatT(), QLatin1String("f")}, +const QHash<QString, QChar> &ShibokenGenerator::formatUnits() +{ + static const QHash<QString, QChar> result = { + {u"char"_s, u'b'}, + {u"unsigned char"_s, u'B'}, + {intT, u'i'}, + {u"unsigned int"_s, u'I'}, + {shortT, u'h'}, + {unsignedShortT, u'H'}, + {longT, u'l'}, + {unsignedLongLongT, u'k'}, + {longLongT, u'L'}, + {u"__int64"_s, u'L'}, + {unsignedLongLongT, u'K'}, + {u"unsigned __int64"_s, u'K'}, + {doubleT, u'd'}, + {floatT, u'f'}, }; return result; } QString ShibokenGenerator::translateTypeForWrapperMethod(const AbstractMetaType &cType, - const AbstractMetaClass *context, + const AbstractMetaClassCPtr &context, Options options) const { - if (cType.isArray()) - return translateTypeForWrapperMethod(*cType.arrayElementType(), context, options) + QLatin1String("[]"); + if (cType.isArray()) { + return translateTypeForWrapperMethod(*cType.arrayElementType(), context, options) + + u"[]"_s; + } if (avoidProtectedHack() && cType.isEnum()) { auto metaEnum = api().findAbstractMetaEnum(cType.typeEntry()); @@ -226,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) @@ -234,37 +223,158 @@ bool ShibokenGenerator::shouldGenerateCppWrapper(const AbstractMetaClass *metaCl && wrapper.testFlag(AbstractMetaClass::CppProtectedHackWrapper)); } -bool ShibokenGenerator::shouldWriteVirtualMethodNative(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; + + const auto functionType = func->functionType(); + switch (functionType) { + case AbstractMetaFunction::ConversionOperator: + case AbstractMetaFunction::AssignmentOperatorFunction: + case AbstractMetaFunction::MoveAssignmentOperatorFunction: + case AbstractMetaFunction::DestructorFunction: + case AbstractMetaFunction::SignalFunction: + case AbstractMetaFunction::GetAttroFunction: + case AbstractMetaFunction::SetAttroFunction: + return result; + default: + if (func->isUserAdded() || func->usesRValueReferences() || !func->isWhiteListed()) + return result; + break; + } + + const bool notModifiedRemoved = !func->isModifiedRemoved(); + const bool isPrivate = func->isPrivate() && !func->isVisibilityModifiedToPrivate(); + switch (functionType) { + case AbstractMetaFunction::ConstructorFunction: + if (!isPrivate && notModifiedRemoved) + result.setFlag(FunctionGenerationFlag::WrapperConstructor); + return result; + case AbstractMetaFunction::CopyConstructorFunction: + if (!isPrivate && notModifiedRemoved) + result.setFlag(FunctionGenerationFlag::WrapperSpecialCopyConstructor); + return result; + case AbstractMetaFunction::NormalFunction: + case AbstractMetaFunction::SlotFunction: + if (avoidProtectedHack() && func->isProtected()) + result.setFlag(FunctionGenerationFlag::ProtectedWrapper); + break; + default: + break; + } + + // Check on virtuals (including operators). + const bool isAbstract = func->isAbstract(); + if (!(isAbstract || func->isVirtual()) + || func->cppAttributes().testFlag(FunctionAttribute::Final) + || func->isModifiedFinal()) { + return result; + } + + // MetaObject virtuals only need to be declared; CppGenerator creates a + // special implementation. + if (functionType == AbstractMetaFunction::NormalFunction + && usePySideExtensions() && isQObject(func->ownerClass())) { + const QString &name = func->name(); + if (name == u"metaObject"_s || name == u"qt_metacall") { + result.setFlag(FunctionGenerationFlag::QMetaObjectMethod); + return result; + } + } + + // Pure virtual functions need a default implementation even if private. + if (isAbstract || (notModifiedRemoved && !isPrivate)) + result.setFlag(FunctionGenerationFlag::VirtualMethod); + + return result; +} + +AbstractMetaFunctionCList ShibokenGenerator::implicitConversions(const TypeEntryCPtr &t) const { - // PYSIDE-803: Extracted this because it is used multiple times. - const AbstractMetaClass *metaClass = func->ownerClass(); - return (!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) - && ((func->isVirtual() || func->isAbstract()) - && !func->attributes().testFlag(AbstractMetaFunction::FinalCppMethod)); + if (!generateImplicitConversions() || !t->isValue()) + return {}; + auto vte = std::static_pointer_cast<const ValueTypeEntry>(t); + auto customConversion = vte->customConversion(); + if (customConversion && customConversion->replaceOriginalTargetToNativeConversions()) + return {}; + + auto result = api().implicitConversions(t); + auto end = std::remove_if(result.begin(), result.end(), + [](const AbstractMetaFunctionCPtr &f) { + return f->isUserAdded(); + }); + result.erase(end, result.end()); + return result; } -QString ShibokenGenerator::wrapperName(const AbstractMetaClass *metaClass) const +QString ShibokenGenerator::wrapperName(const AbstractMetaClassCPtr &metaClass) { Q_ASSERT(shouldGenerateCppWrapper(metaClass)); QString result = metaClass->name(); if (metaClass->enclosingClass()) // is a inner class - result.replace(QLatin1String("::"), QLatin1String("_")); - return result + QLatin1String("Wrapper"); + result.replace(u"::"_s, u"_"_s); + 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() + QLatin1Char('.')); + fullClassName.prepend(enclosing->name() + u'.'); enclosing = enclosing->enclosingClass(); } - fullClassName.prepend(packageName() + QLatin1Char('.')); + 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; @@ -277,24 +387,29 @@ QString ShibokenGenerator::fullPythonFunctionName(const AbstractMetaFunctionCPtr if (func->isConstructor()) { funcName = fullClassName; if (forceFunc) - funcName.append(QLatin1String(".__init__")); + funcName.append(u".__init__"_s); } else { - funcName.prepend(fullClassName + QLatin1Char('.')); + funcName.prepend(fullClassName + u'.'); } } else { - funcName = packageName() + QLatin1Char('.') + func->name(); + funcName = packageName() + u'.' + func->name(); } return funcName; } +bool ShibokenGenerator::wrapperDiagnostics() +{ + return m_options.wrapperDiagnostics; +} + QString ShibokenGenerator::protectedEnumSurrogateName(const AbstractMetaEnum &metaEnum) { QString result = metaEnum.fullName(); - result.replace(QLatin1Char('.'), QLatin1Char('_')); - result.replace(QLatin1String("::"), QLatin1String("_")); - return result + QLatin1String("_Surrogate"); + result.replace(u'.', u'_'); + result.replace(u"::"_s, u"_"_s); + return result + u"_Surrogate"_s; } QString ShibokenGenerator::cpythonFunctionName(const AbstractMetaFunctionCPtr &func) @@ -306,16 +421,16 @@ QString ShibokenGenerator::cpythonFunctionName(const AbstractMetaFunctionCPtr &f if (func->implementingClass()) { result = cpythonBaseName(func->implementingClass()->typeEntry()); if (func->isConstructor()) { - result += QLatin1String("_Init"); + result += u"_Init"_s; } else { - result += QLatin1String("Func_"); + result += u"Func_"_s; if (func->isOperatorOverload()) result += ShibokenGenerator::pythonOperatorFunctionName(func); else result += func->name(); } } else { - result = QLatin1String("Sbk") + moduleName() + QLatin1String("Module_") + func->name(); + result = u"Sbk"_s + moduleName() + u"Module_"_s + func->name(); } return result; @@ -324,37 +439,37 @@ QString ShibokenGenerator::cpythonFunctionName(const AbstractMetaFunctionCPtr &f QString ShibokenGenerator::cpythonMethodDefinitionName(const AbstractMetaFunctionCPtr &func) { if (!func->ownerClass()) - return QString(); - return cpythonBaseName(func->ownerClass()->typeEntry()) + QLatin1String("Method_") + 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) + QLatin1String("_getsetlist"); + return cpythonBaseName(metaClass) + u"_getsetlist"_s; } -QString ShibokenGenerator::cpythonSetattroFunctionName(const AbstractMetaClass *metaClass) +QString ShibokenGenerator::cpythonSetattroFunctionName(const AbstractMetaClassCPtr &metaClass) { - return cpythonBaseName(metaClass) + QLatin1String("_setattro"); + return cpythonBaseName(metaClass) + u"_setattro"_s; } -QString ShibokenGenerator::cpythonGetattroFunctionName(const AbstractMetaClass *metaClass) +QString ShibokenGenerator::cpythonGetattroFunctionName(const AbstractMetaClassCPtr &metaClass) { - return cpythonBaseName(metaClass) + QLatin1String("_getattro"); + 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) @@ -368,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); } @@ -382,159 +497,15 @@ QString ShibokenGenerator::cpythonSetterFunctionName(const QPropertySpec &proper static QString cpythonEnumFlagsName(const QString &moduleName, const QString &qualifiedCppName) { - QString result = QLatin1String("Sbk") + moduleName + QLatin1Char('_') + qualifiedCppName; - result.replace(QLatin1String("::"), QLatin1String("_")); + QString result = u"Sbk"_s + moduleName + u'_' + qualifiedCppName; + result.replace(u"::"_s, u"_"_s); return result; } -// Return the scope for fully qualifying the enumeration including trailing "::". -static QString searchForEnumScope(const AbstractMetaClass *metaClass, const QString &value) -{ - if (!metaClass) - return QString(); - for (const AbstractMetaEnum &metaEnum : metaClass->enums()) { - auto v = metaEnum.findEnumValue(value); - if (v.has_value()) - return resolveScopePrefix(metaEnum, value); - } - // PYSIDE-331: We need to also search the base classes. - QString ret = searchForEnumScope(metaClass->enclosingClass(), value); - if (ret.isEmpty()) - ret = searchForEnumScope(metaClass->baseClass(), value); - return ret; -} - -// Handle QFlags<> for guessScopeForDefaultValue() -QString ShibokenGenerator::guessScopeForDefaultFlagsValue(const AbstractMetaFunctionCPtr &func, - const AbstractMetaArgument &arg, - const QString &value) const -{ - // Numeric values -> "Options(42)" - static const QRegularExpression numberRegEx(QStringLiteral("^\\d+$")); // Numbers to flags - Q_ASSERT(numberRegEx.isValid()); - if (numberRegEx.match(value).hasMatch()) { - QString typeName = translateTypeForWrapperMethod(arg.type(), func->implementingClass()); - if (arg.type().isConstant()) - typeName.remove(0, sizeof("const ") / sizeof(char) - 1); - switch (arg.type().referenceType()) { - case NoReference: - break; - case LValueReference: - typeName.chop(1); - break; - case RValueReference: - typeName.chop(2); - break; - } - return typeName + QLatin1Char('(') + value + QLatin1Char(')'); - } - - // "Options(Option1 | Option2)" -> "Options(Class::Enum::Option1 | Class::Enum::Option2)" - static const QRegularExpression enumCombinationRegEx(QStringLiteral("^([A-Za-z_][\\w:]*)\\(([^,\\(\\)]*)\\)$")); // FlagName(EnumItem|EnumItem|...) - Q_ASSERT(enumCombinationRegEx.isValid()); - const QRegularExpressionMatch match = enumCombinationRegEx.match(value); - if (match.hasMatch()) { - const QString expression = match.captured(2).trimmed(); - if (expression.isEmpty()) - return value; - const QStringList enumItems = expression.split(QLatin1Char('|')); - const QString scope = searchForEnumScope(func->implementingClass(), - enumItems.constFirst().trimmed()); - if (scope.isEmpty()) - return value; - QString result; - QTextStream str(&result); - str << match.captured(1) << '('; // Flag name - for (int i = 0, size = enumItems.size(); i < size; ++i) { - if (i) - str << '|'; - str << scope << enumItems.at(i).trimmed(); - } - str << ')'; - return result; - } - // A single flag "Option1" -> "Class::Enum::Option1" - return searchForEnumScope(func->implementingClass(), value) + value; -} - -/* - * This function uses some heuristics to find out the scope for a given - * argument default value since they must be fully qualified when used outside the class: - * class A { - * enum Enum { e1, e1 }; - * void foo(Enum e = e1); - * } - * should be qualified to: - * A::Enum cppArg0 = A::Enum::e1; - * - * New situations may arise in the future and - * this method should be updated, do it with care. - */ -QString ShibokenGenerator::guessScopeForDefaultValue(const AbstractMetaFunctionCPtr &func, - const AbstractMetaArgument &arg) const -{ - QString value = arg.defaultValueExpression(); - - if (value.isEmpty() || value == QLatin1String("{}") - || arg.hasModifiedDefaultValueExpression() - || arg.type().isPointer()) { - return value; - } - - static const QRegularExpression enumValueRegEx(QStringLiteral("^([A-Za-z_]\\w*)?$")); - Q_ASSERT(enumValueRegEx.isValid()); - // Do not qualify macros by class name, eg QSGGeometry(..., int t = GL_UNSIGNED_SHORT); - static const QRegularExpression macroRegEx(QStringLiteral("^[A-Z_][A-Z0-9_]*$")); - Q_ASSERT(macroRegEx.isValid()); - if (arg.type().isPrimitive() && macroRegEx.match(value).hasMatch()) - return value; - - QString prefix; - if (arg.type().isEnum()) { - auto metaEnum = api().findAbstractMetaEnum(arg.type().typeEntry()); - if (metaEnum.has_value()) - prefix = resolveScopePrefix(metaEnum.value(), value); - } else if (arg.type().isFlags()) { - value = guessScopeForDefaultFlagsValue(func, arg, value); - } else if (arg.type().typeEntry()->isValue()) { - auto metaClass = AbstractMetaClass::findClass(api().classes(), - arg.type().typeEntry()); - if (enumValueRegEx.match(value).hasMatch() && value != QLatin1String("NULL")) - prefix = resolveScopePrefix(metaClass, value); - } else if (arg.type().isPrimitive() && arg.type().name() == intT()) { - if (enumValueRegEx.match(value).hasMatch() && func->implementingClass()) - prefix = resolveScopePrefix(func->implementingClass(), value); - } else if (arg.type().isPrimitive()) { - static const QRegularExpression unknowArgumentRegEx(QStringLiteral("^(?:[A-Za-z_][\\w:]*\\()?([A-Za-z_]\\w*)(?:\\))?$")); // [PrimitiveType(] DESIREDNAME [)] - Q_ASSERT(unknowArgumentRegEx.isValid()); - const QRegularExpressionMatch match = unknowArgumentRegEx.match(value); - if (match.hasMatch() && func->implementingClass()) { - for (const AbstractMetaField &field : func->implementingClass()->fields()) { - if (match.captured(1).trimmed() == field.name()) { - QString fieldName = field.name(); - if (field.isStatic()) { - prefix = resolveScopePrefix(func->implementingClass(), value); - fieldName.prepend(prefix); - prefix.clear(); - } else { - fieldName.prepend(QLatin1String(CPP_SELF_VAR) + QLatin1String("->")); - } - value.replace(match.captured(1), fieldName); - break; - } - } - } - } - - if (!prefix.isEmpty()) - value.prepend(prefix); - return value; -} - -QString ShibokenGenerator::cpythonEnumName(const EnumTypeEntry *enumEntry) +QString ShibokenGenerator::cpythonEnumName(const EnumTypeEntryCPtr &enumEntry) { QString p = enumEntry->targetLangPackage(); - p.replace(QLatin1Char('.'), QLatin1Char('_')); + p.replace(u'.', u'_'); return cpythonEnumFlagsName(p, enumEntry->qualifiedCppName()); } @@ -543,27 +514,25 @@ QString ShibokenGenerator::cpythonEnumName(const AbstractMetaEnum &metaEnum) return cpythonEnumName(metaEnum.typeEntry()); } -QString ShibokenGenerator::cpythonFlagsName(const FlagsTypeEntry *flagsEntry) +QString ShibokenGenerator::cpythonFlagsName(const FlagsTypeEntryCPtr &flagsEntry) { QString p = flagsEntry->targetLangPackage(); - p.replace(QLatin1Char('.'), QLatin1Char('_')); + p.replace(u'.', u'_'); return cpythonEnumFlagsName(p, flagsEntry->originalName()); } QString ShibokenGenerator::cpythonFlagsName(const AbstractMetaEnum *metaEnum) { - const FlagsTypeEntry *flags = metaEnum->typeEntry()->flags(); - if (!flags) - return QString(); - return cpythonFlagsName(flags); + const auto flags = metaEnum->typeEntry()->flags(); + return flags ? cpythonFlagsName(flags) : QString{}; } -QString ShibokenGenerator::cpythonSpecialCastFunctionName(const AbstractMetaClass *metaClass) +QString ShibokenGenerator::cpythonSpecialCastFunctionName(const AbstractMetaClassCPtr &metaClass) { - return cpythonBaseName(metaClass->typeEntry()) + QLatin1String("SpecialCastFunction"); + 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); @@ -573,57 +542,58 @@ QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaType &metaType, const QString &argName) { if (!metaType.isWrapperType()) - return QString(); - return QLatin1String("reinterpret_cast< ::") + metaType.cppSignature() - + QLatin1String(" *>(Shiboken::Conversions::cppPointer(") + cpythonTypeNameExt(metaType) - + QLatin1String(", reinterpret_cast<SbkObject *>(") + argName + QLatin1String(")))"); + return {}; + return u"reinterpret_cast< ::"_s + metaType.cppSignature() + + u" *>(Shiboken::Conversions::cppPointer("_s + cpythonTypeNameExt(metaType) + + u", reinterpret_cast<SbkObject *>("_s + argName + u")))"_s; } -QString ShibokenGenerator::cpythonWrapperCPtr(const TypeEntry *type, +QString ShibokenGenerator::cpythonWrapperCPtr(const TypeEntryCPtr &type, const QString &argName) { if (!type->isWrapperType()) return QString(); - return QLatin1String("reinterpret_cast< ::") + type->qualifiedCppName() - + QLatin1String(" *>(Shiboken::Conversions::cppPointer(") + cpythonTypeNameExt(type) - + QLatin1String(", reinterpret_cast<SbkObject *>(") + argName + QLatin1String(")))"); + 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 ApiExtractorResult &api, - const AbstractMetaFunctionCPtr &func, int argIndex) +bool ShibokenGenerator::shouldRejectNullPointerArgument(const AbstractMetaFunctionCPtr &func, + int argIndex) { - if (argIndex < 0 || argIndex >= func->arguments().count()) + if (argIndex < 0 || argIndex >= func->arguments().size()) return false; const AbstractMetaArgument &arg = func->arguments().at(argIndex); - if (isValueTypeWithCopyConstructorOnly(api, arg.type())) + if (arg.type().isValueTypeWithCopyConstructorOnly()) return true; // Argument type is not a pointer, a None rejection should not be // necessary because the type checking would handle that already. if (!arg.type().isPointer()) return false; - if (func->argumentRemoved(argIndex + 1)) + if (arg.isModifiedRemoved()) return false; for (const auto &funcMod : func->modifications()) { for (const ArgumentModification &argMod : funcMod.argument_mods()) { @@ -634,192 +604,152 @@ bool ShibokenGenerator::shouldRejectNullPointerArgument(const ApiExtractorResult return false; } -QString ShibokenGenerator::getFormatUnitString(const AbstractMetaFunctionCPtr &func, bool incRef) -{ - QString result; - const char objType = (incRef ? 'O' : 'N'); - const AbstractMetaArgumentList &arguments = func->arguments(); - for (const AbstractMetaArgument &arg : arguments) { - if (func->argumentRemoved(arg.argumentIndex() + 1)) - continue; - - const auto &type = arg.type(); - if (!func->typeReplaced(arg.argumentIndex() + 1).isEmpty()) { - result += QLatin1Char(objType); - } else if (arg.type().isObject() - || type.isValue() - || type.isValuePointer() - || type.isNativePointer() - || type.isEnum() - || type.isFlags() - || type.isContainer() - || type.isSmartPointer() - || type.referenceType() == LValueReference) { - result += QLatin1Char(objType); - } else if (type.isPrimitive()) { - const auto *ptype = - static_cast<const PrimitiveTypeEntry *>(type.typeEntry()); - if (ptype->basicReferencedTypeEntry()) - ptype = ptype->basicReferencedTypeEntry(); - const auto it = formatUnits().constFind(ptype->name()); - if (it != formatUnits().cend()) - result += it.value(); - else - result += QLatin1Char(objType); - } else if (type.isCString()) { - result += QLatin1Char('z'); - } else { - qCWarning(lcShiboken).noquote().nospace() - << "Method: " << func->ownerClass()->qualifiedCppName() - << "::" << func->signature() << " => Arg:" - << arg.name() << "index: " << arg.argumentIndex() - << " - cannot be handled properly. Use an inject-code to fix it!"; - result += QLatin1Char('?'); - } - } - return result; -} - QString ShibokenGenerator::cpythonBaseName(const AbstractMetaType &type) { if (type.isCString()) - return QLatin1String("PyString"); + return u"PyString"_s; return cpythonBaseName(type.typeEntry()); } -QString ShibokenGenerator::cpythonBaseName(const AbstractMetaClass *metaClass) +QString ShibokenGenerator::cpythonBaseName(const AbstractMetaClassCPtr &metaClass) { return cpythonBaseName(metaClass->typeEntry()); } -QString ShibokenGenerator::cpythonBaseName(const TypeEntry *type) +QString ShibokenGenerator::containerCpythonBaseName(const ContainerTypeEntryCPtr &ctype) +{ + switch (ctype->containerKind()) { + case ContainerTypeEntry::SetContainer: + return u"PySet"_s; + case ContainerTypeEntry::MapContainer: + case ContainerTypeEntry::MultiMapContainer: + return u"PyDict"_s; + case ContainerTypeEntry::ListContainer: + case ContainerTypeEntry::PairContainer: + case ContainerTypeEntry::SpanContainer: + break; + default: + Q_ASSERT(false); + } + return cPySequenceT; +} + +QString ShibokenGenerator::cpythonBaseName(const TypeEntryCPtr &type) { QString baseName; if (type->isWrapperType() || type->isNamespace()) { // && type->referenceType() == NoReference) { - baseName = QLatin1String("Sbk_") + type->name(); + baseName = u"Sbk_"_s + type->name(); } else if (type->isPrimitive()) { - const auto *ptype = static_cast<const PrimitiveTypeEntry *>(type); - while (ptype->basicReferencedTypeEntry()) - ptype = ptype->basicReferencedTypeEntry(); - if (ptype->targetLangApiName() == ptype->name()) - baseName = pythonPrimitiveTypeName(ptype->name()); - else - baseName = ptype->targetLangApiName(); + const auto ptype = basicReferencedTypeEntry(type); + baseName = ptype->hasTargetLangApiType() + ? ptype->targetLangApiName() : pythonPrimitiveTypeName(ptype->name()); } else if (type->isEnum()) { - baseName = cpythonEnumName(static_cast<const EnumTypeEntry *>(type)); + baseName = cpythonEnumName(std::static_pointer_cast<const EnumTypeEntry>(type)); } else if (type->isFlags()) { - baseName = cpythonFlagsName(static_cast<const FlagsTypeEntry *>(type)); + baseName = cpythonFlagsName(std::static_pointer_cast<const FlagsTypeEntry>(type)); } else if (type->isContainer()) { - const auto *ctype = static_cast<const ContainerTypeEntry *>(type); - switch (ctype->containerKind()) { - case ContainerTypeEntry::ListContainer: - case ContainerTypeEntry::StringListContainer: - case ContainerTypeEntry::LinkedListContainer: - case ContainerTypeEntry::VectorContainer: - case ContainerTypeEntry::StackContainer: - case ContainerTypeEntry::QueueContainer: - //baseName = "PyList"; - //break; - case ContainerTypeEntry::PairContainer: - //baseName = "PyTuple"; - baseName = cPySequenceT(); - break; - case ContainerTypeEntry::SetContainer: - baseName = QLatin1String("PySet"); - break; - case ContainerTypeEntry::MapContainer: - case ContainerTypeEntry::MultiMapContainer: - case ContainerTypeEntry::HashContainer: - case ContainerTypeEntry::MultiHashContainer: - baseName = QLatin1String("PyDict"); - break; - default: - Q_ASSERT(false); - } + const auto ctype = std::static_pointer_cast<const ContainerTypeEntry>(type); + baseName = containerCpythonBaseName(ctype); } else { - baseName = cPyObjectT(); + baseName = cPyObjectT; } - return baseName.replace(QLatin1String("::"), QLatin1String("_")); + return baseName.replace(u"::"_s, u"_"_s); } -QString ShibokenGenerator::cpythonTypeName(const AbstractMetaClass *metaClass) +QString ShibokenGenerator::cpythonTypeName(const AbstractMetaClassCPtr &metaClass) { return cpythonTypeName(metaClass->typeEntry()); } -QString ShibokenGenerator::cpythonTypeName(const TypeEntry *type) -{ - return cpythonBaseName(type) + QLatin1String("_TypeF()"); -} - -QString ShibokenGenerator::cpythonTypeNameExt(const TypeEntry *type) +QString ShibokenGenerator::cpythonTypeName(const TypeEntryCPtr &type) { - return cppApiVariableName(type->targetLangPackage()) + QLatin1Char('[') - + getTypeIndexVariableName(type) + QLatin1Char(']'); + return cpythonBaseName(type) + u"_TypeF()"_s; } QString ShibokenGenerator::converterObject(const AbstractMetaType &type) { if (type.isCString()) - return QLatin1String("Shiboken::Conversions::PrimitiveTypeConverter<const char *>()"); + return u"Shiboken::Conversions::PrimitiveTypeConverter<const char *>()"_s; if (type.isVoidPointer()) - return QLatin1String("Shiboken::Conversions::PrimitiveTypeConverter<void *>()"); + 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() - + QLatin1String(">(") + QString::number(nestedArrayTypes.size()) - + QLatin1Char(')'); + + u">("_s + QString::number(nestedArrayTypes.size()) + + u')'; } auto typeEntry = type.typeEntry(); if (typeEntry->isContainer() || typeEntry->isSmartPointer()) { return convertersVariableName(typeEntry->targetLangPackage()) - + QLatin1Char('[') + getTypeIndexVariableName(type) + QLatin1Char(']'); + + u'[' + getTypeIndexVariableName(type) + u']'; } return converterObject(typeEntry); } -QString ShibokenGenerator::converterObject(const TypeEntry *type) +QString ShibokenGenerator::converterObject(const TypeEntryCPtr &type) { - if (type->isExtendedCppPrimitive()) - return QString::fromLatin1("Shiboken::Conversions::PrimitiveTypeConverter<%1>()").arg(type->qualifiedCppName()); - if (type->isWrapperType() || type->isEnum() || type->isFlags()) - return QString::fromLatin1("*PepType_SGTP(%1)->converter").arg(cpythonTypeNameExt(type)); + if (isExtendedCppPrimitive(type)) + return QString::fromLatin1("Shiboken::Conversions::PrimitiveTypeConverter<%1>()") + .arg(type->qualifiedCppName()); + if (type->isWrapperType()) + return QString::fromLatin1("PepType_SOTP(reinterpret_cast<PyTypeObject *>(%1))->converter") + .arg(cpythonTypeNameExt(type)); + if (type->isEnum() || type->isFlags()) + return QString::fromLatin1("PepType_SETP(reinterpret_cast<SbkEnumType *>(%1))->converter") + .arg(cpythonTypeNameExt(type)); if (type->isArray()) { qDebug() << "Warning: no idea how to handle the Qt5 type " << type->qualifiedCppName(); - return QString(); + return {}; } /* the typedef'd primitive types case */ - const auto *pte = dynamic_cast<const PrimitiveTypeEntry *>(type); + 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()) { + return u"Shiboken::Conversions::PrimitiveTypeConverter<"_s + + pte->qualifiedCppName() + u">()"_s; } - if (pte->basicReferencedTypeEntry()) - pte = pte->basicReferencedTypeEntry(); - if (pte->isPrimitive() && !pte->isCppPrimitive() && !pte->customConversion()) - return QString::fromLatin1("Shiboken::Conversions::PrimitiveTypeConverter<%1>()").arg(pte->qualifiedCppName()); return convertersVariableName(type->targetLangPackage()) - + QLatin1Char('[') + getTypeIndexVariableName(type) + QLatin1Char(']'); + + 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()) + QLatin1Char('[') - + getTypeIndexVariableName(type) + QLatin1Char(']'); + return cppApiVariableName(type.typeEntry()->targetLangPackage()) + u'[' + + getTypeIndexVariableName(type) + "].type"_L1; } -static inline QString unknownOperator() { return QStringLiteral("__UNKNOWN_OPERATOR__"); } +QString ShibokenGenerator::cpythonTypeNameExt(const TypeEntryCPtr &type) +{ + return "Shiboken::Module::get("_L1 + cppApiVariableName(type->targetLangPackage()) + + u'[' + getTypeIndexVariableName(type) + "])"_L1; +} + +QString ShibokenGenerator::cpythonTypeNameExt(const AbstractMetaType &type) +{ + return u"Shiboken::Module::get("_s + cppApiVariableName(type.typeEntry()->targetLangPackage()) + + u'[' + getTypeIndexVariableName(type) + "])"_L1; +} -QString ShibokenGenerator::fixedCppTypeName(const CustomConversion::TargetToNativeConversion *toNative) +QString ShibokenGenerator::fixedCppTypeName(const TargetToNativeConversion &toNative) { - if (toNative->sourceType()) - return fixedCppTypeName(toNative->sourceType()); - return toNative->sourceTypeName(); + if (toNative.sourceType()) + return fixedCppTypeName(toNative.sourceType()); + return toNative.sourceTypeName(); } QString ShibokenGenerator::fixedCppTypeName(const AbstractMetaType &type) { @@ -828,22 +758,22 @@ QString ShibokenGenerator::fixedCppTypeName(const AbstractMetaType &type) static QString _fixedCppTypeName(QString typeName) { - typeName.remove(QLatin1Char(' ')); - typeName.replace(QLatin1Char('.'), QLatin1Char('_')); - typeName.replace(QLatin1Char(','), QLatin1Char('_')); - typeName.replace(QLatin1Char('<'), QLatin1Char('_')); - typeName.replace(QLatin1Char('>'), QLatin1Char('_')); - typeName.replace(QLatin1String("::"), QLatin1String("_")); - typeName.replace(QLatin1String("*"), QLatin1String("PTR")); - typeName.replace(QLatin1String("&"), QLatin1String("REF")); + typeName.remove(u' '); + typeName.replace(u'.', u'_'); + typeName.replace(u',', u'_'); + typeName.replace(u'<', u'_'); + typeName.replace(u'>', u'_'); + typeName.replace(u"::"_s, u"_"_s); + typeName.replace(u"*"_s, u"PTR"_s); + typeName.replace(u"&"_s, u"REF"_s); return typeName; } -QString ShibokenGenerator::fixedCppTypeName(const TypeEntry *type, QString typeName) +QString ShibokenGenerator::fixedCppTypeName(const TypeEntryCPtr &type, QString typeName) { if (typeName.isEmpty()) typeName = type->qualifiedCppName(); if (!type->generateCode()) { - typeName.prepend(QLatin1Char('_')); + typeName.prepend(u'_'); typeName.prepend(type->targetLangPackage()); } return _fixedCppTypeName(typeName); @@ -851,120 +781,66 @@ QString ShibokenGenerator::fixedCppTypeName(const TypeEntry *type, QString typeN QString ShibokenGenerator::pythonPrimitiveTypeName(const QString &cppTypeName) { - QString rv = primitiveTypesCorrespondences().value(cppTypeName, QString()); - if (rv.isEmpty()) { - // activate this when some primitive types are missing, - // i.e. when shiboken itself fails to build. - // In general, this is valid while just called by isNumeric() - // used on Qt5, 2015-09-20 - if (false) { - std::cerr << "primitive type not found: " << qPrintable(cppTypeName) << std::endl; - abort(); - } - } - return rv; -} - -QString ShibokenGenerator::pythonPrimitiveTypeName(const PrimitiveTypeEntry *type) -{ - while (type->basicReferencedTypeEntry()) - type = type->basicReferencedTypeEntry(); - return pythonPrimitiveTypeName(type->name()); -} - -static const QHash<QString, QString> &pythonOperators() -{ - static const QHash<QString, QString> result = { - // call operator - {QLatin1String("operator()"), QLatin1String("call")}, - // Arithmetic operators - {QLatin1String("operator+"), QLatin1String("add")}, - {QLatin1String("operator-"), QLatin1String("sub")}, - {QLatin1String("operator*"), QLatin1String("mul")}, - {QLatin1String("operator/"), QLatin1String("div")}, - {QLatin1String("operator%"), QLatin1String("mod")}, - // Inplace arithmetic operators - {QLatin1String("operator+="), QLatin1String("iadd")}, - {QLatin1String("operator-="), QLatin1String("isub")}, - {QLatin1String("operator++"), QLatin1String("iadd")}, - {QLatin1String("operator--"), QLatin1String("isub")}, - {QLatin1String("operator*="), QLatin1String("imul")}, - {QLatin1String("operator/="), QLatin1String("idiv")}, - {QLatin1String("operator%="), QLatin1String("imod")}, - // Bitwise operators - {QLatin1String("operator&"), QLatin1String("and")}, - {QLatin1String("operator^"), QLatin1String("xor")}, - {QLatin1String("operator|"), QLatin1String("or")}, - {QLatin1String("operator<<"), QLatin1String("lshift")}, - {QLatin1String("operator>>"), QLatin1String("rshift")}, - {QLatin1String("operator~"), QLatin1String("invert")}, - // Inplace bitwise operators - {QLatin1String("operator&="), QLatin1String("iand")}, - {QLatin1String("operator^="), QLatin1String("ixor")}, - {QLatin1String("operator|="), QLatin1String("ior")}, - {QLatin1String("operator<<="), QLatin1String("ilshift")}, - {QLatin1String("operator>>="), QLatin1String("irshift")}, - // Comparison operators - {QLatin1String("operator=="), QLatin1String("eq")}, - {QLatin1String("operator!="), QLatin1String("ne")}, - {QLatin1String("operator<"), QLatin1String("lt")}, - {QLatin1String("operator>"), QLatin1String("gt")}, - {QLatin1String("operator<="), QLatin1String("le")}, - {QLatin1String("operator>="), QLatin1String("ge")}, - }; - return result; -} - -QString ShibokenGenerator::pythonOperatorFunctionName(const QString &cppOpFuncName) -{ - QString value = pythonOperators().value(cppOpFuncName); - if (value.isEmpty()) - return unknownOperator(); - value.prepend(QLatin1String("__")); - value.append(QLatin1String("__")); - return value; + const auto &mapping = primitiveTypesCorrespondences(); + const auto it = mapping.constFind(cppTypeName); + if (it == mapping.cend()) + throw Exception(u"Primitive type not found: "_s + cppTypeName); + return it.value(); } QString ShibokenGenerator::pythonOperatorFunctionName(const AbstractMetaFunctionCPtr &func) { - QString op = pythonOperatorFunctionName(func->originalName()); - if (op == unknownOperator()) - qCWarning(lcShiboken).noquote().nospace() << msgUnknownOperator(func.data()); + QString op = Generator::pythonOperatorFunctionName(func->originalName()); + if (op.isEmpty()) { + qCWarning(lcShiboken).noquote().nospace() << msgUnknownOperator(func.get()); + return "__UNKNOWN_OPERATOR__"_L1; + } if (func->arguments().isEmpty()) { - if (op == QLatin1String("__sub__")) - op = QLatin1String("__neg__"); - else if (op == QLatin1String("__add__")) - op = QLatin1String("__pos__"); + if (op == u"__sub__") + op = u"__neg__"_s; + else if (op == u"__add__") + op = u"__pos__"_s; } else if (func->isStatic() && func->arguments().size() == 2) { // If a operator overload function has 2 arguments and // is static we assume that it is a reverse operator. - op = op.insert(2, QLatin1Char('r')); + op = op.insert(2, u'r'); } return op; } -QString ShibokenGenerator::pythonRichCompareOperatorId(const QString &cppOpFuncName) -{ - return QLatin1String("Py_") + pythonOperators().value(cppOpFuncName).toUpper(); -} - -QString ShibokenGenerator::pythonRichCompareOperatorId(const AbstractMetaFunctionCPtr &func) +bool ShibokenGenerator::isNumber(const QString &cpythonApiName) { - return pythonRichCompareOperatorId(func->originalName()); + return cpythonApiName == pyFloatT || cpythonApiName == pyLongT + || cpythonApiName == pyBoolT; } -bool ShibokenGenerator::isNumber(const QString &cpythonApiName) +static std::optional<TypeSystem::CPythonType> + targetLangApiCPythonType(const PrimitiveTypeEntryCPtr &t) { - return cpythonApiName == pyIntT() - || cpythonApiName == pyFloatT() || cpythonApiName == pyLongT() - || cpythonApiName == pyBoolT(); + if (!t->hasTargetLangApiType()) + return {}; + const auto cte = t->targetLangApiType(); + if (cte->type() != TypeEntry::PythonType) + return {}; + return std::static_pointer_cast<const PythonTypeEntry>(cte)->cPythonType(); } -bool ShibokenGenerator::isNumber(const TypeEntry *type) +bool ShibokenGenerator::isNumber(const TypeEntryCPtr &type) { if (!type->isPrimitive()) return false; - return isNumber(pythonPrimitiveTypeName(static_cast<const PrimitiveTypeEntry *>(type))); + const auto pte = basicReferencedTypeEntry(type); + const auto cPythonTypeOpt = targetLangApiCPythonType(pte); + // FIXME PYSIDE-1660: Return false here after making primitive types built-in? + if (!cPythonTypeOpt.has_value()) { + const auto &mapping = primitiveTypesCorrespondences(); + const auto it = mapping.constFind(pte->name()); + return it != mapping.cend() && isNumber(it.value()); + } + const auto cPythonType = cPythonTypeOpt.value(); + return cPythonType == TypeSystem::CPythonType::Bool + || cPythonType == TypeSystem::CPythonType::Float + || cPythonType == TypeSystem::CPythonType::Integer; } bool ShibokenGenerator::isNumber(const AbstractMetaType &type) @@ -972,12 +848,19 @@ bool ShibokenGenerator::isNumber(const AbstractMetaType &type) return isNumber(type.typeEntry()); } -bool ShibokenGenerator::isPyInt(const TypeEntry *type) +bool ShibokenGenerator::isPyInt(const TypeEntryCPtr &type) { if (!type->isPrimitive()) return false; - return pythonPrimitiveTypeName(static_cast<const PrimitiveTypeEntry *>(type)) - == QLatin1String("PyInt"); + const auto pte = basicReferencedTypeEntry(type); + const auto cPythonTypeOpt = targetLangApiCPythonType(pte); + // FIXME PYSIDE-1660: Return false here after making primitive types built-in? + if (!cPythonTypeOpt.has_value()) { + const auto &mapping = primitiveTypesCorrespondences(); + const auto it = mapping.constFind(pte->name()); + return it != mapping.cend() && it.value() == pyLongT; + } + return cPythonTypeOpt.value() == TypeSystem::CPythonType::Integer; } bool ShibokenGenerator::isPyInt(const AbstractMetaType &type) @@ -985,319 +868,267 @@ bool ShibokenGenerator::isPyInt(const AbstractMetaType &type) return isPyInt(type.typeEntry()); } -bool ShibokenGenerator::isValueTypeWithCopyConstructorOnly(const ApiExtractorResult &api, - const TypeEntry *type) -{ - if (!type || !type->isValue()) - return false; - auto klass = AbstractMetaClass::findClass(api.classes(), type); - return klass != nullptr && klass->isValueTypeWithCopyConstructorOnly(); -} - -bool ShibokenGenerator::isValueTypeWithCopyConstructorOnly(const ApiExtractorResult &api, - const AbstractMetaType &type) -{ - return type.typeEntry()->isValue() - && isValueTypeWithCopyConstructorOnly(api, type.typeEntry()); -} - -bool ShibokenGenerator::valueTypeWithCopyConstructorOnlyPassed(const ApiExtractorResult &api, - const AbstractMetaType &type) -{ - return (type.passByValue() || type.passByConstRef()) - && isValueTypeWithCopyConstructorOnly(api, type); -} - bool ShibokenGenerator::isNullPtr(const QString &value) { - return value == QLatin1String("0") || value == QLatin1String("nullptr") - || value == QLatin1String("NULLPTR") || value == QLatin1String("{}"); + return value == u"0" || value == u"nullptr" + || value == u"NULLPTR" || value == u"{}"; } -QString ShibokenGenerator::cpythonCheckFunction(AbstractMetaType metaType, - bool genericNumberType) const +QString ShibokenGenerator::cpythonCheckFunction(AbstractMetaType metaType) { - if (metaType.typeEntry()->isCustom()) { - auto customCheckResult = guessCPythonCheckFunction(metaType.typeEntry()->name()); - if (!customCheckResult.checkFunction.isEmpty()) - return customCheckResult.checkFunction; - if (customCheckResult.type.has_value()) - metaType = customCheckResult.type.value(); + const auto typeEntry = metaType.typeEntry(); + if (typeEntry->isCustom()) { + const auto cte = std::static_pointer_cast<const CustomTypeEntry>(typeEntry); + if (cte->hasCheckFunction()) + return cte->checkFunction(); + throw Exception(msgUnknownCheckFunction(typeEntry)); } if (metaType.isExtendedCppPrimitive()) { if (metaType.isCString()) - return QLatin1String("Shiboken::String::check"); + return u"Shiboken::String::check"_s; if (metaType.isVoidPointer()) - return QLatin1String("PyObject_Check"); - return cpythonCheckFunction(metaType.typeEntry(), genericNumberType); + return u"true"_s; + return cpythonCheckFunction(typeEntry); } - auto typeEntry = metaType.typeEntry(); + if (typeEntry->isContainer()) { - QString typeCheck = QLatin1String("Shiboken::Conversions::"); + QString typeCheck = u"Shiboken::Conversions::"_s; ContainerTypeEntry::ContainerKind type = - static_cast<const ContainerTypeEntry *>(typeEntry)->containerKind(); + std::static_pointer_cast<const ContainerTypeEntry>(typeEntry)->containerKind(); if (type == ContainerTypeEntry::ListContainer - || type == ContainerTypeEntry::StringListContainer - || type == ContainerTypeEntry::LinkedListContainer - || type == ContainerTypeEntry::VectorContainer - || type == ContainerTypeEntry::StackContainer - || type == ContainerTypeEntry::SetContainer - || type == ContainerTypeEntry::QueueContainer) { + || type == ContainerTypeEntry::SetContainer) { + const QString containerType = type == ContainerTypeEntry::SetContainer + ? u"Iterable"_s : u"Sequence"_s; const AbstractMetaType &type = metaType.instantiations().constFirst(); if (type.isPointerToWrapperType()) { - typeCheck += QString::fromLatin1("checkSequenceTypes(%1, ").arg(cpythonTypeNameExt(type)); + typeCheck += u"check"_s + containerType + u"Types("_s + + cpythonTypeNameExt(type) + u", "_s; } else if (type.isWrapperType()) { - typeCheck += QLatin1String("convertibleSequenceTypes(reinterpret_cast<SbkObjectType *>("); - typeCheck += cpythonTypeNameExt(type); - typeCheck += QLatin1String("), "); + typeCheck += u"convertible"_s + containerType + + u"Types("_s + cpythonTypeNameExt(type) + u", "_s; } else { - typeCheck += QString::fromLatin1("convertibleSequenceTypes(%1, ").arg(converterObject(type)); + typeCheck += u"convertible"_s + containerType + + u"Types("_s + converterObject(type) + u", "_s; } } else if (type == ContainerTypeEntry::MapContainer || type == ContainerTypeEntry::MultiMapContainer - || type == ContainerTypeEntry::HashContainer - || type == ContainerTypeEntry::MultiHashContainer || type == ContainerTypeEntry::PairContainer) { - QString pyType = (type == ContainerTypeEntry::PairContainer) ? QLatin1String("Pair") : QLatin1String("Dict"); + + QString pyType; + if (type == ContainerTypeEntry::PairContainer) + pyType = u"Pair"_s; + else if (type == ContainerTypeEntry::MultiMapContainer) + pyType = u"MultiDict"_s; + else + pyType = u"Dict"_s; + const AbstractMetaType &firstType = metaType.instantiations().constFirst(); const AbstractMetaType &secondType = metaType.instantiations().constLast(); if (firstType.isPointerToWrapperType() && secondType.isPointerToWrapperType()) { - typeCheck += QString::fromLatin1("check%1Types(%2, %3, ") - .arg(pyType, cpythonTypeNameExt(firstType), cpythonTypeNameExt(secondType)); + QTextStream(&typeCheck) << "check" << pyType << "Types(" + << cpythonTypeNameExt(firstType) << ", " + << cpythonTypeNameExt(secondType) << ", "; } else { - typeCheck += QString::fromLatin1("convertible%1Types(%2, %3, %4, %5, ") - .arg(pyType, converterObject(firstType), - firstType.isPointerToWrapperType() ? QLatin1String("true") : QLatin1String("false"), - converterObject(secondType), - secondType.isPointerToWrapperType() ? QLatin1String("true") : QLatin1String("false")); + QTextStream(&typeCheck) << "convertible" << pyType << "Types(" + << converterObject(firstType) << ", " + << (firstType.isPointerToWrapperType() ? "true" : "false") + << ", " << converterObject(secondType) << ", " + << (secondType.isPointerToWrapperType() ? "true" :"false") + << ", "; } } return typeCheck; } - return cpythonCheckFunction(typeEntry, genericNumberType); + return cpythonCheckFunction(typeEntry); } -QString ShibokenGenerator::cpythonCheckFunction(const TypeEntry *type, bool genericNumberType) const +QString ShibokenGenerator::cpythonCheckFunction(TypeEntryCPtr type) { if (type->isCustom()) { - AbstractMetaType metaType; - auto customCheckResult = guessCPythonCheckFunction(type->name()); - if (customCheckResult.type.has_value()) - return cpythonCheckFunction(customCheckResult.type.value(), genericNumberType); - return customCheckResult.checkFunction; + const auto cte = std::static_pointer_cast<const CustomTypeEntry>(type); + if (cte->hasCheckFunction()) + return cte->checkFunction(); + throw Exception(msgUnknownCheckFunction(type)); } if (type->isEnum() || type->isFlags() || type->isWrapperType()) - return QString::fromLatin1("SbkObject_TypeCheck(%1, ").arg(cpythonTypeNameExt(type)); - if (type->isExtendedCppPrimitive()) { - return pythonPrimitiveTypeName(static_cast<const PrimitiveTypeEntry *>(type)) - + QLatin1String("_Check"); - } - QString typeCheck; - if (type->targetLangApiName() == type->name()) - typeCheck = cpythonIsConvertibleFunction(api(), type); - else if (type->targetLangApiName() == QLatin1String("PyUnicode")) - typeCheck = QLatin1String("Shiboken::String::check"); - else - typeCheck = type->targetLangApiName() + QLatin1String("_Check"); - return typeCheck; -} - -ShibokenGenerator::CPythonCheckFunctionResult - ShibokenGenerator::guessCPythonCheckFunction(const QString &type) -{ - // PYSIDE-795: We abuse PySequence for iterables. - // This part handles the overrides in the XML files. - if (type == cPySequenceT()) - return {QLatin1String("Shiboken::String::checkIterable"), {}}; - - if (type == cPyTypeObjectT()) - return {QLatin1String("PyType_Check"), {}}; + return u"SbkObject_TypeCheck("_s + cpythonTypeNameExt(type) + u", "_s; - if (type == cPyBufferT()) - return {QLatin1String("Shiboken::Buffer::checkType"), {}}; + if (type->isPrimitive()) + type = basicReferencedTypeEntry(type); - if (type == pyStrT()) - return {QLatin1String("Shiboken::String::check"), {}}; - - if (type == cPyArrayObjectT()) - return {QLatin1String("PyArray_Check"), {}}; - - // PYSIDE-1499: We replace some strings by path objects. - if (type == pyPathLikeT()) - return {QLatin1String("Shiboken::String::checkPath"), {}}; + if (auto tla = type->targetLangApiType()) { + if (tla->hasCheckFunction()) + return tla->checkFunction(); + } - CPythonCheckFunctionResult result; - result.type = buildAbstractMetaTypeFromString(type); + if (isExtendedCppPrimitive(type)) + return pythonPrimitiveTypeName(type->name()) + u"_Check"_s; - if (!result.type.has_value()) { - result.checkFunction = type + QLatin1String("_Check"); - } else if (result.type->typeEntry()->isCustom()) { - auto ct = static_cast<const CustomTypeEntry *>(result.type->typeEntry()); - result.checkFunction = ct->checkFunction(); - if (result.checkFunction.isEmpty()) - result.checkFunction = type + QLatin1String("_Check"); - } - return result; + return cpythonIsConvertibleFunction(type); } -QString ShibokenGenerator::cpythonIsConvertibleFunction(const ApiExtractorResult &api, const TypeEntry *type, - bool /* genericNumberType */, - bool /* checkExact */) +QString ShibokenGenerator::cpythonIsConvertibleFunction(const TypeEntryCPtr &type) { if (type->isWrapperType()) { - QString result = QLatin1String("Shiboken::Conversions::"); - result += (type->isValue() && !isValueTypeWithCopyConstructorOnly(api, type)) - ? QLatin1String("isPythonToCppValueConvertible") - : QLatin1String("isPythonToCppPointerConvertible"); - result += QLatin1String("(reinterpret_cast<SbkObjectType *>(") - + cpythonTypeNameExt(type) + QLatin1String("), "); + QString result = u"Shiboken::Conversions::"_s; + bool isValue = false; + if (type->isValue()) { + const auto cte = std::static_pointer_cast<const ComplexTypeEntry>(type); + isValue = !cte->isValueTypeWithCopyConstructorOnly(); + } + result += isValue ? u"isPythonToCppValueConvertible"_s + : u"isPythonToCppPointerConvertible"_s; + result += u"("_s + cpythonTypeNameExt(type) + u", "_s; return result; } return QString::fromLatin1("Shiboken::Conversions::isPythonToCppConvertible(%1, ") .arg(converterObject(type)); } -QString ShibokenGenerator::cpythonIsConvertibleFunction(AbstractMetaType metaType, - bool /* genericNumberType */) const + +QString ShibokenGenerator::cpythonIsConvertibleFunction(const AbstractMetaType &metaType) { - if (metaType.typeEntry()->isCustom()) { - auto customCheckResult = guessCPythonCheckFunction(metaType.typeEntry()->name()); - if (!customCheckResult.checkFunction.isEmpty()) - return customCheckResult.checkFunction; - if (customCheckResult.type.has_value()) - metaType = customCheckResult.type.value(); + const auto typeEntry = metaType.typeEntry(); + if (typeEntry->isCustom()) { + const auto cte = std::static_pointer_cast<const CustomTypeEntry>(typeEntry); + if (cte->hasCheckFunction()) + return cte->checkFunction(); + throw Exception(msgUnknownCheckFunction(typeEntry)); } - QString result = QLatin1String("Shiboken::Conversions::"); + QString result = u"Shiboken::Conversions::"_s; + if (metaType.generateOpaqueContainer()) { + result += u"pythonToCppReferenceConversion("_s + + converterObject(metaType) + u", "_s; + return result; + } if (metaType.isWrapperType()) { - if (metaType.isPointer() || isValueTypeWithCopyConstructorOnly(api(), metaType)) - result += QLatin1String("isPythonToCppPointerConvertible"); - else if (metaType.referenceType() == LValueReference) - result += QLatin1String("isPythonToCppReferenceConvertible"); - else - result += QLatin1String("isPythonToCppValueConvertible"); - result += QLatin1String("(reinterpret_cast<SbkObjectType *>(") - + cpythonTypeNameExt(metaType) + QLatin1String("), "); + if (metaType.isPointer() || metaType.isValueTypeWithCopyConstructorOnly()) { + result += u"pythonToCppPointerConversion"_s; + } else if (metaType.referenceType() == LValueReference + || (metaType.referenceType() == RValueReference && typeEntry->isObject())) { + result += u"pythonToCppReferenceConversion"_s; + } else { + result += u"pythonToCppValueConversion"_s; + } + result += u'(' + cpythonTypeNameExt(metaType) + u", "_s; return result; } - result += QLatin1String("isPythonToCppConvertible(") + converterObject(metaType); + result += u"pythonToCppConversion("_s + converterObject(metaType); // Write out array sizes if known const AbstractMetaTypeList nestedArrayTypes = metaType.nestedArrayTypes(); if (!nestedArrayTypes.isEmpty() && nestedArrayTypes.constLast().isCppPrimitive()) { const int dim1 = metaType.arrayElementCount(); const int dim2 = nestedArrayTypes.constFirst().isArray() ? nestedArrayTypes.constFirst().arrayElementCount() : -1; - result += QLatin1String(", ") + QString::number(dim1) - + QLatin1String(", ") + QString::number(dim2); + result += u", "_s + QString::number(dim1) + + u", "_s + QString::number(dim2); } - result += QLatin1String(", "); + result += u", "_s; return result; } -QString ShibokenGenerator::cpythonIsConvertibleFunction(const AbstractMetaArgument &metaArg, - bool genericNumberType) const +QString ShibokenGenerator::cpythonIsConvertibleFunction(const AbstractMetaArgument &metaArg) { - return cpythonIsConvertibleFunction(metaArg.type(), genericNumberType); + return cpythonIsConvertibleFunction(metaArg.type()); } -QString ShibokenGenerator::cpythonToCppConversionFunction(const AbstractMetaClass *metaClass) +QString ShibokenGenerator::cpythonToCppConversionFunction(const AbstractMetaClassCPtr &metaClass) { - return QLatin1String("Shiboken::Conversions::pythonToCppPointer(reinterpret_cast<SbkObjectType *>(") - + cpythonTypeNameExt(metaClass->typeEntry()) + QLatin1String("), "); + 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 QLatin1String("Shiboken::Conversions::pythonToCpp") - + (type.isPointer() ? QLatin1String("Pointer") : QLatin1String("Copy")) - + QLatin1String("(reinterpret_cast<SbkObjectType *>(") - + cpythonTypeNameExt(type) + QLatin1String("), "); + 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; if (type.referenceType() == LValueReference && !(type.isValue() && type.isConstant()) && !type.isPointer()) { - conversion = QLatin1String("reference"); + conversion = u"reference"_s; } else if (type.isValue() || type.isSmartPointer()) { - conversion = QLatin1String("copy"); + conversion = u"copy"_s; } else { - conversion = QLatin1String("pointer"); + conversion = u"pointer"_s; } - QString result = QLatin1String("Shiboken::Conversions::") + conversion - + QLatin1String("ToPython(reinterpret_cast<SbkObjectType *>(") - + cpythonTypeNameExt(type) + QLatin1String("), "); - if (conversion != QLatin1String("pointer")) - result += QLatin1Char('&'); + QString result = u"Shiboken::Conversions::"_s + conversion + + u"ToPython("_s + + cpythonTypeNameExt(type) + u", "_s; + if (conversion != u"pointer") + result += u'&'; return result; } - return QStringLiteral("Shiboken::Conversions::copyToPython(%1, %2") - .arg(converterObject(type), - (type.isCString() || type.isVoidPointer()) ? QString() : QLatin1String("&")); + + 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()); } -QString ShibokenGenerator::cpythonToPythonConversionFunction(const TypeEntry *type) +QString ShibokenGenerator::cpythonToPythonConversionFunction(const TypeEntryCPtr &type) { if (type->isWrapperType()) { - const QString conversion = type->isValue() ? QLatin1String("copy") : QLatin1String("pointer"); - QString result = QLatin1String("Shiboken::Conversions::") + conversion - + QLatin1String("ToPython(reinterpret_cast<SbkObjectType *>(") + cpythonTypeNameExt(type) - + QLatin1String("), "); - if (conversion != QLatin1String("pointer")) - result += QLatin1Char('&'); + const QString conversion = type->isValue() ? u"copy"_s : u"pointer"_s; + QString result = u"Shiboken::Conversions::"_s + conversion + + u"ToPython("_s + cpythonTypeNameExt(type) + + u", "_s; + if (conversion != u"pointer") + result += u'&'; return result; } - return QStringLiteral("Shiboken::Conversions::copyToPython(%1, &").arg(converterObject(type)); + return u"Shiboken::Conversions::copyToPython("_s + + converterObject(type) + u", &"_s; } QString ShibokenGenerator::argumentString(const AbstractMetaFunctionCPtr &func, const AbstractMetaArgument &argument, Options options) const { - QString modified_type; - if (!(options & OriginalTypeDescription)) - modified_type = func->typeReplaced(argument.argumentIndex() + 1); - QString arg; + auto type = options.testFlag(OriginalTypeDescription) + ? argument.type() : argument.modifiedType(); - if (modified_type.isEmpty()) - arg = translateType(argument.type(), func->implementingClass(), options); - else - arg = modified_type.replace(QLatin1Char('$'), QLatin1Char('.')); + + QString arg = translateType(type, func->implementingClass(), options); + + if (argument.isTypeModified()) + arg.replace(u'$', u'.'); // Haehh? // "int a", "int a[]" - const int arrayPos = arg.indexOf(QLatin1Char('[')); + const auto arrayPos = arg.indexOf(u'['); if (arrayPos != -1) - arg.insert(arrayPos, QLatin1Char(' ') + argument.name()); + arg.insert(arrayPos, u' ' + argument.name()); else - arg.append(QLatin1Char(' ') + argument.name()); + arg.append(u' ' + argument.name()); if ((options & Generator::SkipDefaultValues) != Generator::SkipDefaultValues && !argument.originalDefaultValueExpression().isEmpty()) { QString default_value = argument.originalDefaultValueExpression(); - if (default_value == QLatin1String("NULL")) - default_value = QLatin1String(NULL_PTR); + if (default_value == u"NULL") + default_value = NULL_PTR; //WORKAROUND: fix this please - if (default_value.startsWith(QLatin1String("new "))) + if (default_value.startsWith(u"new ")) default_value.remove(0, 4); - arg += QLatin1String(" = ") + default_value; + arg += u" = "_s + default_value; } return arg; @@ -1315,21 +1146,23 @@ void ShibokenGenerator::writeFunctionArguments(TextStream &s, const AbstractMetaFunctionCPtr &func, Options options) const { - AbstractMetaArgumentList arguments = func->arguments(); - int argUsed = 0; - for (int i = 0; i < arguments.size(); ++i) { - if ((options & Generator::SkipRemovedArguments) && func->argumentRemoved(i+1)) + if (func->isUserAddedPythonOverride()) { + s << "Shiboken::GilState &gil, PyObject *" << PYTHON_OVERRIDE_VAR; + argUsed += 2; + } + for (const auto &arg : func->arguments()) { + if (options.testFlag(Generator::SkipRemovedArguments) && arg.isModifiedRemoved()) continue; if (argUsed != 0) s << ", "; - writeArgument(s, func, arguments[i], options); + writeArgument(s, func, arg, options); argUsed++; } } -GeneratorContext ShibokenGenerator::contextForClass(const AbstractMetaClass *c) const +GeneratorContext ShibokenGenerator::contextForClass(const AbstractMetaClassCPtr &c) const { GeneratorContext result = Generator::contextForClass(c); if (shouldGenerateCppWrapper(c)) { @@ -1341,9 +1174,8 @@ GeneratorContext ShibokenGenerator::contextForClass(const AbstractMetaClass *c) QString ShibokenGenerator::functionReturnType(const AbstractMetaFunctionCPtr &func, Options options) const { - QString modifiedReturnType = QString(func->typeReplaced(0)); - if (!modifiedReturnType.isEmpty() && !(options & OriginalTypeDescription)) - return modifiedReturnType; + if (func->isTypeModified() && !options.testFlag(OriginalTypeDescription)) + return func->modifiedTypeName(); return translateType(func->type(), func->implementingClass(), options); } @@ -1355,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 @@ -1386,14 +1220,18 @@ void ShibokenGenerator::writeArgumentNames(TextStream &s, int argCount = 0; for (const auto &argument : arguments) { const int index = argument.argumentIndex() + 1; - if ((options & Generator::SkipRemovedArguments) && (func->argumentRemoved(index))) + if (options.testFlag(Generator::SkipRemovedArguments) && argument.isModifiedRemoved()) continue; + const auto &type = argument.type(); + if (argCount > 0) + s << ", "; + const bool isVirtualCall = options.testFlag(Option::VirtualCall); + const bool useStdMove = isVirtualCall && type.isUniquePointer() && type.passByValue(); + s << (useStdMove ? stdMove(argument.name()) : argument.name()); - s << ((argCount > 0) ? ", " : "") << argument.name(); - - if (((options & Generator::VirtualCall) == 0) - && (!func->conversionRule(TypeSystem::NativeCode, index).isEmpty() - || !func->conversionRule(TypeSystem::TargetLangCode, index).isEmpty()) + if (!isVirtualCall + && (func->hasConversionRule(TypeSystem::NativeCode, index) + || func->hasConversionRule(TypeSystem::TargetLangCode, index)) && !func->isConstructor()) { s << CONV_RULE_OUT_VAR_SUFFIX; } @@ -1412,55 +1250,18 @@ void ShibokenGenerator::writeFunctionCall(TextStream &s, s << ')'; } -void ShibokenGenerator::writeUnusedVariableCast(TextStream &s, const QString &variableName) -{ - s << "SBK_UNUSED(" << variableName<< ")\n"; -} - -static bool filterFunction(const AbstractMetaFunctionCPtr &func, bool avoidProtectedHack) -{ - switch (func->functionType()) { - case AbstractMetaFunction::DestructorFunction: - case AbstractMetaFunction::SignalFunction: - case AbstractMetaFunction::GetAttroFunction: - case AbstractMetaFunction::SetAttroFunction: - return false; - default: - break; - } - if (func->usesRValueReferences()) - return false; - if (func->isModifiedRemoved() && !func->isAbstract() - && (!avoidProtectedHack || !func->isProtected())) { - return false; - } - return true; -} - -AbstractMetaFunctionCList ShibokenGenerator::filterFunctions(const AbstractMetaClass *metaClass) const -{ - AbstractMetaFunctionCList result; - const AbstractMetaFunctionCList &funcs = metaClass->functions(); - result.reserve(funcs.size()); - for (const auto &func : funcs) { - if (filterFunction(func, avoidProtectedHack())) - result.append(func); - } - return result; -} - 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)) + if (!shouldGenerate(metaClass->typeEntry())) continue; const auto &overloads = metaClass->operatorOverloads(OperatorQueryOption::ConversionOp); for (const auto &convOp : overloads) { // Get only the conversion operators that return a type from another module, // that are value-types and were not removed in the type system. - const TypeEntry *convType = convOp->type().typeEntry(); + const auto convType = convOp->type().typeEntry(); if (convType->generateCode() || !convType->isValue() || convOp->isModifiedRemoved()) continue; @@ -1470,15 +1271,13 @@ ShibokenGenerator::ExtendedConverterData ShibokenGenerator::getExtendedConverter return extConvs; } -QList<const CustomConversion *> ShibokenGenerator::getPrimitiveCustomConversions() +QList<CustomConversionPtr> ShibokenGenerator::getPrimitiveCustomConversions() { - QList<const CustomConversion *> conversions; - const PrimitiveTypeEntryList &primitiveTypeList = primitiveTypes(); - for (const PrimitiveTypeEntry *type : primitiveTypeList) { - if (!shouldGenerateTypeEntry(type) || !type->isUserPrimitive() || !type->customConversion()) - continue; - - conversions << type->customConversion(); + QList<CustomConversionPtr> conversions; + const auto &primitiveTypeList = primitiveTypes(); + for (const auto &type : primitiveTypeList) { + if (type->shouldGenerate() && isUserPrimitive(type) && type->hasCustomConversion()) + conversions << type->customConversion(); } return conversions; } @@ -1491,20 +1290,20 @@ 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(); - while (str.at(pos) == QLatin1Char(' ') || str.at(pos) == QLatin1Char('\t')) + while (str.at(pos) == u' ' || str.at(pos) == u'\t') ++pos; - if (str.at(pos) == QLatin1Char('(')) + if (str.at(pos) == u'(') ++pos; int begin = pos; int counter = 1; while (counter != 0) { - if (str.at(pos) == QLatin1Char('(')) + if (str.at(pos) == u'(') ++counter; - else if (str.at(pos) == QLatin1Char(')')) + else if (str.at(pos) == u')') --counter; ++pos; } @@ -1529,14 +1328,13 @@ void ShibokenGenerator::processClassCodeSnip(QString &code, const GeneratorConte auto metaClass = context.metaClass(); // Replace template variable by the Python Type object // for the class context in which the variable is used. - code.replace(QLatin1String("%PYTHONTYPEOBJECT"), - cpythonTypeName(metaClass) + QLatin1String("->type")); - const QString className = context.useWrapper() - ? context.wrapperName() : metaClass->qualifiedCppName(); - code.replace(QLatin1String("%TYPE"), className); - code.replace(QLatin1String("%CPPTYPE"), metaClass->name()); + code.replace(u"%PYTHONTYPEOBJECT"_s, + u"(*"_s + cpythonTypeName(metaClass) + u')'); + const QString className = context.effectiveClassName(); + code.replace(u"%TYPE"_s, className); + code.replace(u"%CPPTYPE"_s, metaClass->name()); - processCodeSnip(code); + processCodeSnip(code, context.effectiveClassName()); } void ShibokenGenerator::processCodeSnip(QString &code) const @@ -1554,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, @@ -1563,38 +1370,30 @@ ShibokenGenerator::ArgumentVarReplacementList TypeSystem::Language convLang = (language == TypeSystem::TargetLangCode) ? TypeSystem::NativeCode : TypeSystem::TargetLangCode; int removed = 0; - for (int i = 0; i < func->arguments().size(); ++i) { + for (qsizetype i = 0; i < func->arguments().size(); ++i) { const AbstractMetaArgument &arg = func->arguments().at(i); QString argValue; if (language == TypeSystem::TargetLangCode) { - bool hasConversionRule = !func->conversionRule(convLang, i+1).isEmpty(); - const bool argRemoved = func->argumentRemoved(i+1); + const bool hasConversionRule = func->hasConversionRule(convLang, i + 1); + const bool argRemoved = arg.isModifiedRemoved(); if (argRemoved) ++removed; if (argRemoved && hasConversionRule) - argValue = arg.name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX); + argValue = arg.name() + CONV_RULE_OUT_VAR_SUFFIX; else if (argRemoved || (lastArg && arg.argumentIndex() > lastArg->argumentIndex())) - argValue = QLatin1String(CPP_ARG_REMOVED) + QString::number(i); + argValue = CPP_ARG_REMOVED(i); if (!argRemoved && argValue.isEmpty()) { int argPos = i - removed; - AbstractMetaType type = arg.type(); - QString typeReplaced = func->typeReplaced(arg.argumentIndex() + 1); - if (!typeReplaced.isEmpty()) { - auto builtType = buildAbstractMetaTypeFromString(typeReplaced); - if (builtType.has_value()) - type = builtType.value(); - } + AbstractMetaType type = arg.modifiedType(); if (type.typeEntry()->isCustom()) { argValue = usePyArgs - ? pythonArgsAt(argPos) : QLatin1String(PYTHON_ARG); + ? pythonArgsAt(argPos) : PYTHON_ARG; } else { argValue = hasConversionRule - ? arg.name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX) - : QLatin1String(CPP_ARG) + QString::number(argPos); - if (type.isWrapperType()) { - if (type.referenceType() == LValueReference && !type.isPointer()) - argValue.prepend(QLatin1Char('*')); - } + ? arg.name() + CONV_RULE_OUT_VAR_SUFFIX + : CPP_ARG_N(argPos); + const auto generatorArg = GeneratorArgument::fromMetaType(type); + AbstractMetaType::applyDereference(&argValue, generatorArg.indirections); } } } else { @@ -1632,138 +1431,153 @@ void ShibokenGenerator::writeCodeSnips(TextStream &s, s << "// Begin code injection\n" << code << "// End of code injection\n\n"; } +static void replacePyArg0(TypeSystem::Language language, QString *code) +{ + static constexpr auto pyArg0 = "%PYARG_0"_L1; + + if (!code->contains(pyArg0)) + return; + if (language != TypeSystem::NativeCode) { + code->replace(pyArg0, PYTHON_RETURN_VAR); + return; + } + + // pyResult is an AutoDecRef in overridden methods of wrapper classes which + // has a cast operator for PyObject *. This may however not work in all + // situations (fex _PyVarObject_CAST(op) defined as ((PyVarObject*)(op))). + // Append ".object()" unless it is followed by a '.' indicating explicit + // AutoDecRef member invocation. + static const QString pyObject = PYTHON_RETURN_VAR + u".object()"_s; + qsizetype pos{}; + while ( (pos = code->indexOf(pyArg0)) >= 0) { + const auto next = pos + pyArg0.size(); + const bool memberInvocation = next < code->size() && code->at(next) == u'.'; + code->replace(pos, pyArg0.size(), + memberInvocation ? PYTHON_RETURN_VAR : pyObject); + } +} + void ShibokenGenerator::writeCodeSnips(TextStream &s, const CodeSnipList &codeSnips, TypeSystem::CodeSnipPosition position, TypeSystem::Language language, const AbstractMetaFunctionCPtr &func, + bool usePyArgs, const AbstractMetaArgument *lastArg) const { QString code = getCodeSnippets(codeSnips, position, language); if (code.isEmpty()) return; - // Calculate the real number of arguments. - int argsRemoved = 0; - for (int i = 0; i < func->arguments().size(); i++) { - if (func->argumentRemoved(i+1)) - argsRemoved++; - } - - const auto &groups = func->implementingClass() - ? getFunctionGroups(func->implementingClass()) - : getGlobalFunctionGroups(); - OverloadData od(groups[func->name()], api()); - bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(od); - // Replace %PYARG_# variables. - code.replace(QLatin1String("%PYARG_0"), QLatin1String(PYTHON_RETURN_VAR)); + 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, QLatin1String(PYTHON_ARGS) + QLatin1String("[\\1-1]")); + 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(QLatin1String("%PYARG_1"), QLatin1String(PYTHON_ARG)); + code.replace(u"%PYARG_1"_s, PYTHON_ARG); } } else { // Replaces the simplest case of attribution to a // Python argument on the binding virtual method. - static const QRegularExpression pyArgsAttributionRegex(QStringLiteral("%PYARG_(\\d+)\\s*=[^=]\\s*([^;]+)")); + static const QRegularExpression pyArgsAttributionRegex("%PYARG_(\\d+)\\s*=[^=]\\s*([^;]+)"_L1); Q_ASSERT(pyArgsAttributionRegex.isValid()); - code.replace(pyArgsAttributionRegex, QLatin1String("PyTuple_SET_ITEM(") - + QLatin1String(PYTHON_ARGS) + QLatin1String(", \\1-1, \\2)")); - code.replace(pyArgsRegex, QLatin1String("PyTuple_GET_ITEM(") - + QLatin1String(PYTHON_ARGS) + QLatin1String(", \\1-1)")); + code.replace(pyArgsAttributionRegex, u"PyTuple_SET_ITEM("_s + + PYTHON_ARGS + u".object(), \\1-1, \\2)"_s); + code.replace(pyArgsRegex, u"PyTuple_GET_ITEM("_s + + PYTHON_ARGS + u".object(), \\1-1)"_s); } // Replace %ARG#_TYPE variables. const AbstractMetaArgumentList &arguments = func->arguments(); for (const AbstractMetaArgument &arg : arguments) { - QString argTypeVar = QStringLiteral("%ARG%1_TYPE").arg(arg.argumentIndex() + 1); + QString argTypeVar = u"%ARG"_s + QString::number(arg.argumentIndex() + 1) + + u"_TYPE"_s; QString argTypeVal = arg.type().cppSignature(); code.replace(argTypeVar, argTypeVal); } - static const QRegularExpression cppArgTypeRegexCheck(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. if (func->isConstructor()) { - code.replace(QLatin1String("%0."), QLatin1String("cptr->")); - code.replace(QLatin1String("%0"), QLatin1String("cptr")); + code.replace(u"%0."_s, u"cptr->"_s); + code.replace(u"%0"_s, u"cptr"_s); } else if (!func->isVoid()) { QString returnValueOp = func->type().isPointerToWrapperType() - ? QLatin1String("%1->") : QLatin1String("%1."); + ? u"%1->"_s : u"%1."_s; if (func->type().isWrapperType()) - code.replace(QLatin1String("%0."), returnValueOp.arg(QLatin1String(CPP_RETURN_VAR))); - code.replace(QLatin1String("%0"), QLatin1String(CPP_RETURN_VAR)); + code.replace(u"%0."_s, returnValueOp.arg(CPP_RETURN_VAR)); + code.replace(u"%0"_s, CPP_RETURN_VAR); } // Replace template variable for self Python object. QString pySelf = language == TypeSystem::NativeCode - ? QLatin1String("pySelf") : QLatin1String("self"); - code.replace(QLatin1String("%PYSELF"), pySelf); + ? u"pySelf"_s : u"self"_s; + code.replace(u"%PYSELF"_s, pySelf); // Replace template variable for a pointer to C++ of this object. if (func->implementingClass()) { - QString replacement = func->isStatic() ? QLatin1String("%1::") : QLatin1String("%1->"); + QString replacement = func->isStatic() ? u"%1::"_s : u"%1->"_s; QString cppSelf; if (func->isStatic()) cppSelf = func->ownerClass()->qualifiedCppName(); else if (language == TypeSystem::NativeCode) - cppSelf = QLatin1String("this"); + cppSelf = u"this"_s; else - cppSelf = QLatin1String(CPP_SELF_VAR); + cppSelf = CPP_SELF_VAR; // On comparison operator CPP_SELF_VAR is always a reference. if (func->isComparisonOperator()) - replacement = QLatin1String("%1."); + replacement = u"%1."_s; if (func->isVirtual() && !func->isAbstract() && (!avoidProtectedHack() || !func->isProtected())) { QString methodCallArgs = getArgumentsFromMethodCall(code); if (!methodCallArgs.isEmpty()) { - const QString pattern = QStringLiteral("%CPPSELF.%FUNCTION_NAME(%1)").arg(methodCallArgs); - if (func->name() == QLatin1String("metaObject")) { + const QString pattern = u"%CPPSELF.%FUNCTION_NAME("_s + methodCallArgs + u')'; + QString replacement = u"(Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>("_s + + pySelf + u")) ? "_s; + if (func->name() == u"metaObject") { QString wrapperClassName = wrapperName(func->ownerClass()); QString cppSelfVar = avoidProtectedHack() - ? QLatin1String("%CPPSELF") - : QStringLiteral("reinterpret_cast<%1 *>(%CPPSELF)").arg(wrapperClassName); - code.replace(pattern, - QString::fromLatin1("(Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(%1))" - " ? %2->::%3::%FUNCTION_NAME(%4)" - " : %CPPSELF.%FUNCTION_NAME(%4))").arg(pySelf, cppSelfVar, wrapperClassName, methodCallArgs)); + ? u"%CPPSELF"_s + : u"reinterpret_cast<"_s + wrapperClassName + u" *>(%CPPSELF)"_s; + replacement += cppSelfVar + u"->::"_s + wrapperClassName + + u"::%FUNCTION_NAME("_s + methodCallArgs + + u") : %CPPSELF.%FUNCTION_NAME("_s + methodCallArgs + u"))"_s; } else { - code.replace(pattern, - QString::fromLatin1("(Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(%1))" - " ? %CPPSELF->::%TYPE::%FUNCTION_NAME(%2)" - " : %CPPSELF.%FUNCTION_NAME(%2))").arg(pySelf, methodCallArgs)); + replacement += u"%CPPSELF->::%TYPE::%FUNCTION_NAME("_s + methodCallArgs + + u") : %CPPSELF.%FUNCTION_NAME("_s + methodCallArgs + u"))"_s; } + code.replace(pattern, replacement); } } - code.replace(QLatin1String("%CPPSELF."), replacement.arg(cppSelf)); - code.replace(QLatin1String("%CPPSELF"), cppSelf); + code.replace(u"%CPPSELF."_s, replacement.arg(cppSelf)); + code.replace(u"%CPPSELF"_s, cppSelf); - if (code.indexOf(QLatin1String("%BEGIN_ALLOW_THREADS")) > -1) { - if (code.count(QLatin1String("%BEGIN_ALLOW_THREADS")) == code.count(QLatin1String("%END_ALLOW_THREADS"))) { - code.replace(QLatin1String("%BEGIN_ALLOW_THREADS"), QLatin1String(BEGIN_ALLOW_THREADS)); - code.replace(QLatin1String("%END_ALLOW_THREADS"), QLatin1String(END_ALLOW_THREADS)); + if (code.indexOf(u"%BEGIN_ALLOW_THREADS") > -1) { + if (code.count(u"%BEGIN_ALLOW_THREADS"_s) == code.count(u"%END_ALLOW_THREADS"_s)) { + code.replace(u"%BEGIN_ALLOW_THREADS"_s, BEGIN_ALLOW_THREADS); + code.replace(u"%END_ALLOW_THREADS"_s, END_ALLOW_THREADS); } else { qCWarning(lcShiboken) << "%BEGIN_ALLOW_THREADS and %END_ALLOW_THREADS mismatch"; } @@ -1772,11 +1586,11 @@ void ShibokenGenerator::writeCodeSnips(TextStream &s, // replace template variable for the Python Type object for the // class implementing the method in which the code snip is written if (func->isStatic()) { - code.replace(QLatin1String("%PYTHONTYPEOBJECT"), - cpythonTypeName(func->implementingClass()) + QLatin1String("->type")); + code.replace(u"%PYTHONTYPEOBJECT"_s, + u"(*"_s + cpythonTypeName(func->implementingClass()) + u')'); } else { - code.replace(QLatin1String("%PYTHONTYPEOBJECT."), pySelf + QLatin1String("->ob_type->")); - code.replace(QLatin1String("%PYTHONTYPEOBJECT"), pySelf + QLatin1String("->ob_type")); + code.replace(u"%PYTHONTYPEOBJECT."_s, pySelf + u"->ob_type->"_s); + code.replace(u"%PYTHONTYPEOBJECT"_s, pySelf + u"->ob_type"_s); } } @@ -1786,28 +1600,23 @@ void ShibokenGenerator::writeCodeSnips(TextStream &s, QStringList args; for (const ArgumentVarReplacementPair &pair : argReplacements) { - if (pair.second.startsWith(QLatin1String(CPP_ARG_REMOVED))) + if (pair.second.startsWith(CPP_ARG_REMOVED_PREFIX)) continue; args << pair.second; } - code.replace(QLatin1String("%ARGUMENT_NAMES"), args.join(QLatin1String(", "))); + code.replace(u"%ARGUMENT_NAMES"_s, args.join(u", "_s)); for (const ArgumentVarReplacementPair &pair : argReplacements) { const AbstractMetaArgument &arg = pair.first; int idx = arg.argumentIndex() + 1; - AbstractMetaType type = arg.type(); - QString typeReplaced = func->typeReplaced(arg.argumentIndex() + 1); - if (!typeReplaced.isEmpty()) { - auto builtType = buildAbstractMetaTypeFromString(typeReplaced); - if (builtType.has_value()) - type = builtType.value(); - } + AbstractMetaType type = arg.modifiedType(); if (type.isWrapperType()) { QString replacement = pair.second; - if (type.referenceType() == LValueReference && !type.isPointer()) - replacement.remove(0, 1); + const auto generatorArg = GeneratorArgument::fromMetaType(type); + if (generatorArg.indirections > 0) + AbstractMetaType::stripDereference(&replacement); if (type.referenceType() == LValueReference || type.isPointer()) - code.replace(QString::fromLatin1("%%1.").arg(idx), replacement + QLatin1String("->")); + code.replace(u'%' + QString::number(idx) + u'.', replacement + u"->"_s); } code.replace(CodeSnipAbstract::placeHolderRegex(idx), pair.second); } @@ -1816,11 +1625,11 @@ void ShibokenGenerator::writeCodeSnips(TextStream &s, // Replaces template %PYTHON_ARGUMENTS variable with a pointer to the Python tuple // containing the converted virtual method arguments received from C++ to be passed // to the Python override. - code.replace(QLatin1String("%PYTHON_ARGUMENTS"), QLatin1String(PYTHON_ARGS)); + code.replace(u"%PYTHON_ARGUMENTS"_s, PYTHON_ARGS); // replace variable %PYTHON_METHOD_OVERRIDE for a pointer to the Python method // override for the C++ virtual method in which this piece of code was inserted - code.replace(QLatin1String("%PYTHON_METHOD_OVERRIDE"), QLatin1String(PYTHON_OVERRIDE_VAR)); + code.replace(u"%PYTHON_METHOD_OVERRIDE"_s, PYTHON_OVERRIDE_VAR); } if (avoidProtectedHack()) { @@ -1829,31 +1638,34 @@ void ShibokenGenerator::writeCodeSnips(TextStream &s, // name and if any of them is of the protected visibility. This is used to replace // calls to %FUNCTION_NAME on user written custom code for calls to the protected // dispatcher. - bool hasProtectedOverload = false; - if (func->isUserAdded()) { - const auto &funcs = getFunctionOverloads(func->ownerClass(), func->name()); - for (const auto &f : funcs) - hasProtectedOverload |= f->isProtected(); + bool isProtected = func->isProtected(); + auto owner = func->ownerClass(); + if (!isProtected && func->isUserAdded() && owner != nullptr) { + const auto &funcs = getFunctionGroups(owner).value(func->name()); + isProtected = std::any_of(funcs.cbegin(), funcs.cend(), + [](const AbstractMetaFunctionCPtr &f) { + return f->isProtected(); + }); } - if (func->isProtected() || hasProtectedOverload) { - code.replace(QLatin1String("%TYPE::%FUNCTION_NAME"), - QStringLiteral("%1::%2_protected") - .arg(wrapperName(func->ownerClass()), func->originalName())); - code.replace(QLatin1String("%FUNCTION_NAME"), - func->originalName() + QLatin1String("_protected")); + if (isProtected) { + code.replace(u"%TYPE::%FUNCTION_NAME"_s, + wrapperName(func->ownerClass()) + "::"_L1 + + func->originalName() + "_protected"_L1); + code.replace(u"%FUNCTION_NAME"_s, + func->originalName() + u"_protected"_s); } } if (func->isConstructor() && shouldGenerateCppWrapper(func->ownerClass())) - code.replace(QLatin1String("%TYPE"), wrapperName(func->ownerClass())); + code.replace(u"%TYPE"_s, wrapperName(func->ownerClass())); if (func->ownerClass()) - code.replace(QLatin1String("%CPPTYPE"), func->ownerClass()->name()); + code.replace(u"%CPPTYPE"_s, func->ownerClass()->name()); replaceTemplateVariables(code, func); - processCodeSnip(code); + processCodeSnip(code, func->classQualifiedSignature()); s << "// Begin code injection\n" << code << "// End of code injection\n\n"; } @@ -1861,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(); } @@ -1874,15 +1686,15 @@ static QString miniNormalizer(const QString &varType) QString normalized = varType.trimmed(); if (normalized.isEmpty()) return normalized; - if (normalized.startsWith(QLatin1String("::"))) + if (normalized.startsWith(u"::")) normalized.remove(0, 2); QString suffix; - while (normalized.endsWith(QLatin1Char('*')) || normalized.endsWith(QLatin1Char('&'))) { - suffix.prepend(normalized.at(normalized.count() - 1)); + while (normalized.endsWith(u'*') || normalized.endsWith(u'&')) { + suffix.prepend(normalized.at(normalized.size() - 1)); normalized.chop(1); normalized = normalized.trimmed(); } - const QString result = normalized + QLatin1Char(' ') + suffix; + const QString result = normalized + u' ' + suffix; return result.trimmed(); } // The position must indicate the first character after the opening '('. @@ -1893,7 +1705,7 @@ static QString getConverterTypeSystemVariableArgument(const QString &code, int p QString arg; int parenthesisDepth = 0; int count = 0; - while (pos + count < code.count()) { + while (pos + count < code.size()) { char c = code.at(pos+count).toLatin1(); // toAscii is gone if (c == '(') { ++parenthesisDepth; @@ -1914,15 +1726,15 @@ static QString getConverterTypeSystemVariableArgument(const QString &code, int p const QHash<int, QString> &ShibokenGenerator::typeSystemConvName() { static const QHash<int, QString> result = { - {TypeSystemCheckFunction, QLatin1String("checkType")}, - {TypeSystemIsConvertibleFunction, QLatin1String("isConvertible")}, - {TypeSystemToCppFunction, QLatin1String("toCpp")}, - {TypeSystemToPythonFunction, QLatin1String("toPython")} + {TypeSystemCheckFunction, u"checkType"_s}, + {TypeSystemIsConvertibleFunction, u"isConvertible"_s}, + {TypeSystemToCppFunction, u"toCpp"_s}, + {TypeSystemToPythonFunction, u"toPython"_s} }; return result; } -using StringPair = QPair<QString, QString>; +using StringPair = std::pair<QString, QString>; void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVariable converterVariable, QString &code) const @@ -1935,7 +1747,7 @@ void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVa QString conversionString = list.constFirst(); const QString &conversionTypeName = list.constLast(); QString message; - const auto conversionTypeO = buildAbstractMetaTypeFromString(conversionTypeName, &message); + const auto conversionTypeO = AbstractMetaType::fromString(conversionTypeName, &message); if (!conversionTypeO.has_value()) { throw Exception(msgCannotFindType(conversionTypeName, typeSystemConvName().value(converterVariable), @@ -1948,7 +1760,7 @@ void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVa StringStream c(TextStream::Language::Cpp); int end = match.capturedStart(); int start = end; - while (start > 0 && code.at(start) != QLatin1Char('\n')) + while (start > 0 && code.at(start) != u'\n') --start; while (code.at(start).isSpace()) ++start; @@ -1957,21 +1769,13 @@ void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVa varType = miniNormalizer(varType); QString varName = list.at(1).trimmed(); if (!varType.isEmpty()) { - const QString conversionSignature = conversionType.cppSignature(); - if (varType != QLatin1String("auto") && varType != conversionSignature) - throw Exception(msgConversionTypesDiffer(varType, conversionSignature)); - c << getFullTypeName(conversionType) << ' ' << varName; - writeMinimalConstructorExpression(c, api(), conversionType); - c << ";\n"; + c << getFullTypeName(conversionType) << ' ' << varName + << minimalConstructorExpression(api(), conversionType) << ";\n"; } c << cpythonToCppConversionFunction(conversionType); QString prefix; - if (varName.startsWith(QLatin1Char('*'))) { - varName.remove(0, 1); - varName = varName.trimmed(); - } else { - prefix = QLatin1Char('&'); - } + if (!AbstractMetaType::stripDereference(&varName)) + prefix = u'&'; QString arg = getConverterTypeSystemVariableArgument(code, match.capturedEnd()); conversionString += arg; c << arg << ", " << prefix << '(' << varName << ')'; @@ -1981,8 +1785,8 @@ void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVa case TypeSystemCheckFunction: conversion = cpythonCheckFunction(conversionType); if (conversionType.typeEntry()->isPrimitive() - && (conversionType.typeEntry()->name() == cPyObjectT() - || !conversion.endsWith(QLatin1Char(' ')))) { + && (conversionType.typeEntry()->name() == cPyObjectT + || !conversion.endsWith(u' '))) { conversion += u'('; break; } @@ -2004,17 +1808,17 @@ void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVa << code << '\''; throw Exception(m); } - if (conversion.contains(QLatin1String("%in"))) { - conversion.prepend(QLatin1Char('(')); - conversion.replace(QLatin1String("%in"), arg); + if (conversion.contains(u"%in")) { + conversion.prepend(u'('); + conversion.replace(u"%in"_s, arg); } else { conversion += arg; } } } - replacements.append(qMakePair(conversionString, conversion)); + replacements.append(std::make_pair(conversionString, conversion)); } - for (const StringPair &rep : qAsConst(replacements)) + for (const StringPair &rep : std::as_const(replacements)) code.replace(rep.first, rep.second); } @@ -2023,9 +1827,9 @@ bool ShibokenGenerator::injectedCodeCallsCppFunction(const GeneratorContext &con { if (func->injectedCodeContains(u"%FUNCTION_NAME(")) return true; - QString funcCall = func->originalName() + QLatin1Char('('); + QString funcCall = func->originalName() + u'('; if (func->isConstructor()) - funcCall.prepend(QLatin1String("new ")); + funcCall.prepend(u"new "_s); if (func->injectedCodeContains(funcCall)) return true; if (!func->isConstructor()) @@ -2035,18 +1839,17 @@ bool ShibokenGenerator::injectedCodeCallsCppFunction(const GeneratorContext &con const auto owner = func->ownerClass(); if (!owner->isPolymorphic()) return false; - const QString className = context.useWrapper() - ? context.wrapperName() : owner->qualifiedCppName(); - const QString wrappedCtorCall = QLatin1String("new ") + className + QLatin1Char('('); + const QString wrappedCtorCall = u"new "_s + context.effectiveClassName() + u'('; 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()) { @@ -2058,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; @@ -2070,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; @@ -2086,7 +1889,7 @@ bool ShibokenGenerator::classNeedsGetattroFunctionImpl(const AbstractMetaClass * const auto &functionGroup = getFunctionGroups(metaClass); for (auto it = functionGroup.cbegin(), end = functionGroup.cend(); it != end; ++it) { AbstractMetaFunctionCList overloads; - for (const auto &func : qAsConst(it.value())) { + for (const auto &func : std::as_const(it.value())) { if (func->isAssignmentOperator() || func->isConversionOperator() || func->isModifiedRemoved() || func->isPrivate() || func->ownerClass() != func->implementingClass() @@ -2103,14 +1906,14 @@ bool ShibokenGenerator::classNeedsGetattroFunctionImpl(const AbstractMetaClass * } AbstractMetaFunctionCList - ShibokenGenerator::getMethodsWithBothStaticAndNonStaticMethods(const AbstractMetaClass *metaClass) + ShibokenGenerator::getMethodsWithBothStaticAndNonStaticMethods(const AbstractMetaClassCPtr &metaClass) { AbstractMetaFunctionCList methods; if (metaClass) { const auto &functionGroups = getFunctionGroups(metaClass); for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) { AbstractMetaFunctionCList overloads; - for (const auto &func : qAsConst(it.value())) { + for (const auto &func : std::as_const(it.value())) { if (func->isAssignmentOperator() || func->isConversionOperator() || func->isModifiedRemoved() || func->isPrivate() || func->ownerClass() != func->implementingClass() @@ -2127,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; @@ -2138,72 +1942,48 @@ 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; } -std::optional<AbstractMetaType> - ShibokenGenerator::buildAbstractMetaTypeFromString(QString typeSignature, - QString *errorMessage) +IncludeGroupList ShibokenGenerator::classIncludes(const AbstractMetaClassCPtr &metaClass) const { - typeSignature = typeSignature.trimmed(); - if (typeSignature.startsWith(QLatin1String("::"))) - typeSignature.remove(0, 2); + IncludeGroupList result; + const auto typeEntry = metaClass->typeEntry(); + //Extra includes + result.append(IncludeGroup{u"Extra includes"_s, + typeEntry->extraIncludes()}); - auto &cache = *metaTypeFromStringCache(); - auto it = cache.find(typeSignature); - if (it == cache.end()) { - auto metaType = - AbstractMetaBuilder::translateType(typeSignature, nullptr, {}, errorMessage); - if (Q_UNLIKELY(!metaType.has_value())) { - if (errorMessage) - errorMessage->prepend(msgCannotBuildMetaType(typeSignature)); - return {}; + result.append({u"Enum includes"_s, {}}); + for (const auto &cppEnum : metaClass->enums()) + result.back().includes.append(cppEnum.typeEntry()->extraIncludes()); + + result.append({u"Argument includes"_s, typeEntry->argumentIncludes()}); + const auto implicitConvs = implicitConversions(typeEntry); + for (const auto &f : implicitConvs) { + if (f->isConversionOperator()) { + const auto source = f->ownerClass(); + Q_ASSERT(source); + result.back().append(source->typeEntry()->include()); } - it = cache.insert(typeSignature, metaType.value()); } - return it.value(); -} - -AbstractMetaType - ShibokenGenerator::buildAbstractMetaTypeFromTypeEntry(const TypeEntry *typeEntry) -{ - QString typeName = typeEntry->qualifiedCppName(); - if (typeName.startsWith(QLatin1String("::"))) - typeName.remove(0, 2); - auto &cache = *metaTypeFromStringCache(); - auto it = cache.find(typeName); - if (it != cache.end()) - return it.value(); - AbstractMetaType metaType(typeEntry); - metaType.clearIndirections(); - metaType.setReferenceType(NoReference); - metaType.setConstant(false); - metaType.decideUsagePattern(); - cache.insert(typeName, metaType); - return metaType; -} - -AbstractMetaType - ShibokenGenerator::buildAbstractMetaTypeFromAbstractMetaClass(const AbstractMetaClass *metaClass) -{ - return ShibokenGenerator::buildAbstractMetaTypeFromTypeEntry(metaClass->typeEntry()); + return result; } /* static void dumpFunction(AbstractMetaFunctionList lst) { qDebug() << "DUMP FUNCTIONS: "; - for (AbstractMetaFunction *func : qAsConst(lst)) + for (AbstractMetaFunction *func : std::as_const(lst)) qDebug() << "*" << func->ownerClass()->name() << func->signature() << "Private: " << func->isPrivate() @@ -2247,40 +2027,78 @@ 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; } -ShibokenGenerator::FunctionGroups ShibokenGenerator::getFunctionGroupsImpl(const AbstractMetaClass *scope) +QList<AbstractMetaFunctionCList> + ShibokenGenerator::numberProtocolOperators(const AbstractMetaClassCPtr &scope) +{ + Q_ASSERT(scope); + return getGeneratorClassInfo(scope).numberProtocolOperators; +} + +BoolCastFunctionOptional ShibokenGenerator::boolCast(const AbstractMetaClassCPtr &scope) +{ + Q_ASSERT(scope); + return getGeneratorClassInfo(scope).boolCastFunctionO; +} + +// Use non-const overloads only, for example, "foo()" and "foo()const" +// the second is removed. +static void removeConstOverloads(AbstractMetaFunctionCList *overloads) +{ + for (qsizetype i = overloads->size() - 1; i >= 0; --i) { + const auto &f = overloads->at(i); + if (f->isConstant()) { + for (qsizetype c = 0, size = overloads->size(); c < size; ++c) { + if (f->isConstOverloadOf(overloads->at(c).get())) { + overloads->removeAt(i); + break; + } + } + } + } +} + +ShibokenGenerator::FunctionGroups + ShibokenGenerator::getFunctionGroupsImpl(const AbstractMetaClassCPtr &scope) { AbstractMetaFunctionCList lst = scope->functions(); scope->getFunctionsFromInvisibleNamespacesToBeGenerated(&lst); FunctionGroups results; for (const auto &func : lst) { - if (isGroupable(func)) { + if (isGroupable(func) + && func->ownerClass() == func->implementingClass() + && func->generateBinding()) { auto it = results.find(func->name()); if (it == results.end()) { - results.insert(func->name(), AbstractMetaFunctionCList(1, func)); + it = results.insert(func->name(), AbstractMetaFunctionCList(1, func)); } else { // If there are virtuals methods in the mix (PYSIDE-570, // QFileSystemModel::index(QString,int) and @@ -2292,389 +2110,541 @@ ShibokenGenerator::FunctionGroups ShibokenGenerator::getFunctionGroupsImpl(const else it.value().append(func); } + getInheritedOverloads(scope, &it.value()); + removeConstOverloads(&it.value()); } } return results; } -AbstractMetaFunctionCList - ShibokenGenerator::getInheritedOverloads(const AbstractMetaFunctionCPtr &func, QSet<QString> *seen) -{ - AbstractMetaFunctionCList results; - AbstractMetaClass *basis; - if (func->ownerClass() && (basis = func->ownerClass()->baseClass())) { - for (; basis; basis = basis->baseClass()) { - const auto inFunc = basis->findFunction(func->name()); - if (!inFunc.isNull() && !seen->contains(inFunc->minimalSignature())) { - seen->insert(inFunc->minimalSignature()); - AbstractMetaFunction *newFunc = inFunc->copy(); - newFunc->setImplementingClass(func->implementingClass()); - results << AbstractMetaFunctionCPtr(newFunc); - } - } - } - return results; +static bool removeNumberProtocolOperator(const AbstractMetaFunctionCPtr &f) +{ + return !f->generateBinding() + || (f->ownerClass() != f->implementingClass() && !f->isAbstract()); } -AbstractMetaFunctionCList - ShibokenGenerator::getFunctionAndInheritedOverloads(const AbstractMetaFunctionCPtr &func, - QSet<QString> *seen) +QList<AbstractMetaFunctionCList> + ShibokenGenerator::getNumberProtocolOperators(const AbstractMetaClassCPtr &metaClass) { - AbstractMetaFunctionCList results; - seen->insert(func->minimalSignature()); - results << func << getInheritedOverloads(func, seen); - return results; + 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; } -AbstractMetaFunctionCList ShibokenGenerator::getFunctionOverloads(const AbstractMetaClass *scope, - const QString &functionName) const +BoolCastFunctionOptional +ShibokenGenerator::getBoolCast(const AbstractMetaClassCPtr &metaClass) { - const auto &lst = scope ? scope->functions() : api().globalFunctions(); + if (metaClass->isNamespace()) + return std::nullopt; - AbstractMetaFunctionCList results; - QSet<QString> seenSignatures; - for (const auto &func : qAsConst(lst)) { - if (func->name() != functionName) - continue; - if (isGroupable(func)) { - // PYSIDE-331: look also into base classes. - results << getFunctionAndInheritedOverloads(func, &seenSignatures); + 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}; } } - return results; + + 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; } -Generator::OptionDescriptions ShibokenGenerator::options() const +static bool isInplaceAdd(const AbstractMetaFunctionCPtr &func) { - return { - {QLatin1String(AVOID_PROTECTED_HACK), - QLatin1String("Avoid the use of the '#define protected public' hack.")}, - {QLatin1String(DISABLE_VERBOSE_ERROR_MESSAGES), - QLatin1String("Disable verbose error messages. Turn the python code hard to debug\n" - "but safe few kB on the generated bindings.")}, - {QLatin1String(PARENT_CTOR_HEURISTIC), - QLatin1String("Enable heuristics to detect parent relationship on constructors.")}, - {QLatin1String(ENABLE_PYSIDE_EXTENSIONS), - QLatin1String("Enable PySide extensions, such as support for signal/slots,\n" - "use this if you are creating a binding for a Qt-based library.")}, - {QLatin1String(RETURN_VALUE_HEURISTIC), - QLatin1String("Enable heuristics to detect parent relationship on return values\n" - "(USE WITH CAUTION!)")}, - {QLatin1String(USE_ISNULL_AS_NB_NONZERO), - QLatin1String("If a class have an isNull() const method, it will be used to compute\n" - "the value of boolean casts")}, - {QLatin1String(WRAPPER_DIAGNOSTICS), - QLatin1String("Generate diagnostic code around wrappers")} - }; + return func->name() == u"operator+="; } -bool ShibokenGenerator::handleOption(const QString &key, const QString & /* value */) -{ - if (key == QLatin1String(PARENT_CTOR_HEURISTIC)) - return (m_useCtorHeuristic = true); - if (key == QLatin1String(ENABLE_PYSIDE_EXTENSIONS)) - return (m_usePySideExtensions = true); - if (key == QLatin1String(RETURN_VALUE_HEURISTIC)) - return (m_userReturnValueHeuristic = true); - if (key == QLatin1String(DISABLE_VERBOSE_ERROR_MESSAGES)) - return (m_verboseErrorMessagesDisabled = true); - if (key == QLatin1String(USE_ISNULL_AS_NB_NONZERO)) - return (m_useIsNullAsNbNonZero = true); - if (key == QLatin1String(AVOID_PROTECTED_HACK)) - return (m_avoidProtectedHack = true); - if (key == QLatin1String(WRAPPER_DIAGNOSTICS)) - return (m_wrapperDiagnostics = true); - return false; +static bool isIncrementOperator(const AbstractMetaFunctionCPtr &func) +{ + return func->functionType() == AbstractMetaFunction::IncrementOperator; } -static void getCode(QStringList &code, const CodeSnipList &codeSnips) +static bool isDecrementOperator(const AbstractMetaFunctionCPtr &func) { - for (const CodeSnip &snip : qAsConst(codeSnips)) - code.append(snip.code()); + 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 void getCode(QStringList &code, const TypeEntry *type) +static bool hidesBaseClassFunctions(const AbstractMetaFunctionCPtr &f) { - getCode(code, type->codeSnips()); + auto attributes = f->cppAttributes(); + return !attributes.testFlag(FunctionAttribute::Override) + && !attributes.testFlag(FunctionAttribute::Final); +} - CustomConversion *customConversion = type->customConversion(); - if (!customConversion) +void ShibokenGenerator::getInheritedOverloads(const AbstractMetaClassCPtr &scope, + AbstractMetaFunctionCList *overloads) +{ + if (overloads->isEmpty() || scope->isNamespace() || scope->baseClasses().isEmpty()) return; - if (!customConversion->nativeToTargetConversion().isEmpty()) - code.append(customConversion->nativeToTargetConversion()); + // PYSIDE-331: look also into base classes. Check for any non-overriding + // function hiding the base class functions. + const bool hideBaseClassFunctions = + std::any_of(overloads->cbegin(), overloads->cend(), hidesBaseClassFunctions); - const CustomConversion::TargetToNativeConversions &toCppConversions = customConversion->targetToNativeConversions(); - if (toCppConversions.isEmpty()) - return; + const QString &functionName = overloads->constFirst()->name(); + const bool hasUsingDeclarations = scope->hasUsingMemberFor(functionName); + if (hideBaseClassFunctions && !hasUsingDeclarations) + return; // No base function is visible - for (CustomConversion::TargetToNativeConversion *toNative : qAsConst(toCppConversions)) - code.append(toNative->conversion()); + // Collect base candidates by name and signature + bool staticEncountered = false; + QSet<QString> seenSignatures; + for (const auto &func : *overloads) { + seenSignatures.insert(func->minimalSignature()); + staticEncountered |= func->isStatic(); + } + + AbstractMetaFunctionCList baseCandidates; + + auto basePredicate = [&functionName, &seenSignatures, &baseCandidates] + (const AbstractMetaClassCPtr &b) { + for (const auto &f : b->functions()) { + if (f->generateBinding() && f->name() == functionName) { + const QString signature = f->minimalSignature(); + if (!seenSignatures.contains(signature)) { + seenSignatures.insert(signature); + baseCandidates.append(f); + } + } + } + return false; // Keep going + }; + + for (const auto &baseClass : scope->baseClasses()) + recurseClassHierarchy(baseClass, basePredicate); + + // Remove the ones that are not made visible with using declarations + if (hideBaseClassFunctions && hasUsingDeclarations) { + const auto pred = [scope](const AbstractMetaFunctionCPtr &f) { + return !scope->isUsingMember(f->ownerClass(), f->name(), f->access()); + }; + auto end = std::remove_if(baseCandidates.begin(), baseCandidates.end(), pred); + baseCandidates.erase(end, baseCandidates.end()); + } + + // PYSIDE-886: If the method does not have any static overloads declared + // in the class in question, remove all inherited static methods as setting + // METH_STATIC in that case can cause crashes for the instance methods. + // Manifested as crash when calling QPlainTextEdit::find() (clash with + // static QWidget::find(WId)). + if (!staticEncountered) { + auto end = std::remove_if(baseCandidates.begin(), baseCandidates.end(), + [](const AbstractMetaFunctionCPtr &f) { return f->isStatic(); }); + baseCandidates.erase(end, baseCandidates.end()); + } + + for (const auto &baseCandidate : baseCandidates) { + AbstractMetaFunction *newFunc = baseCandidate->copy(); + newFunc->setImplementingClass(scope); + overloads->append(AbstractMetaFunctionCPtr(newFunc)); + } } -bool ShibokenGenerator::doSetup() +QList<OptionDescription> ShibokenGenerator::options() +{ + return { + {DISABLE_VERBOSE_ERROR_MESSAGES, + u"Disable verbose error messages. Turn the python code hard to debug\n" + "but safe few kB on the generated bindings."_s}, + {PARENT_CTOR_HEURISTIC, + u"Enable heuristics to detect parent relationship on constructors."_s}, + {RETURN_VALUE_HEURISTIC, + u"Enable heuristics to detect parent relationship on return values\n" + "(USE WITH CAUTION!)"_s}, + {USE_ISNULL_AS_NB_BOOL, + u"If a class have an isNull() const method, it will be used to compute\n" + "the value of boolean casts"_s}, + {LEAN_HEADERS, + u"Forward declare classes in module headers"_s}, + {USE_OPERATOR_BOOL_AS_NB_BOOL, + u"If a class has an operator bool, it will be used to compute\n" + "the value of boolean casts"_s}, + {NO_IMPLICIT_CONVERSIONS, + u"Do not generate implicit_conversions for function arguments."_s}, + {WRAPPER_DIAGNOSTICS, + u"Generate diagnostic code around wrappers"_s} + }; +} + +class ShibokenGeneratorOptionsParser : public OptionsParser { - QStringList snips; - const PrimitiveTypeEntryList &primitiveTypeList = primitiveTypes(); - for (const PrimitiveTypeEntry *type : primitiveTypeList) - getCode(snips, type); - const ContainerTypeEntryList &containerTypeList = containerTypes(); - for (const ContainerTypeEntry *type : containerTypeList) - getCode(snips, type); - for (auto metaClass : api().classes()) - getCode(snips, metaClass->typeEntry()); +public: + explicit ShibokenGeneratorOptionsParser(ShibokenGeneratorOptions *o) : m_options(o) {} - const TypeSystemTypeEntry *moduleEntry = TypeDatabase::instance()->defaultTypeSystemType(); - Q_ASSERT(moduleEntry); - getCode(snips, moduleEntry); + bool handleBoolOption(const QString & key, OptionSource source) override; - const auto &functionGroups = getGlobalFunctionGroups(); - for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) { - for (const auto &func : it.value()) - getCode(snips, func->injectedCodeSnips()); - } +private: + ShibokenGeneratorOptions *m_options; +}; - for (const QString &code : qAsConst(snips)) { - collectContainerTypesFromConverterMacros(code, true); - collectContainerTypesFromConverterMacros(code, false); +bool ShibokenGeneratorOptionsParser::handleBoolOption(const QString &key, OptionSource source) +{ + if (source == OptionSource::CommandLineSingleDash) + return false; + if (key == PARENT_CTOR_HEURISTIC) + return (m_options->useCtorHeuristic = true); + if (key == RETURN_VALUE_HEURISTIC) + return (m_options->userReturnValueHeuristic = true); + if (key == DISABLE_VERBOSE_ERROR_MESSAGES) + return (m_options->verboseErrorMessagesDisabled = true); + if (key == USE_ISNULL_AS_NB_BOOL || key == USE_ISNULL_AS_NB_NONZERO) { + return (m_options->useIsNullAsNbBool = true); + } + if (key == LEAN_HEADERS) + return (m_options->leanHeaders= true); + if (key == USE_OPERATOR_BOOL_AS_NB_BOOL || key == USE_OPERATOR_BOOL_AS_NB_NONZERO) { + return (m_options->useOperatorBoolAsNbBool = true); + } + if (key == NO_IMPLICIT_CONVERSIONS) { + m_options->generateImplicitConversions = false; + return true; } + if (key == WRAPPER_DIAGNOSTICS) + return (m_options->wrapperDiagnostics = true); + return false; +} + +std::shared_ptr<OptionsParser> ShibokenGenerator::createOptionsParser() +{ + return std::make_shared<ShibokenGeneratorOptionsParser>(&m_options); +} +bool ShibokenGenerator::doSetup() +{ return true; } -void ShibokenGenerator::collectContainerTypesFromConverterMacros(const QString &code, bool toPythonMacro) +bool ShibokenGenerator::useCtorHeuristic() { - QString convMacro = toPythonMacro ? QLatin1String("%CONVERTTOPYTHON[") : QLatin1String("%CONVERTTOCPP["); - int offset = toPythonMacro ? sizeof("%CONVERTTOPYTHON") : sizeof("%CONVERTTOCPP"); - int start = 0; - QString errorMessage; - while ((start = code.indexOf(convMacro, start)) != -1) { - int end = code.indexOf(QLatin1Char(']'), start); - start += offset; - if (code.at(start) != QLatin1Char('%')) { - QString typeString = code.mid(start, end - start); - auto type = buildAbstractMetaTypeFromString(typeString, &errorMessage); - if (type.has_value()) { - addInstantiatedContainersAndSmartPointers(type.value(), type->originalTypeDescription()); - } else { - QString m; - QTextStream(&m) << __FUNCTION__ << ": Cannot translate type \"" - << typeString << "\": " << errorMessage; - throw Exception(m); - } - } - start = end; - } + return m_options.useCtorHeuristic; } -bool ShibokenGenerator::useCtorHeuristic() const +bool ShibokenGenerator::useReturnValueHeuristic() { - return m_useCtorHeuristic; + return m_options.userReturnValueHeuristic; } -bool ShibokenGenerator::useReturnValueHeuristic() const +bool ShibokenGenerator::useIsNullAsNbBool() { - return m_userReturnValueHeuristic; + return m_options.useIsNullAsNbBool; } -bool ShibokenGenerator::usePySideExtensions() const +bool ShibokenGenerator::leanHeaders() { - return m_usePySideExtensions; + return m_options.leanHeaders; } -bool ShibokenGenerator::useIsNullAsNbNonZero() const +bool ShibokenGenerator::useOperatorBoolAsNbBool() { - return m_useIsNullAsNbNonZero; + return m_options.useOperatorBoolAsNbBool; } -bool ShibokenGenerator::avoidProtectedHack() const +bool ShibokenGenerator::generateImplicitConversions() { - return m_avoidProtectedHack; + return m_options.generateImplicitConversions; } QString ShibokenGenerator::moduleCppPrefix(const QString &moduleName) { QString result = moduleName.isEmpty() ? packageName() : moduleName; - result.replace(QLatin1Char('.'), QLatin1Char('_')); + result.replace(u'.', u'_'); return result; } +QString ShibokenGenerator::cppApiVariableNameOld(const QString &moduleName) +{ + return "Sbk"_L1 + moduleCppPrefix(moduleName) + "Types"_L1; +} + QString ShibokenGenerator::cppApiVariableName(const QString &moduleName) { - return QLatin1String("Sbk") + moduleCppPrefix(moduleName) - + QLatin1String("Types"); + return "Sbk"_L1 + moduleCppPrefix(moduleName) + "TypeStructs"_L1; } QString ShibokenGenerator::pythonModuleObjectName(const QString &moduleName) { - return QLatin1String("Sbk") + moduleCppPrefix(moduleName) - + QLatin1String("ModuleObject"); + 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(QLatin1String("Converters")); + result.append(u"Converters"_s); return result; } static QString processInstantiationsVariableName(const AbstractMetaType &type) { - QString res = QLatin1Char('_') + _fixedCppTypeName(type.typeEntry()->qualifiedCppName()).toUpper(); + QString res = u'_' + _fixedCppTypeName(type.typeEntry()->qualifiedCppName()); for (const auto &instantiation : type.instantiations()) { res += instantiation.isContainer() ? processInstantiationsVariableName(instantiation) - : QLatin1Char('_') + _fixedCppTypeName(instantiation.cppSignature()).toUpper(); + : u'_' + _fixedCppTypeName(instantiation.cppSignature()); } return res; } static void appendIndexSuffix(QString *s) { - if (!s->endsWith(QLatin1Char('_'))) - s->append(QLatin1Char('_')); - s->append(QStringLiteral("IDX")); + if (!s->endsWith(u'_')) + s->append(u'_'); + 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 = QLatin1String("SBK_") - + _fixedCppTypeName(templateBaseClass->typeEntry()->qualifiedCppName()).toUpper(); + QString result = u"SBK_"_s + + _fixedCppTypeName(templateBaseClass->typeEntry()->qualifiedCppName()); for (const auto &instantiation : metaClass->templateBaseClassInstantiations()) result += processInstantiationsVariableName(instantiation); appendIndexSuffix(&result); return result; } -QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaClass *metaClass) +QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaClassCPtr &metaClass) { return getTypeIndexVariableName(metaClass->typeEntry()); } -QString ShibokenGenerator::getTypeIndexVariableName(const TypeEntry *type) +QString ShibokenGenerator::getTypeIndexVariableName(TypeEntryCPtr type) { - if (type->isCppPrimitive()) { - const auto *trueType = static_cast<const PrimitiveTypeEntry *>(type); - if (trueType->basicReferencedTypeEntry()) - type = trueType->basicReferencedTypeEntry(); - } - QString result = QLatin1String("SBK_"); + if (isCppPrimitive(type)) + type = basicReferencedTypeEntry(type); + QString result = u"SBK_"_s; // Disambiguate namespaces per module to allow for extending them. if (type->isNamespace()) { QString package = type->targetLangPackage(); - const int dot = package.lastIndexOf(QLatin1Char('.')); + 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; } QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaType &type) { - QString result = QLatin1String("SBK"); + QString result = u"SBK"_s; if (type.typeEntry()->isContainer()) - result += QLatin1Char('_') + moduleName().toUpper(); + result += u'_' + moduleName(); result += processInstantiationsVariableName(type); appendIndexSuffix(&result); return result; } -bool ShibokenGenerator::verboseErrorMessagesDisabled() const +void collectfromTypeEntry(TypeEntryCPtr entry, QStringList &typeNames) { - return m_verboseErrorMessagesDisabled; + 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(); + } + } + } } -bool ShibokenGenerator::pythonFunctionWrapperUsesListOfArguments(const OverloadData &overloadData) +void ShibokenGenerator::collectFullTypeNamesArray(QStringList &typeNames) { - if (overloadData.referenceFunction()->isCallOperator()) - return true; - if (overloadData.referenceFunction()->isOperatorOverload()) - return false; - int maxArgs = overloadData.maxArgs(); - int minArgs = overloadData.minArgs(); - return (minArgs != maxArgs) - || (maxArgs > 1) - || overloadData.referenceFunction()->isConstructor() - || overloadData.hasArgumentWithDefaultValue(); + 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); } -void ShibokenGenerator::writeMinimalConstructorExpression(TextStream &s, - const ApiExtractorResult &api, - const AbstractMetaType &type, - const QString &defaultCtor) +bool ShibokenGenerator::verboseErrorMessagesDisabled() +{ + return m_options.verboseErrorMessagesDisabled; +} + +bool ShibokenGenerator::pythonFunctionWrapperUsesListOfArguments(const AbstractMetaFunctionCPtr &func) const +{ + const auto &groups = func->implementingClass() + ? getFunctionGroups(func->implementingClass()) + : getGlobalFunctionGroups(); + OverloadData od(groups.value(func->name()), api()); + return od.pythonFunctionWrapperUsesListOfArguments(); +} + +QString ShibokenGenerator::minimalConstructorExpression(const ApiExtractorResult &api, + const AbstractMetaType &type) { - if (!defaultCtor.isEmpty()) { - s << " = " << defaultCtor; - return; - } if (type.isExtendedCppPrimitive() || type.isSmartPointer()) - return; + return {}; QString errorMessage; const auto ctor = minimalConstructor(api, type, &errorMessage); - if (ctor.has_value()) { - s << ctor->initialization(); - } else { - const QString message = - msgCouldNotFindMinimalConstructor(QLatin1String(__FUNCTION__), - type.cppSignature(), errorMessage); - qCWarning(lcShiboken()).noquote() << message; - s << ";\n#error " << message << '\n'; - } + if (ctor.has_value()) + return ctor->initialization(); + + const QString message = + msgCouldNotFindMinimalConstructor(QLatin1StringView(__FUNCTION__), + type.cppSignature(), errorMessage); + qCWarning(lcShiboken()).noquote() << message; + return u";\n#error "_s + message + u'\n'; } -void ShibokenGenerator::writeMinimalConstructorExpression(TextStream &s, - const ApiExtractorResult &api, - const TypeEntry *type, - const QString &defaultCtor) +QString ShibokenGenerator::minimalConstructorExpression(const ApiExtractorResult &api, + const TypeEntryCPtr &type) { - if (!defaultCtor.isEmpty()) { - s << " = " << defaultCtor; - return; - } - if (type->isExtendedCppPrimitive()) - return; + if (isExtendedCppPrimitive(type)) + return {}; const auto ctor = minimalConstructor(api, type); - if (ctor.has_value()) { - s << ctor->initialization(); - } else { - const QString message = msgCouldNotFindMinimalConstructor(QLatin1String(__FUNCTION__), type->qualifiedCppName()); - qCWarning(lcShiboken()).noquote() << message; - s << ";\n#error " << message << '\n'; - } + if (ctor.has_value()) + return ctor->initialization(); + + const QString message = + msgCouldNotFindMinimalConstructor(QLatin1StringView(__FUNCTION__), + type->qualifiedCppName()); + qCWarning(lcShiboken()).noquote() << message; + return u";\n#error "_s + message + u'\n'; } QString ShibokenGenerator::pythonArgsAt(int i) { - return QLatin1String(PYTHON_ARGS) + QLatin1Char('[') - + QString::number(i) + QLatin1Char(']'); + return PYTHON_ARGS + u'[' + QString::number(i) + u']'; } 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(QLatin1String("%TYPE"), cpp_class->name()); + code.replace(u"%TYPE"_s, cpp_class->name()); const AbstractMetaArgumentList &argument = func->arguments(); for (const AbstractMetaArgument &arg : argument) - code.replace(QLatin1Char('%') + QString::number(arg.argumentIndex() + 1), arg.name()); + code.replace(u'%' + QString::number(arg.argumentIndex() + 1), arg.name()); //template values - code.replace(QLatin1String("%RETURN_TYPE"), translateType(func->type(), cpp_class)); - code.replace(QLatin1String("%FUNCTION_NAME"), func->originalName()); + code.replace(u"%RETURN_TYPE"_s, translateType(func->type(), cpp_class)); + code.replace(u"%FUNCTION_NAME"_s, func->originalName()); - if (code.contains(QLatin1String("%ARGUMENT_NAMES"))) { + if (code.contains(u"%ARGUMENT_NAMES")) { StringStream aux_stream; writeArgumentNames(aux_stream, func, Generator::SkipRemovedArguments); - code.replace(QLatin1String("%ARGUMENT_NAMES"), aux_stream); + code.replace(u"%ARGUMENT_NAMES"_s, aux_stream); } - if (code.contains(QLatin1String("%ARGUMENTS"))) { + if (code.contains(u"%ARGUMENTS")) { StringStream aux_stream; writeFunctionArguments(aux_stream, func, Options(SkipDefaultValues) | SkipRemovedArguments); - code.replace(QLatin1String("%ARGUMENTS"), aux_stream); + code.replace(u"%ARGUMENTS"_s, aux_stream); } } + +QString ShibokenGenerator::stdMove(const QString &c) +{ + return u"std::move("_s + c + u')'; +} diff --git a/sources/shiboken6/generator/shiboken/shibokengenerator.h b/sources/shiboken6/generator/shiboken/shibokengenerator.h index ba904ea49..22ee73fa2 100644 --- a/sources/shiboken6/generator/shiboken/shibokengenerator.h +++ b/sources/shiboken6/generator/shiboken/shibokengenerator.h @@ -1,66 +1,42 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef SHIBOKENGENERATOR_H #define SHIBOKENGENERATOR_H -extern const char *CPP_ARG; -extern const char *CPP_ARG_REMOVED; -extern const char *CPP_RETURN_VAR; -extern const char *CPP_SELF_VAR; -extern const char *NULL_PTR; -extern const char *PYTHON_ARG; -extern const char *PYTHON_ARGS; -extern const char *PYTHON_OVERRIDE_VAR; -extern const char *PYTHON_RETURN_VAR; -extern const char *PYTHON_TO_CPP_VAR; -extern const char *SMART_POINTER_GETTER; - -extern const char *CONV_RULE_OUT_VAR_SUFFIX; -extern const char *BEGIN_ALLOW_THREADS; -extern const char *END_ALLOW_THREADS; - #include <generator.h> -#include "typesystem.h" +#include "customconversion_typedefs.h" +#include "abstractmetalang_enums.h" +#include "typesystem_typedefs.h" +#include "typesystem_enums.h" #include <QtCore/QRegularExpression> #include <array> +#include <optional> +class EnumTypeEntry; +class FlagsTypeEntry; class DocParser; class CodeSnip; class QPropertySpec; class OverloadData; -class TextStream; +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. @@ -68,6 +44,29 @@ 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). + enum class FunctionGenerationFlag + { + None = 0x0, + /// Virtual method overridable in Python + VirtualMethod = 0x1, + /// Special QObject virtuals + QMetaObjectMethod = 0x2, + /// Needs a protected wrapper for avoidProtectedHack() + /// public "foo_protected()" calling "foo()" + ProtectedWrapper = 0x4, // + /// Pass through constructor + WrapperConstructor = 0x8, + /// Generate a special copy constructor + /// "FooBar_Wrapper(const Foo&)" for constructing a wrapper from a value + WrapperSpecialCopyConstructor = 0x10 + }; + Q_DECLARE_FLAGS(FunctionGeneration, FunctionGenerationFlag); + enum class AttroCheckFlag { None = 0x0, @@ -90,13 +89,18 @@ public: const char *name() const override { return "Shiboken"; } - /// Returns true if the user enabled PySide extensions. - bool usePySideExtensions() const; + static QList<OptionDescription> options(); + static std::shared_ptr<OptionsParser> createOptionsParser(); + + static QString minimalConstructorExpression(const ApiExtractorResult &api, + const AbstractMetaType &type); + static QString minimalConstructorExpression(const ApiExtractorResult &api, + const TypeEntryCPtr &type); protected: bool doSetup() override; - GeneratorContext contextForClass(const 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. @@ -104,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. @@ -117,24 +126,26 @@ protected: /// Write user's custom code snippets at class or module level. void writeClassCodeSnips(TextStream &s, - const CodeSnipList &codeSnips, + const QList<CodeSnip> &codeSnips, TypeSystem::CodeSnipPosition position, TypeSystem::Language language, const GeneratorContext &context) const; void writeCodeSnips(TextStream &s, - const CodeSnipList &codeSnips, + const QList<CodeSnip> &codeSnips, TypeSystem::CodeSnipPosition position, TypeSystem::Language language) const; /// Write user's custom code snippets at function level. void writeCodeSnips(TextStream &s, - const CodeSnipList &codeSnips, + const QList<CodeSnip> &codeSnips, TypeSystem::CodeSnipPosition position, TypeSystem::Language language, const AbstractMetaFunctionCPtr &func, - const AbstractMetaArgument *lastArg = nullptr) const; + bool usePyArgs, + const AbstractMetaArgument *lastArg) const; /// Replaces variables for the user's custom code at global or class level. void processCodeSnip(QString &code) const; + void processCodeSnip(QString &code, const QString &context) const; void processClassCodeSnip(QString &code, const GeneratorContext &context) const; /** @@ -160,211 +171,166 @@ 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 ApiExtractorResult &api, - const AbstractMetaFunctionCPtr &func, + 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 + static FunctionGeneration functionGeneration(const AbstractMetaFunctionCPtr &func); + + // Return a list of implicit conversions if generation is enabled. + AbstractMetaFunctionCList implicitConversions(const TypeEntryCPtr &t) const; - /// Condition to call WriteVirtualMethodNative. Was extracted because also used to count these calls. - bool shouldWriteVirtualMethodNative(const AbstractMetaFunctionCPtr &func) const; + static QString wrapperName(const AbstractMetaClassCPtr &metaClass); - QString wrapperName(const AbstractMetaClass *metaClass) const; + 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 pythonPrimitiveTypeName(const PrimitiveTypeEntry *type); - static QString pythonOperatorFunctionName(const QString &cppOpFuncName); static QString pythonOperatorFunctionName(const AbstractMetaFunctionCPtr &func); - static QString pythonRichCompareOperatorId(const QString &cppOpFuncName); - static QString pythonRichCompareOperatorId(const AbstractMetaFunctionCPtr &func); + static QList<AbstractMetaFunctionCList> + filterGroupedOperatorFunctions(const AbstractMetaClassCPtr &metaClass, + OperatorQueryOptions query); - static QString fixedCppTypeName(const CustomConversion::TargetToNativeConversion *toNative); + static QString fixedCppTypeName(const TargetToNativeConversion &toNative); static QString fixedCppTypeName(const AbstractMetaType &type); - static QString fixedCppTypeName(const TypeEntry *type, QString typeName = QString()); + static QString fixedCppTypeName(const TypeEntryCPtr &type, QString typeName = {}); static bool isNumber(const QString &cpythonApiName); - static bool isNumber(const TypeEntry *type); + static bool isNumber(const TypeEntryCPtr &type); static bool isNumber(const AbstractMetaType &type); - static bool isPyInt(const TypeEntry *type); + static bool isPyInt(const TypeEntryCPtr &type); static bool isPyInt(const AbstractMetaType &type); - /// Returns whether the underlying type is a value type with copy constructor only - static bool isValueTypeWithCopyConstructorOnly(const ApiExtractorResult &api, - const TypeEntry *type); - static bool isValueTypeWithCopyConstructorOnly(const ApiExtractorResult &api, - const AbstractMetaType &type); - /// Returns whether the type (function argument) is a value type with - /// copy constructor only is passed as value or const-ref and thus - /// no default value can be constructed. - static bool valueTypeWithCopyConstructorOnlyPassed(const ApiExtractorResult &api, - const AbstractMetaType &type); - static bool isNullPtr(const QString &value); static QString converterObject(const AbstractMetaType &type) ; - static QString converterObject(const TypeEntry *type); + static QString converterObject(const TypeEntryCPtr &type); - static QString cpythonBaseName(const AbstractMetaClass *metaClass); - static QString cpythonBaseName(const TypeEntry *type); + static QString cpythonBaseName(const AbstractMetaClassCPtr &metaClass); + static QString cpythonBaseName(const TypeEntryCPtr &type); + static QString containerCpythonBaseName(const ContainerTypeEntryCPtr &ctype); static QString cpythonBaseName(const AbstractMetaType &type); - static QString cpythonTypeName(const AbstractMetaClass *metaClass); - static QString cpythonTypeName(const TypeEntry *type); - static QString cpythonTypeNameExt(const TypeEntry *type); - static QString cpythonTypeNameExt(const AbstractMetaType &type) ; - QString cpythonCheckFunction(const TypeEntry *type, bool genericNumberType = false) const; - QString cpythonCheckFunction(AbstractMetaType metaType, bool genericNumberType = false) const; - /** - * Receives the argument \p type and tries to find the appropriate AbstractMetaType for it - * or a custom type check. - * \param type A string representing the type to be discovered. - * \param metaType A pointer to an AbstractMetaType pointer, to where write a new meta type object - * if one is produced from the \p type string. This object must be deallocated by - * the caller. It will set the target variable to nullptr, is \p type is a Python type. - * \return A custom check if \p type is a custom type, or an empty string if \p metaType - * receives an existing type object. - */ - struct CPythonCheckFunctionResult - { - QString checkFunction; - std::optional<AbstractMetaType> type; - }; - static CPythonCheckFunctionResult guessCPythonCheckFunction(const QString &type); - static QString cpythonIsConvertibleFunction(const ApiExtractorResult &api, - const TypeEntry *type, - bool genericNumberType = false, - bool checkExact = false); - QString cpythonIsConvertibleFunction(AbstractMetaType metaType, - bool genericNumberType = false) const; - QString cpythonIsConvertibleFunction(const AbstractMetaArgument &metaArg, - bool genericNumberType = false) const; - - 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 cpythonToPythonConversionFunction(const TypeEntry *type); + static QString cpythonTypeName(const AbstractMetaClassCPtr &metaClass); + static QString cpythonTypeName(const TypeEntryCPtr &type); + static QString cpythonTypeNameExtSet(const TypeEntryCPtr &type); + static QString cpythonTypeNameExtSet(const AbstractMetaType &type); + static QString cpythonTypeNameExt(const TypeEntryCPtr &type); + static QString cpythonTypeNameExt(const AbstractMetaType &type); + static QString cpythonCheckFunction(TypeEntryCPtr type); + static QString cpythonCheckFunction(AbstractMetaType metaType); + static QString cpythonIsConvertibleFunction(const TypeEntryCPtr &type); + static QString cpythonIsConvertibleFunction(const AbstractMetaType &metaType); + static QString cpythonIsConvertibleFunction(const AbstractMetaArgument &metaArg); + + static QString cpythonToCppConversionFunction(const AbstractMetaClassCPtr &metaClass) ; + static QString cpythonToCppConversionFunction(const AbstractMetaType &type); + static QString cpythonToPythonConversionFunction(const AbstractMetaType &type); + static QString cpythonToPythonConversionFunction(const AbstractMetaClassCPtr &metaClass); + static QString cpythonToPythonConversionFunction(const TypeEntryCPtr &type); static QString cpythonFunctionName(const AbstractMetaFunctionCPtr &func) ; static QString cpythonMethodDefinitionName(const AbstractMetaFunctionCPtr &func); - static QString cpythonGettersSettersDefinitionName(const 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 = QLatin1String("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 TypeEntry *type, const QString &argName); - - /// Guesses the scope to where belongs an argument's default value. - QString guessScopeForDefaultValue(const AbstractMetaFunctionCPtr &func, - const AbstractMetaArgument &arg) const; - QString guessScopeForDefaultFlagsValue(const AbstractMetaFunctionCPtr &func, - const AbstractMetaArgument &arg, - const QString &value) const; + static QString cpythonWrapperCPtr(const TypeEntryCPtr &type, const QString &argName); - static QString cpythonEnumName(const EnumTypeEntry *enumEntry); + static QString cpythonEnumName(const EnumTypeEntryCPtr &enumEntry); static QString cpythonEnumName(const AbstractMetaEnum &metaEnum); - static QString cpythonFlagsName(const FlagsTypeEntry *flagsEntry); + 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); - static QString getFormatUnitString(const AbstractMetaFunctionCPtr &func, bool incRef = false); - - /// 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()); - OptionDescriptions options() const override; - bool handleOption(const QString &key, const QString &value) override; + /// Includes for header (native wrapper class) or binding source + QList<IncludeGroup> classIncludes(const AbstractMetaClassCPtr &metaClass) const; /// Returns true if the user enabled the so called "parent constructor heuristic". - 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; - /// Returns true if the generated code should use the "#define protected public" hack. - bool avoidProtectedHack() const; + static bool useIsNullAsNbBool(); + /// Whether to generate lean module headers + static bool leanHeaders(); + /// Returns true if the generator should use operator bool to compute boolean casts. + static bool useOperatorBoolAsNbBool(); + /// Generate implicit conversions of function arguments + static bool generateImplicitConversions(); + static QString cppApiVariableNameOld(const QString &moduleName = {}); static QString cppApiVariableName(const QString &moduleName = QString()); static QString pythonModuleObjectName(const QString &moduleName = QString()); static QString convertersVariableName(const QString &moduleName = QString()); /// Returns the type index variable name for a given class. - static QString getTypeIndexVariableName(const 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 getTypeIndexVariableName(const TypeEntry *type); + static QString getTypeAlternateTemplateIndexVariableName(const AbstractMetaClassCPtr &metaClass); + static QString getTypeIndexVariableName(TypeEntryCPtr type); static QString getTypeIndexVariableName(const AbstractMetaType &type) ; - /// Returns true if the user don't want verbose error messages on the generated bindings. - bool verboseErrorMessagesDisabled() const; + /// Collect all type names as an array for initializing the type/name struct. + void collectFullTypeNamesArray(QStringList &typeNames); - /** - * Builds an AbstractMetaType object from a QString. - * Returns nullptr if no type could be built from the string. - * \param typeSignature The string describing the type to be built. - * \return A new AbstractMetaType object that must be deleted by the caller, - * or a nullptr pointer in case of failure. - */ - static std::optional<AbstractMetaType> - buildAbstractMetaTypeFromString(QString typeSignature, - QString *errorMessage = nullptr); - - /// Creates an AbstractMetaType object from a TypeEntry. - static AbstractMetaType - buildAbstractMetaTypeFromTypeEntry(const TypeEntry *typeEntry); - /// Creates an AbstractMetaType object from an AbstractMetaClass. - static AbstractMetaType - buildAbstractMetaTypeFromAbstractMetaClass(const AbstractMetaClass *metaClass); - - static void writeMinimalConstructorExpression(TextStream &s, const ApiExtractorResult &api, - const AbstractMetaType &type, - const QString &defaultCtor = QString()); - static void writeMinimalConstructorExpression(TextStream &s, const ApiExtractorResult &api, - const TypeEntry *type, - const QString &defaultCtor = QString()); + /// Returns true if the user don't want verbose error messages on the generated bindings. + static bool verboseErrorMessagesDisabled(); void collectContainerTypesFromConverterMacros(const QString &code, bool toPythonMacro); @@ -372,42 +338,45 @@ protected: const AbstractMetaFunctionCPtr &metaFunc, Options options = NoOption); - static void writeUnusedVariableCast(TextStream &s, const QString &variableName); - - AbstractMetaFunctionCList filterFunctions(const AbstractMetaClass *metaClass) const; - // All data about extended converters: the type entries of the target type, and a // list of AbstractMetaClasses accepted as argument for the conversion. - using ExtendedConverterData = QHash<const TypeEntry *, AbstractMetaClassCList>; + using ExtendedConverterData = QHash<TypeEntryCPtr, AbstractMetaClassCList>; /// Returns all extended conversions for the current module. ExtendedConverterData getExtendedConverters() const; /// Returns a list of converters for the non wrapper types of the current module. - static QList<const CustomConversion *> getPrimitiveCustomConversions() ; + static QList<CustomConversionPtr> getPrimitiveCustomConversions(); /// Returns true if the Python wrapper for the received OverloadData must accept a list of arguments. - static bool pythonFunctionWrapperUsesListOfArguments(const OverloadData &overloadData); + bool pythonFunctionWrapperUsesListOfArguments(const AbstractMetaFunctionCPtr &func) const; static const QRegularExpression &convertToCppRegEx() { return typeSystemConvRegExps()[TypeSystemToCppFunction]; } static QString pythonArgsAt(int i); - static const QHash<QString, QString> &formatUnits(); + /// Return the format character for C++->Python->C++ conversion (Py_BuildValue) + static const QHash<QString, QChar> &formatUnits(); + + static QString stdMove(const QString &c); private: static QString getModuleHeaderFileBaseName(const QString &moduleName = QString()); static QString cpythonGetterFunctionName(const QString &name, - const 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; /** @@ -416,16 +385,10 @@ private: * \param func the metafunction to be searched in subclasses. * \param seen the function's minimal signatures already seen. */ - static AbstractMetaFunctionCList getInheritedOverloads(const AbstractMetaFunctionCPtr & func, - QSet<QString> *seen); + static void getInheritedOverloads(const AbstractMetaClassCPtr &scope, + AbstractMetaFunctionCList *overloads); + - /** - * Returns all overloads for a function named \p functionName. - * \param scope scope used to search for overloads. - * \param functionName the function name. - */ - AbstractMetaFunctionCList getFunctionOverloads(const AbstractMetaClass *scope, - const QString &functionName) const; /** * Write a function argument in the C++ in the text stream \p s. * This function just call \code s << argumentString(); \endcode @@ -451,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, @@ -459,7 +422,7 @@ private: const AbstractMetaArgument *lastArg); /// Returns a string with the user's custom code snippets that comply with \p position and \p language. - static QString getCodeSnippets(const CodeSnipList &codeSnips, + static QString getCodeSnippets(const QList<CodeSnip> &codeSnips, TypeSystem::CodeSnipPosition position, TypeSystem::Language language); @@ -513,13 +476,7 @@ private: void replaceTemplateVariables(QString &code, const AbstractMetaFunctionCPtr &func) const; - bool m_useCtorHeuristic = false; - bool m_userReturnValueHeuristic = false; - bool m_usePySideExtensions = false; - bool m_verboseErrorMessagesDisabled = false; - bool m_useIsNullAsNbNonZero = false; - bool m_avoidProtectedHack = false; - bool m_wrapperDiagnostics = false; + static ShibokenGeneratorOptions m_options; /// Type system converter variable replacement names and regular expressions. static const QHash<int, QString> &typeSystemConvName(); @@ -528,6 +485,7 @@ private: static const TypeSystemConverterRegExps &typeSystemConvRegExps(); }; +Q_DECLARE_OPERATORS_FOR_FLAGS(ShibokenGenerator::FunctionGeneration); Q_DECLARE_OPERATORS_FOR_FLAGS(ShibokenGenerator::AttroCheck); #endif // SHIBOKENGENERATOR_H |