diff options
Diffstat (limited to 'sources/shiboken6/ApiExtractor/apiextractor.cpp')
-rw-r--r-- | sources/shiboken6/ApiExtractor/apiextractor.cpp | 745 |
1 files changed, 635 insertions, 110 deletions
diff --git a/sources/shiboken6/ApiExtractor/apiextractor.cpp b/sources/shiboken6/ApiExtractor/apiextractor.cpp index cee8bbdcd..83ee4437e 100644 --- a/sources/shiboken6/ApiExtractor/apiextractor.cpp +++ b/sources/shiboken6/ApiExtractor/apiextractor.cpp @@ -1,156 +1,314 @@ -/**************************************************************************** -** -** 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 "apiextractor.h" #include "apiextractorresult.h" +#include "abstractmetaargument.h" +#include "abstractmetabuilder.h" +#include "abstractmetaenum.h" +#include "abstractmetafield.h" +#include "abstractmetafunction.h" #include "abstractmetalang.h" +#include "codesnip.h" +#include "exception.h" +#include "messages.h" +#include "modifications.h" +#include "optionsparser.h" +#include "reporthandler.h" +#include "typedatabase.h" +#include "customconversion.h" +#include "containertypeentry.h" +#include "primitivetypeentry.h" +#include "smartpointertypeentry.h" +#include "typedefentry.h" +#include "namespacetypeentry.h" +#include "typesystemtypeentry.h" + +#include "qtcompat.h" + +#include <QtCore/QDir> +#include <QtCore/QDebug> +#include <QtCore/QTemporaryFile> -#include <QDir> -#include <QDebug> -#include <QTemporaryFile> #include <algorithm> #include <iostream> #include <iterator> -#include "reporthandler.h" -#include "typesystem.h" -#include "fileout.h" -#include "abstractmetabuilder.h" -#include "abstractmetaenum.h" -#include "typedatabase.h" -#include "typesystem.h" +using namespace Qt::StringLiterals; -#include <algorithm> -#include <iterator> +struct InstantiationCollectContext +{ + AbstractMetaTypeList instantiatedContainers; + InstantiatedSmartPointers instantiatedSmartPointers; + QStringList instantiatedContainersNames; +}; -ApiExtractor::ApiExtractor() +struct ApiExtractorOptions { - // Environment TYPESYSTEMPATH - QString envTypesystemPaths = QFile::decodeName(qgetenv("TYPESYSTEMPATH")); - if (!envTypesystemPaths.isEmpty()) - TypeDatabase::instance()->addTypesystemPath(envTypesystemPaths); + QString m_typeSystemFileName; + QFileInfoList m_cppFileNames; + HeaderPaths m_includePaths; + QStringList m_clangOptions; + QString m_logDirectory; + LanguageLevel m_languageLevel = LanguageLevel::Default; + bool m_skipDeprecated = false; +}; + +static inline QString languageLevelDescription() +{ + return u"C++ Language level (c++11..c++17, default="_s + + QLatin1StringView(clang::languageLevelOption(clang::emulatedCompilerLanguageLevel())) + + u')'; } -ApiExtractor::~ApiExtractor() +QList<OptionDescription> ApiExtractor::options() { - delete m_builder; + return { + {u"use-global-header"_s, + u"Use the global headers in generated code."_s}, + {u"clang-option"_s, + u"Option to be passed to clang"_s}, + {u"clang-options"_s, + u"A comma-separated list of options to be passed to clang"_s}, + {u"skip-deprecated"_s, + u"Skip deprecated functions"_s}, + {u"-F<path>"_s, {} }, + {u"framework-include-paths="_s + OptionsParser::pathSyntax(), + u"Framework include paths used by the C++ parser"_s}, + {u"-isystem<path>"_s, {} }, + {u"system-include-paths="_s + OptionsParser::pathSyntax(), + u"System include paths used by the C++ parser"_s}, + {u"language-level=, -std=<level>"_s, + languageLevelDescription()}, + }; } -void ApiExtractor::addTypesystemSearchPath (const QString& path) +class ApiExtractorOptionsParser : public OptionsParser { - TypeDatabase::instance()->addTypesystemPath(path); +public: + explicit ApiExtractorOptionsParser(ApiExtractorOptions *o) : m_options(o) {} + + bool handleBoolOption(const QString &key, OptionSource source) override; + bool handleOption(const QString &key, const QString &value, + OptionSource source) override; + +private: + void parseIncludePathOption(const QString &value, HeaderType headerType); + void parseIncludePathOption(const QStringList &values, HeaderType headerType); + void setLanguageLevel(const QString &value); + + ApiExtractorOptions *m_options; +}; + +void ApiExtractorOptionsParser::parseIncludePathOption(const QString &value, + HeaderType headerType) +{ + if (value.isEmpty()) + throw Exception(u"Empty value passed to include path option"_s); + const auto path = QFile::encodeName(QDir::cleanPath(value)); + m_options->m_includePaths.append(HeaderPath{path, headerType}); } -void ApiExtractor::addTypesystemSearchPath(const QStringList& paths) +void ApiExtractorOptionsParser::parseIncludePathOption(const QStringList &values, + HeaderType headerType) { - for (const QString &path : paths) - addTypesystemSearchPath(path); + for (const auto &value : values) + parseIncludePathOption(value, headerType); } -void ApiExtractor::setTypesystemKeywords(const QStringList &keywords) +void ApiExtractorOptionsParser::setLanguageLevel(const QString &value) { - TypeDatabase::instance()->setTypesystemKeywords(keywords); + const QByteArray languageLevelBA = value.toLatin1(); + const LanguageLevel level = clang::languageLevelFromOption(languageLevelBA.constData()); + if (level == LanguageLevel::Default) + throw Exception(msgInvalidLanguageLevel(value)); + m_options->m_languageLevel = level; +} + +bool ApiExtractorOptionsParser::handleBoolOption(const QString &key, OptionSource source) +{ + static const auto isystemOption = "isystem"_L1; + + switch (source) { + case OptionSource::CommandLine: + case OptionSource::ProjectFile: + if (key == u"use-global-header") { + AbstractMetaBuilder::setUseGlobalHeader(true); + return true; + } + if (key == u"skip-deprecated") { + m_options->m_skipDeprecated = true; + return true; + } + break; + + case OptionSource::CommandLineSingleDash: + if (key.startsWith(u'I')) { // Shorthand path arguments -I/usr/include... + parseIncludePathOption(key.sliced(1), HeaderType::Standard); + return true; + } + if (key.startsWith(u'F')) { + parseIncludePathOption(key.sliced(1), HeaderType::Framework); + return true; + } + if (key.startsWith(isystemOption)) { + parseIncludePathOption(key.sliced(isystemOption.size()), HeaderType::System); + return true; + } + break; + } + return false; } -void ApiExtractor::addIncludePath(const HeaderPath& path) +bool ApiExtractorOptionsParser::handleOption(const QString &key, const QString &value, + OptionSource source) { - m_includePaths << path; + if (source == OptionSource::CommandLineSingleDash) { + if (key == u"std") { + setLanguageLevel(value); + return true; + } + return false; + } + + if (key == u"clang-option") { + m_options->m_clangOptions.append(value); + return true; + } + if (key == u"clang-options") { + m_options->m_clangOptions.append(value.split(u',', Qt::SkipEmptyParts)); + return true; + } + if (key == u"include-paths") { + parseIncludePathOption(value.split(QDir::listSeparator(), Qt::SkipEmptyParts), + HeaderType::Standard); + return true; + } + if (key == u"framework-include-paths") { + parseIncludePathOption(value.split(QDir::listSeparator(), Qt::SkipEmptyParts), + HeaderType::Framework); + return true; + } + if (key == u"system-include-paths") { + parseIncludePathOption(value.split(QDir::listSeparator(), Qt::SkipEmptyParts), + HeaderType::System); + return true; + } + if (key == u"language-level") { + setLanguageLevel(value); + return true; + } + + if (source == OptionSource::ProjectFile) { + if (key == u"include-path") { + parseIncludePathOption(value, HeaderType::Standard); + return true; + } + if (key == u"framework-include-path") { + parseIncludePathOption(value, HeaderType::Framework); + return true; + } + if (key == u"system-include-path") { + parseIncludePathOption(value, HeaderType::System); + return true; + } + } + + return false; } -void ApiExtractor::addIncludePath(const HeaderPaths& paths) +std::shared_ptr<OptionsParser> ApiExtractor::createOptionsParser() { - m_includePaths << paths; + return std::make_shared<ApiExtractorOptionsParser>(d); } -void ApiExtractor::setLogDirectory(const QString& logDir) +struct ApiExtractorPrivate : public ApiExtractorOptions +{ + bool runHelper(ApiExtractorFlags flags); + + static QString getSimplifiedContainerTypeName(const AbstractMetaType &type); + void addInstantiatedContainersAndSmartPointers(InstantiationCollectContext &context, + const AbstractMetaType &type, + const QString &contextName); + void collectInstantiatedContainersAndSmartPointers(InstantiationCollectContext &context, + const AbstractMetaFunctionCPtr &func); + void collectInstantiatedContainersAndSmartPointers(InstantiationCollectContext &context, + const AbstractMetaClassCPtr &metaClass); + void collectInstantiatedContainersAndSmartPointers(InstantiationCollectContext &context); + void collectInstantiatedOpqaqueContainers(InstantiationCollectContext &context); + void collectContainerTypesFromSnippets(InstantiationCollectContext &context); + void collectContainerTypesFromConverterMacros(InstantiationCollectContext &context, + const QString &code, + bool toPythonMacro); + void addInstantiatedSmartPointer(InstantiationCollectContext &context, + const AbstractMetaType &type); + + AbstractMetaBuilder *m_builder = nullptr; +}; + +ApiExtractor::ApiExtractor() : + d(new ApiExtractorPrivate) { - m_logDirectory = logDir; } -void ApiExtractor::setCppFileNames(const QFileInfoList &cppFileName) +ApiExtractor::~ApiExtractor() { - m_cppFileNames = cppFileName; + delete d->m_builder; + delete d; } -void ApiExtractor::setTypeSystem(const QString& typeSystemFileName) +HeaderPaths ApiExtractor::includePaths() const { - m_typeSystemFileName = typeSystemFileName; + return d->m_includePaths; } -void ApiExtractor::setSkipDeprecated(bool value) +void ApiExtractor::setLogDirectory(const QString& logDir) { - m_skipDeprecated = value; - if (m_builder) - m_builder->setSkipDeprecated(m_skipDeprecated); + d->m_logDirectory = logDir; } -void ApiExtractor::setSuppressWarnings ( bool value ) +void ApiExtractor::setCppFileNames(const QFileInfoList &cppFileName) { - TypeDatabase::instance()->setSuppressWarnings(value); + d->m_cppFileNames = cppFileName; } -void ApiExtractor::setSilent ( bool value ) +QFileInfoList ApiExtractor::cppFileNames() const { - ReportHandler::setSilent(value); + return d->m_cppFileNames; } -bool ApiExtractor::setApiVersion(const QString& package, const QString &version) +void ApiExtractor::setTypeSystem(const QString& typeSystemFileName) { - return TypeDatabase::setApiVersion(package, version); + d->m_typeSystemFileName = typeSystemFileName; } -void ApiExtractor::setDropTypeEntries(const QStringList &dropEntries) +QString ApiExtractor::typeSystem() const { - TypeDatabase::instance()->setDropTypeEntries(dropEntries); + return d->m_typeSystemFileName; } const AbstractMetaEnumList &ApiExtractor::globalEnums() const { - Q_ASSERT(m_builder); - return m_builder->globalEnums(); + Q_ASSERT(d->m_builder); + return d->m_builder->globalEnums(); } const AbstractMetaFunctionCList &ApiExtractor::globalFunctions() const { - Q_ASSERT(m_builder); - return m_builder->globalFunctions(); + Q_ASSERT(d->m_builder); + return d->m_builder->globalFunctions(); } const AbstractMetaClassList &ApiExtractor::classes() const { - Q_ASSERT(m_builder); - return m_builder->classes(); + Q_ASSERT(d->m_builder); + return d->m_builder->classes(); } const AbstractMetaClassList &ApiExtractor::smartPointers() const { - Q_ASSERT(m_builder); - return m_builder->smartPointers(); + Q_ASSERT(d->m_builder); + return d->m_builder->smartPointers(); } // Add defines required for parsing Qt code headers @@ -171,7 +329,7 @@ static void addPySideExtensions(QByteArrayList *a) a->append(QByteArrayLiteral("-DQSIMD_H")); } -bool ApiExtractor::runHelper(bool usePySideExtensions) +bool ApiExtractorPrivate::runHelper(ApiExtractorFlags flags) { if (m_builder) return false; @@ -181,9 +339,8 @@ bool ApiExtractor::runHelper(bool usePySideExtensions) return false; } - const QString pattern = QDir::tempPath() + QLatin1Char('/') - + m_cppFileNames.constFirst().baseName() - + QStringLiteral("_XXXXXX.hpp"); + const QString pattern = QDir::tempPath() + u'/' + + m_cppFileNames.constFirst().baseName() + "_XXXXXX.hpp"_L1; QTemporaryFile ppFile(pattern); bool autoRemove = !qEnvironmentVariableIsSet("KEEP_TEMP_FILES"); // make sure that a tempfile can be written @@ -192,7 +349,7 @@ bool ApiExtractor::runHelper(bool usePySideExtensions) << ": " << qPrintable(ppFile.errorString()) << '\n'; return false; } - for (const auto &cppFileName : qAsConst(m_cppFileNames)) { + for (const auto &cppFileName : std::as_const(m_cppFileNames)) { ppFile.write("#include \""); ppFile.write(cppFileName.absoluteFilePath().toLocal8Bit()); ppFile.write("\"\n"); @@ -204,6 +361,7 @@ bool ApiExtractor::runHelper(bool usePySideExtensions) m_builder->setGlobalHeaders(m_cppFileNames); m_builder->setSkipDeprecated(m_skipDeprecated); m_builder->setHeaderPaths(m_includePaths); + m_builder->setApiExtractorFlags(flags); QByteArrayList arguments; const auto clangOptionsSize = m_clangOptions.size(); @@ -220,19 +378,20 @@ bool ApiExtractor::runHelper(bool usePySideExtensions) arguments.append(m_clangOptions.at(i).toUtf8()); } - for (const HeaderPath &headerPath : qAsConst(m_includePaths)) + for (const HeaderPath &headerPath : std::as_const(m_includePaths)) arguments.append(HeaderPath::includeOption(headerPath)); + if (flags.testFlag(ApiExtractorFlag::UsePySideExtensions)) + addPySideExtensions(&arguments); arguments.append(QFile::encodeName(preprocessedCppFileName)); + if (ReportHandler::isDebug(ReportHandler::SparseDebug)) { qCInfo(lcShiboken).noquote().nospace() << "clang language level: " << int(m_languageLevel) << "\nclang arguments: " << arguments; } - if (usePySideExtensions) - addPySideExtensions(&arguments); - - const bool result = m_builder->build(arguments, addCompilerSupportArguments, m_languageLevel); + const bool result = m_builder->build(arguments, flags, addCompilerSupportArguments, + m_languageLevel); if (!result) autoRemove = false; if (!autoRemove) { @@ -248,42 +407,408 @@ static inline void classListToCList(const AbstractMetaClassList &list, AbstractM std::copy(list.cbegin(), list.cend(), std::back_inserter(*target)); } -std::optional<ApiExtractorResult> ApiExtractor::run(bool usePySideExtensions) +std::optional<ApiExtractorResult> ApiExtractor::run(ApiExtractorFlags flags) { - if (!runHelper(usePySideExtensions)) + if (!d->runHelper(flags)) return {}; + InstantiationCollectContext collectContext; + d->collectInstantiatedContainersAndSmartPointers(collectContext); + ApiExtractorResult result; - classListToCList(m_builder->classes(), &result.m_metaClasses); - classListToCList(m_builder->smartPointers(), &result.m_smartPointers); - result.m_globalFunctions = m_builder->globalFunctions(); - result.m_globalEnums = m_builder->globalEnums(); - result.m_enums = m_builder->typeEntryToEnumsHash(); + classListToCList(d->m_builder->takeClasses(), &result.m_metaClasses); + classListToCList(d->m_builder->takeSmartPointers(), &result.m_smartPointers); + result.m_globalFunctions = d->m_builder->globalFunctions(); + result.m_globalEnums = d->m_builder->globalEnums(); + result.m_enums = d->m_builder->typeEntryToEnumsHash(); + result.m_flags = flags; + result.m_typedefTargetToName = d->m_builder->typedefTargetToName(); + qSwap(result.m_instantiatedContainers, collectContext.instantiatedContainers); + qSwap(result.m_instantiatedSmartPointers, collectContext.instantiatedSmartPointers); return result; } LanguageLevel ApiExtractor::languageLevel() const { - return m_languageLevel; + return d->m_languageLevel; } -void ApiExtractor::setLanguageLevel(LanguageLevel languageLevel) +QStringList ApiExtractor::clangOptions() const { - m_languageLevel = languageLevel; + return d->m_clangOptions; } -QStringList ApiExtractor::clangOptions() const +AbstractMetaFunctionPtr + ApiExtractor::inheritTemplateFunction(const AbstractMetaFunctionCPtr &function, + const AbstractMetaTypeList &templateTypes) +{ + return AbstractMetaBuilder::inheritTemplateFunction(function, templateTypes); +} + +AbstractMetaFunctionPtr + ApiExtractor::inheritTemplateMember(const AbstractMetaFunctionCPtr &function, + const AbstractMetaTypeList &templateTypes, + const AbstractMetaClassCPtr &templateClass, + const AbstractMetaClassPtr &subclass) { - return m_clangOptions; + return AbstractMetaBuilder::inheritTemplateMember(function, templateTypes, + templateClass, subclass); } -void ApiExtractor::setClangOptions(const QStringList &co) +AbstractMetaClassPtr ApiExtractor::inheritTemplateClass(const ComplexTypeEntryPtr &te, + const AbstractMetaClassCPtr &templateClass, + const AbstractMetaTypeList &templateTypes, + InheritTemplateFlags flags) { - m_clangOptions = co; + return AbstractMetaBuilder::inheritTemplateClass(te, templateClass, + templateTypes, flags); } -void ApiExtractor::setUseGlobalHeader(bool h) +QString ApiExtractorPrivate::getSimplifiedContainerTypeName(const AbstractMetaType &type) { - AbstractMetaBuilder::setUseGlobalHeader(h); + 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(u'*') || typeName.endsWith(u' ')) + 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 TypeEntryCPtr pointeeTypeEntry(const AbstractMetaType &smartPtrType) +{ + return smartPtrType.instantiations().constFirst().typeEntry(); +} + +static AbstractMetaType simplifiedType(AbstractMetaType type) +{ + type.setIndirections(0); + type.setConstant(false); + type.setReferenceType(NoReference); + type.decideUsagePattern(); + return type; +} + +void +ApiExtractorPrivate::addInstantiatedContainersAndSmartPointers(InstantiationCollectContext &context, + const AbstractMetaType &type, + const QString &contextName) +{ + for (const auto &t : type.instantiations()) + addInstantiatedContainersAndSmartPointers(context, t, contextName); + const auto typeEntry = type.typeEntry(); + const bool isContainer = typeEntry->isContainer(); + if (!isContainer + && !(typeEntry->isSmartPointer() && typeEntry->generateCode())) { + return; + } + if (type.hasTemplateChildren()) { + const auto piece = isContainer ? "container"_L1 : "smart pointer"_L1; + QString warning = + QString::fromLatin1("Skipping instantiation of %1 '%2' because it has template" + " arguments.").arg(piece, type.originalTypeDescription()); + if (!contextName.isEmpty()) + warning.append(" Calling context: "_L1 + contextName); + + qCWarning(lcShiboken).noquote().nospace() << warning; + return; + + } + if (isContainer) { + const QString typeName = getSimplifiedContainerTypeName(type); + if (!context.instantiatedContainersNames.contains(typeName)) { + context.instantiatedContainersNames.append(typeName); + context.instantiatedContainers.append(simplifiedType(type)); + } + return; + } + + // 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(context.instantiatedSmartPointers.cbegin(), + context.instantiatedSmartPointers.cend(), + [typeEntry, pt] (const InstantiatedSmartPointer &smp) { + return smp.type.typeEntry() == typeEntry + && pointeeTypeEntry(smp.type) == pt; + }); + if (!present) + addInstantiatedSmartPointer(context, type); +} + +// Create a modification that invalidates the pointee argument of a smart +// pointer constructor or reset(). +static FunctionModification invalidateArgMod(const AbstractMetaFunctionCPtr &f, int index = 1) +{ + ArgumentModification argMod; + argMod.setTargetOwnerShip(TypeSystem::CppOwnership); + argMod.setIndex(index); + FunctionModification funcMod; + funcMod.setSignature(f->minimalSignature()); + funcMod.setArgument_mods({argMod}); + return funcMod; +} + +static void addOwnerModification(const AbstractMetaFunctionCList &functions, + const ComplexTypeEntryPtr &typeEntry) +{ + for (const auto &f : functions) { + if (!f->arguments().isEmpty() + && f->arguments().constFirst().type().indirections() > 0) { + std::const_pointer_cast<AbstractMetaFunction>(f)->clearModificationsCache(); + typeEntry->addFunctionModification(invalidateArgMod(f)); + } + } +} + +void ApiExtractorPrivate::addInstantiatedSmartPointer(InstantiationCollectContext &context, + const AbstractMetaType &type) +{ + InstantiatedSmartPointer smp; + smp.type = canonicalSmartPtrInstantiation(type); + smp.smartPointer = AbstractMetaClass::findClass(m_builder->smartPointers(), + type.typeEntry()); + Q_ASSERT(smp.smartPointer); + + const auto &instantiatedType = type.instantiations().constFirst(); + const auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(smp.smartPointer->typeEntry()); + QString name = ste->getTargetName(smp.type); + auto parentTypeEntry = ste->parent(); + InheritTemplateFlags flags; + + auto colonPos = name.lastIndexOf(u"::"); + const bool withinNameSpace = colonPos != -1; + if (withinNameSpace) { // user defined + const QString nameSpace = name.left(colonPos); + name.remove(0, colonPos + 2); + const auto nameSpaces = TypeDatabase::instance()->findNamespaceTypes(nameSpace); + if (nameSpaces.isEmpty()) + throw Exception(msgNamespaceNotFound(name)); + parentTypeEntry = nameSpaces.constFirst(); + } else { + flags.setFlag(InheritTemplateFlag::SetEnclosingClass); + } + + TypedefEntryPtr typedefEntry(new TypedefEntry(name, ste->name(), ste->version(), + parentTypeEntry)); + typedefEntry->setTargetLangPackage(ste->targetLangPackage()); + auto instantiationEntry = TypeDatabase::initializeTypeDefEntry(typedefEntry, ste); + + smp.specialized = ApiExtractor::inheritTemplateClass(instantiationEntry, smp.smartPointer, + {instantiatedType}, flags); + Q_ASSERT(smp.specialized); + if (withinNameSpace) { // move class to desired namespace + const auto enclClass = AbstractMetaClass::findClass(m_builder->classes(), parentTypeEntry); + Q_ASSERT(enclClass); + auto specialized = std::const_pointer_cast<AbstractMetaClass>(smp.specialized); + specialized->setEnclosingClass(enclClass); + enclClass->addInnerClass(specialized); + } + + if (instantiationEntry->isComplex()) { + addOwnerModification(smp.specialized->queryFunctions(FunctionQueryOption::Constructors), + instantiationEntry); + if (!ste->resetMethod().isEmpty()) { + addOwnerModification(smp.specialized->findFunctions(ste->resetMethod()), + instantiationEntry); + } + } + + context.instantiatedSmartPointers.append(smp); +} + +void +ApiExtractorPrivate::collectInstantiatedContainersAndSmartPointers(InstantiationCollectContext &context, + const AbstractMetaFunctionCPtr &func) +{ + addInstantiatedContainersAndSmartPointers(context, func->type(), func->signature()); + for (const AbstractMetaArgument &arg : func->arguments()) { + const auto argType = arg.type(); + const auto type = argType.viewOn() != nullptr ? *argType.viewOn() : argType; + addInstantiatedContainersAndSmartPointers(context, type, func->signature()); + } +} + +void +ApiExtractorPrivate::collectInstantiatedContainersAndSmartPointers(InstantiationCollectContext &context, + const AbstractMetaClassCPtr &metaClass) +{ + if (!metaClass->typeEntry()->generateCode()) + return; + for (const auto &func : metaClass->functions()) + collectInstantiatedContainersAndSmartPointers(context, func); + for (const auto &func : metaClass->userAddedPythonOverrides()) + collectInstantiatedContainersAndSmartPointers(context, func); + for (const AbstractMetaField &field : metaClass->fields()) + addInstantiatedContainersAndSmartPointers(context, field.type(), field.name()); + + // The list of inner classes might be extended when smart pointer + // instantiations are specified to be in namespaces. + const auto &innerClasses = metaClass->innerClasses(); + for (auto i = innerClasses.size() - 1; i >= 0; --i) { + const auto innerClass = innerClasses.at(i); + if (!innerClass->typeEntry()->isSmartPointer()) + collectInstantiatedContainersAndSmartPointers(context, innerClass); + } +} + +void +ApiExtractorPrivate::collectInstantiatedContainersAndSmartPointers(InstantiationCollectContext &context) +{ + collectInstantiatedOpqaqueContainers(context); + for (const auto &func : m_builder->globalFunctions()) + collectInstantiatedContainersAndSmartPointers(context, func); + for (const auto &metaClass : m_builder->classes()) + collectInstantiatedContainersAndSmartPointers(context, metaClass); + collectContainerTypesFromSnippets(context); +} + +// Whether to generate an opaque container: If the instantiation type is in +// the current package or, for primitive types, if the container is in the +// current package. +static bool generateOpaqueContainer(const AbstractMetaType &type, + const TypeSystemTypeEntryCPtr &moduleEntry) +{ + auto te = type.instantiations().constFirst().typeEntry(); + auto typeModuleEntry = typeSystemTypeEntry(te); + return typeModuleEntry == moduleEntry + || (te->isPrimitive() && typeSystemTypeEntry(type.typeEntry()) == moduleEntry); +} + +void ApiExtractorPrivate::collectInstantiatedOpqaqueContainers(InstantiationCollectContext &context) +{ + // Add all instantiations of opaque containers for types from the current + // module. + auto *td = TypeDatabase::instance(); + const auto moduleEntry = TypeDatabase::instance()->defaultTypeSystemType(); + const auto &containers = td->containerTypes(); + for (const auto &container : containers) { + for (const auto &oc : container->opaqueContainers()) { + QString errorMessage; + const QString typeName = container->qualifiedCppName() + oc.templateParameters(); + auto typeOpt = AbstractMetaType::fromString(typeName, &errorMessage); + if (typeOpt.has_value() + && generateOpaqueContainer(typeOpt.value(), moduleEntry)) { + addInstantiatedContainersAndSmartPointers(context, typeOpt.value(), + u"opaque containers"_s); + } + } + } +} + +static void getCode(QStringList &code, const CodeSnipList &codeSnips) +{ + for (const CodeSnip &snip : std::as_const(codeSnips)) + code.append(snip.code()); +} + +static void getCode(QStringList &code, const TypeEntryCPtr &type) +{ + if (type->isComplex()) + getCode(code, std::static_pointer_cast<const ComplexTypeEntry>(type)->codeSnips()); + else if (type->isTypeSystem()) + getCode(code, std::static_pointer_cast<const TypeSystemTypeEntry>(type)->codeSnips()); + + auto customConversion = CustomConversion::getCustomConversion(type); + if (!customConversion) + return; + + if (!customConversion->nativeToTargetConversion().isEmpty()) + code.append(customConversion->nativeToTargetConversion()); + + const auto &toCppConversions = customConversion->targetToNativeConversions(); + if (toCppConversions.isEmpty()) + return; + + for (const auto &toNative : std::as_const(toCppConversions)) + code.append(toNative.conversion()); +} + +void ApiExtractorPrivate::collectContainerTypesFromSnippets(InstantiationCollectContext &context) +{ + QStringList snips; + auto *td = TypeDatabase::instance(); + const PrimitiveTypeEntryCList &primitiveTypeList = td->primitiveTypes(); + for (const auto &type : primitiveTypeList) + getCode(snips, type); + const ContainerTypeEntryCList &containerTypeList = td->containerTypes(); + for (const auto &type : containerTypeList) + getCode(snips, type); + for (const auto &metaClass : m_builder->classes()) + getCode(snips, metaClass->typeEntry()); + + const auto moduleEntry = td->defaultTypeSystemType(); + Q_ASSERT(moduleEntry); + getCode(snips, moduleEntry); + + for (const auto &func : m_builder->globalFunctions()) + getCode(snips, func->injectedCodeSnips()); + + for (const QString &code : std::as_const(snips)) { + collectContainerTypesFromConverterMacros(context, code, true); + collectContainerTypesFromConverterMacros(context, code, false); + } +} + +void +ApiExtractorPrivate::collectContainerTypesFromConverterMacros(InstantiationCollectContext &context, + const QString &code, + bool toPythonMacro) +{ + QString convMacro = toPythonMacro ? u"%CONVERTTOPYTHON["_s : u"%CONVERTTOCPP["_s; + const qsizetype offset = toPythonMacro ? sizeof("%CONVERTTOPYTHON") : sizeof("%CONVERTTOCPP"); + qsizetype start = 0; + QString errorMessage; + while ((start = code.indexOf(convMacro, start)) != -1) { + int end = code.indexOf(u']', start); + start += offset; + if (code.at(start) != u'%') { + QString typeString = code.mid(start, end - start); + auto type = AbstractMetaType::fromString(typeString, &errorMessage); + if (type.has_value()) { + const QString &d = type->originalTypeDescription(); + addInstantiatedContainersAndSmartPointers(context, type.value(), d); + } else { + QString m; + QTextStream(&m) << __FUNCTION__ << ": Cannot translate type \"" + << typeString << "\": " << errorMessage; + throw Exception(m); + } + } + start = end; + } } #ifndef QT_NO_DEBUG_STREAM @@ -311,7 +836,7 @@ QDebug operator<<(QDebug d, const ApiExtractor &ae) d.setVerbosity(3); // Trigger verbose output of AbstractMetaClass d << "ApiExtractor(typeSystem=\"" << ae.typeSystem() << "\", cppFileNames=\"" << ae.cppFileNames() << ", "; - ae.m_builder->formatDebug(d); + ae.d->m_builder->formatDebug(d); d << ')'; return d; } |