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.cpp843
1 files changed, 843 insertions, 0 deletions
diff --git a/sources/shiboken6/ApiExtractor/apiextractor.cpp b/sources/shiboken6/ApiExtractor/apiextractor.cpp
new file mode 100644
index 000000000..83ee4437e
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/apiextractor.cpp
@@ -0,0 +1,843 @@
+// 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 <algorithm>
+#include <iostream>
+#include <iterator>
+
+using namespace Qt::StringLiterals;
+
+struct InstantiationCollectContext
+{
+ AbstractMetaTypeList instantiatedContainers;
+ InstantiatedSmartPointers instantiatedSmartPointers;
+ QStringList instantiatedContainersNames;
+};
+
+struct ApiExtractorOptions
+{
+ 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')';
+}
+
+QList<OptionDescription> ApiExtractor::options()
+{
+ 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()},
+ };
+}
+
+class ApiExtractorOptionsParser : public OptionsParser
+{
+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 ApiExtractorOptionsParser::parseIncludePathOption(const QStringList &values,
+ HeaderType headerType)
+{
+ for (const auto &value : values)
+ parseIncludePathOption(value, headerType);
+}
+
+void ApiExtractorOptionsParser::setLanguageLevel(const QString &value)
+{
+ 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;
+}
+
+bool ApiExtractorOptionsParser::handleOption(const QString &key, const QString &value,
+ OptionSource source)
+{
+ 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;
+}
+
+std::shared_ptr<OptionsParser> ApiExtractor::createOptionsParser()
+{
+ return std::make_shared<ApiExtractorOptionsParser>(d);
+}
+
+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)
+{
+}
+
+ApiExtractor::~ApiExtractor()
+{
+ delete d->m_builder;
+ delete d;
+}
+
+HeaderPaths ApiExtractor::includePaths() const
+{
+ return d->m_includePaths;
+}
+
+void ApiExtractor::setLogDirectory(const QString& logDir)
+{
+ d->m_logDirectory = logDir;
+}
+
+void ApiExtractor::setCppFileNames(const QFileInfoList &cppFileName)
+{
+ d->m_cppFileNames = cppFileName;
+}
+
+QFileInfoList ApiExtractor::cppFileNames() const
+{
+ return d->m_cppFileNames;
+}
+
+void ApiExtractor::setTypeSystem(const QString& typeSystemFileName)
+{
+ d->m_typeSystemFileName = typeSystemFileName;
+}
+
+QString ApiExtractor::typeSystem() const
+{
+ return d->m_typeSystemFileName;
+}
+
+const AbstractMetaEnumList &ApiExtractor::globalEnums() const
+{
+ Q_ASSERT(d->m_builder);
+ return d->m_builder->globalEnums();
+}
+
+const AbstractMetaFunctionCList &ApiExtractor::globalFunctions() const
+{
+ Q_ASSERT(d->m_builder);
+ return d->m_builder->globalFunctions();
+}
+
+const AbstractMetaClassList &ApiExtractor::classes() const
+{
+ Q_ASSERT(d->m_builder);
+ return d->m_builder->classes();
+}
+
+const AbstractMetaClassList &ApiExtractor::smartPointers() const
+{
+ Q_ASSERT(d->m_builder);
+ return d->m_builder->smartPointers();
+}
+
+// Add defines required for parsing Qt code headers
+static void addPySideExtensions(QByteArrayList *a)
+{
+ // Make "signals:", "slots:" visible as access specifiers
+ a->append(QByteArrayLiteral("-DQT_ANNOTATE_ACCESS_SPECIFIER(a)=__attribute__((annotate(#a)))"));
+
+ // Q_PROPERTY is defined as class annotation which does not work since a
+ // sequence of properties will to expand to a sequence of annotations
+ // annotating nothing, causing clang to complain. Instead, define it away in a
+ // static assert with the stringified argument in a ','-operator (cf qdoc).
+ a->append(QByteArrayLiteral("-DQT_ANNOTATE_CLASS(type,...)=static_assert(sizeof(#__VA_ARGS__),#type);"));
+
+ // With Qt6, qsimd.h became public header and was included in <QtCore>. That
+ // introduced a conflict with libclang headers on macOS. To be able to include
+ // <QtCore>, we prevent its inclusion by adding its include guard.
+ a->append(QByteArrayLiteral("-DQSIMD_H"));
+}
+
+bool ApiExtractorPrivate::runHelper(ApiExtractorFlags flags)
+{
+ if (m_builder)
+ return false;
+
+ if (!TypeDatabase::instance()->parseFile(m_typeSystemFileName)) {
+ std::cerr << "Cannot parse file: " << qPrintable(m_typeSystemFileName);
+ return false;
+ }
+
+ 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
+ if (!ppFile.open()) {
+ std::cerr << "could not create tempfile " << qPrintable(pattern)
+ << ": " << qPrintable(ppFile.errorString()) << '\n';
+ return false;
+ }
+ for (const auto &cppFileName : std::as_const(m_cppFileNames)) {
+ ppFile.write("#include \"");
+ ppFile.write(cppFileName.absoluteFilePath().toLocal8Bit());
+ ppFile.write("\"\n");
+ }
+ const QString preprocessedCppFileName = ppFile.fileName();
+ ppFile.close();
+ m_builder = new AbstractMetaBuilder;
+ m_builder->setLogDirectory(m_logDirectory);
+ 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();
+ arguments.reserve(m_includePaths.size() + clangOptionsSize + 1);
+
+ bool addCompilerSupportArguments = true;
+ if (clangOptionsSize > 0) {
+ qsizetype i = 0;
+ if (m_clangOptions.at(i) == u"-") {
+ ++i;
+ addCompilerSupportArguments = false; // No built-in options
+ }
+ for (; i < clangOptionsSize; ++i)
+ arguments.append(m_clangOptions.at(i).toUtf8());
+ }
+
+ 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;
+ }
+
+ const bool result = m_builder->build(arguments, flags, addCompilerSupportArguments,
+ m_languageLevel);
+ if (!result)
+ autoRemove = false;
+ if (!autoRemove) {
+ ppFile.setAutoRemove(false);
+ std::cerr << "Keeping temporary file: " << qPrintable(QDir::toNativeSeparators(preprocessedCppFileName)) << '\n';
+ }
+ return result;
+}
+
+static inline void classListToCList(const AbstractMetaClassList &list, AbstractMetaClassCList *target)
+{
+ target->reserve(list.size());
+ std::copy(list.cbegin(), list.cend(), std::back_inserter(*target));
+}
+
+std::optional<ApiExtractorResult> ApiExtractor::run(ApiExtractorFlags flags)
+{
+ if (!d->runHelper(flags))
+ return {};
+ InstantiationCollectContext collectContext;
+ d->collectInstantiatedContainersAndSmartPointers(collectContext);
+
+ ApiExtractorResult result;
+ 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 d->m_languageLevel;
+}
+
+QStringList ApiExtractor::clangOptions() const
+{
+ return d->m_clangOptions;
+}
+
+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 AbstractMetaBuilder::inheritTemplateMember(function, templateTypes,
+ templateClass, subclass);
+}
+
+AbstractMetaClassPtr ApiExtractor::inheritTemplateClass(const ComplexTypeEntryPtr &te,
+ const AbstractMetaClassCPtr &templateClass,
+ const AbstractMetaTypeList &templateTypes,
+ InheritTemplateFlags flags)
+{
+ return AbstractMetaBuilder::inheritTemplateClass(te, templateClass,
+ templateTypes, flags);
+}
+
+QString ApiExtractorPrivate::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(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
+template <class Container>
+static void debugFormatSequence(QDebug &d, const char *key, const Container& c)
+{
+ if (c.isEmpty())
+ return;
+ const auto begin = c.begin();
+ d << "\n " << key << '[' << c.size() << "]=(";
+ for (auto it = begin, end = c.end(); it != end; ++it) {
+ if (it != begin)
+ d << ", ";
+ d << *it;
+ }
+ d << ')';
+}
+
+QDebug operator<<(QDebug d, const ApiExtractor &ae)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ if (ReportHandler::debugLevel() >= ReportHandler::FullDebug)
+ d.setVerbosity(3); // Trigger verbose output of AbstractMetaClass
+ d << "ApiExtractor(typeSystem=\"" << ae.typeSystem() << "\", cppFileNames=\""
+ << ae.cppFileNames() << ", ";
+ ae.d->m_builder->formatDebug(d);
+ d << ')';
+ return d;
+}
+#endif // QT_NO_DEBUG_STREAM