aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/ApiExtractor/apiextractor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/ApiExtractor/apiextractor.cpp')
-rw-r--r--sources/shiboken6/ApiExtractor/apiextractor.cpp745
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;
}