aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/ApiExtractor/typedatabase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/ApiExtractor/typedatabase.cpp')
-rw-r--r--sources/shiboken6/ApiExtractor/typedatabase.cpp1047
1 files changed, 1047 insertions, 0 deletions
diff --git a/sources/shiboken6/ApiExtractor/typedatabase.cpp b/sources/shiboken6/ApiExtractor/typedatabase.cpp
new file mode 100644
index 000000000..6b56d362e
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/typedatabase.cpp
@@ -0,0 +1,1047 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "typedatabase.h"
+#include "typesystem.h"
+#include "typesystemparser.h"
+
+#include <QtCore/QFile>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QPair>
+#include <QtCore/QVector>
+#include <QtCore/QRegularExpression>
+#include <QtCore/QVersionNumber>
+#include <QtCore/QXmlStreamReader>
+#include "reporthandler.h"
+// #include <tr1/tuple>
+#include <algorithm>
+
+// package -> api-version
+
+static QString wildcardToRegExp(QString w)
+{
+ w.replace(QLatin1Char('?'), QLatin1Char('.'));
+ w.replace(QLatin1Char('*'), QStringLiteral(".*"));
+ return w;
+}
+
+using ApiVersion =QPair<QRegularExpression, QVersionNumber>;
+using ApiVersions = QVector<ApiVersion>;
+
+Q_GLOBAL_STATIC(ApiVersions, apiVersions)
+
+TypeDatabase::TypeDatabase()
+{
+ addType(new VoidTypeEntry());
+ addType(new VarargsTypeEntry());
+}
+
+TypeDatabase::~TypeDatabase() = default;
+
+TypeDatabase* TypeDatabase::instance(bool newInstance)
+{
+ static TypeDatabase *db = nullptr;
+ if (!db || newInstance) {
+ delete db;
+ db = new TypeDatabase;
+ }
+ return db;
+}
+
+// A list of regex/replacements to fix int types like "ushort" to "unsigned short"
+// unless present in TypeDatabase
+struct IntTypeNormalizationEntry
+{
+ QRegularExpression regex;
+ QString replacement;
+};
+
+using IntTypeNormalizationEntries = QVector<IntTypeNormalizationEntry>;
+
+static const IntTypeNormalizationEntries &intTypeNormalizationEntries()
+{
+ static IntTypeNormalizationEntries result;
+ static bool firstTime = true;
+ if (firstTime) {
+ firstTime = false;
+ for (auto t : {"char", "short", "int", "long"}) {
+ const QString intType = QLatin1String(t);
+ if (!TypeDatabase::instance()->findType(QLatin1Char('u') + intType)) {
+ IntTypeNormalizationEntry entry;
+ entry.replacement = QStringLiteral("unsigned ") + intType;
+ entry.regex.setPattern(QStringLiteral("\\bu") + intType + QStringLiteral("\\b"));
+ Q_ASSERT(entry.regex.isValid());
+ result.append(entry);
+ }
+ }
+ }
+ return result;
+}
+
+QString TypeDatabase::normalizedSignature(const QString &signature)
+{
+ QString normalized = QLatin1String(QMetaObject::normalizedSignature(signature.toUtf8().constData()));
+
+ if (instance() && signature.contains(QLatin1String("unsigned"))) {
+ const IntTypeNormalizationEntries &entries = intTypeNormalizationEntries();
+ for (const auto &entry : entries)
+ normalized.replace(entry.regex, entry.replacement);
+ }
+
+ return normalized;
+}
+
+QStringList TypeDatabase::requiredTargetImports() const
+{
+ return m_requiredTargetImports;
+}
+
+void TypeDatabase::addRequiredTargetImport(const QString& moduleName)
+{
+ if (!m_requiredTargetImports.contains(moduleName))
+ m_requiredTargetImports << moduleName;
+}
+
+void TypeDatabase::addTypesystemPath(const QString& typesystem_paths)
+{
+ #if defined(Q_OS_WIN32)
+ const char path_splitter = ';';
+ #else
+ const char path_splitter = ':';
+ #endif
+ m_typesystemPaths += typesystem_paths.split(QLatin1Char(path_splitter));
+}
+
+IncludeList TypeDatabase::extraIncludes(const QString& className) const
+{
+ ComplexTypeEntry* typeEntry = findComplexType(className);
+ return typeEntry ? typeEntry->extraIncludes() : IncludeList();
+}
+
+void TypeDatabase::addSystemInclude(const QString &name)
+{
+ m_systemIncludes.append(name.toUtf8());
+}
+
+// Add a lookup for the short name excluding inline namespaces
+// so that "std::shared_ptr" finds "std::__1::shared_ptr" as well.
+// Note: This inserts duplicate TypeEntry * into m_entries.
+void TypeDatabase::addInlineNamespaceLookups(const NamespaceTypeEntry *n)
+{
+ QVector<TypeEntry *> additionalEntries; // Store before modifying the hash
+ for (TypeEntry *entry : m_entries) {
+ if (entry->isChildOf(n))
+ additionalEntries.append(entry);
+ }
+ for (const auto &ae : additionalEntries)
+ m_entries.insert(ae->shortName(), ae);
+}
+
+ContainerTypeEntry* TypeDatabase::findContainerType(const QString &name) const
+{
+ QString template_name = name;
+
+ int pos = name.indexOf(QLatin1Char('<'));
+ if (pos > 0)
+ template_name = name.left(pos);
+
+ TypeEntry* type_entry = findType(template_name);
+ if (type_entry && type_entry->isContainer())
+ return static_cast<ContainerTypeEntry*>(type_entry);
+ return nullptr;
+}
+
+static bool inline useType(const TypeEntry *t)
+{
+ return !t->isPrimitive()
+ || static_cast<const PrimitiveTypeEntry *>(t)->preferredTargetLangType();
+}
+
+FunctionTypeEntry* TypeDatabase::findFunctionType(const QString& name) const
+{
+ const auto entries = findTypeRange(name);
+ for (TypeEntry *entry : entries) {
+ if (entry->type() == TypeEntry::FunctionType && useType(entry))
+ return static_cast<FunctionTypeEntry*>(entry);
+ }
+ return nullptr;
+}
+
+void TypeDatabase::addTypeSystemType(const TypeSystemTypeEntry *e)
+{
+ m_typeSystemEntries.append(e);
+}
+
+const TypeSystemTypeEntry *TypeDatabase::findTypeSystemType(const QString &name) const
+{
+ for (auto entry : m_typeSystemEntries) {
+ if (entry->name() == name)
+ return entry;
+ }
+ return nullptr;
+}
+
+const TypeSystemTypeEntry *TypeDatabase::defaultTypeSystemType() const
+{
+ return m_typeSystemEntries.value(0, nullptr);
+}
+
+QString TypeDatabase::defaultPackageName() const
+{
+ Q_ASSERT(!m_typeSystemEntries.isEmpty());
+ return m_typeSystemEntries.constFirst()->name();
+}
+
+TypeEntry* TypeDatabase::findType(const QString& name) const
+{
+ const auto entries = findTypeRange(name);
+ for (TypeEntry *entry : entries) {
+ if (useType(entry))
+ return entry;
+ }
+ return nullptr;
+}
+
+template <class Predicate>
+TypeEntries TypeDatabase::findTypesHelper(const QString &name, Predicate pred) const
+{
+ TypeEntries result;
+ const auto entries = findTypeRange(name);
+ for (TypeEntry *entry : entries) {
+ if (pred(entry))
+ result.append(entry);
+ }
+ return result;
+}
+
+TypeEntries TypeDatabase::findTypes(const QString &name) const
+{
+ return findTypesHelper(name, useType);
+}
+
+static bool useCppType(const TypeEntry *t)
+{
+ bool result = false;
+ switch (t->type()) {
+ case TypeEntry::PrimitiveType:
+ case TypeEntry::VoidType:
+ case TypeEntry::FlagsType:
+ case TypeEntry::EnumType:
+ case TypeEntry::TemplateArgumentType:
+ case TypeEntry::BasicValueType:
+ case TypeEntry::ContainerType:
+ case TypeEntry::ObjectType:
+ case TypeEntry::ArrayType:
+ case TypeEntry::CustomType:
+ case TypeEntry::SmartPointerType:
+ case TypeEntry::TypedefType:
+ result = useType(t);
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+TypeEntries TypeDatabase::findCppTypes(const QString &name) const
+{
+ return findTypesHelper(name, useCppType);
+}
+
+TypeEntryMultiMapConstIteratorRange TypeDatabase::findTypeRange(const QString &name) const
+{
+ const auto range = m_entries.equal_range(name);
+ return {range.first, range.second};
+}
+
+PrimitiveTypeEntryList TypeDatabase::primitiveTypes() const
+{
+ PrimitiveTypeEntryList returned;
+ for (auto it = m_entries.cbegin(), end = m_entries.cend(); it != end; ++it) {
+ TypeEntry *typeEntry = it.value();
+ if (typeEntry->isPrimitive())
+ returned.append(static_cast<PrimitiveTypeEntry *>(typeEntry));
+ }
+ return returned;
+}
+
+ContainerTypeEntryList TypeDatabase::containerTypes() const
+{
+ ContainerTypeEntryList returned;
+ for (auto it = m_entries.cbegin(), end = m_entries.cend(); it != end; ++it) {
+ TypeEntry *typeEntry = it.value();
+ if (typeEntry->isContainer())
+ returned.append(static_cast<ContainerTypeEntry *>(typeEntry));
+ }
+ return returned;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const TypeRejection &r)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "TypeRejection(type=" << r.matchType << ", class="
+ << r.className.pattern() << ", pattern=" << r.pattern.pattern() << ')';
+ return d;
+}
+#endif // !QT_NO_DEBUG_STREAM
+
+void TypeDatabase::addRejection(const TypeRejection &r)
+{
+ m_rejections << r;
+}
+
+static inline QString msgRejectReason(const TypeRejection &r, const QString &needle = QString())
+{
+ QString result;
+ QTextStream str(&result);
+ switch (r.matchType) {
+ case TypeRejection::ExcludeClass:
+ str << " matches class exclusion \"" << r.className.pattern() << '"';
+ break;
+ case TypeRejection::Function:
+ case TypeRejection::Field:
+ case TypeRejection::Enum:
+ str << " matches class \"" << r.className.pattern() << "\" and \"" << r.pattern.pattern() << '"';
+ break;
+ case TypeRejection::ArgumentType:
+ case TypeRejection::ReturnType:
+ str << " matches class \"" << r.className.pattern() << "\" and \"" << needle
+ << "\" matches \"" << r.pattern.pattern() << '"';
+ break;
+ case TypeRejection::Invalid:
+ break;
+ }
+ return result;
+}
+
+// Match class name only
+bool TypeDatabase::isClassRejected(const QString& className, QString *reason) const
+{
+ for (const TypeRejection& r : m_rejections) {
+ if (r.matchType == TypeRejection::ExcludeClass && r.className.match(className).hasMatch()) {
+ if (reason)
+ *reason = msgRejectReason(r);
+ return true;
+ }
+ }
+ return false;
+}
+
+// Match class name and function/enum/field
+static bool findRejection(const QVector<TypeRejection> &rejections,
+ TypeRejection::MatchType matchType,
+ const QString& className, const QString& name,
+ QString *reason = nullptr)
+{
+ Q_ASSERT(matchType != TypeRejection::ExcludeClass);
+ for (const TypeRejection& r : rejections) {
+ if (r.matchType == matchType && r.pattern.match(name).hasMatch()
+ && r.className.match(className).hasMatch()) {
+ if (reason)
+ *reason = msgRejectReason(r, name);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool TypeDatabase::isEnumRejected(const QString& className, const QString& enumName, QString *reason) const
+{
+ return findRejection(m_rejections, TypeRejection::Enum, className, enumName, reason);
+}
+
+TypeEntry *TypeDatabase::resolveTypeDefEntry(TypedefEntry *typedefEntry,
+ QString *errorMessage)
+{
+ QString sourceName = typedefEntry->sourceType();
+ const int lessThanPos = sourceName.indexOf(QLatin1Char('<'));
+ if (lessThanPos != -1)
+ sourceName.truncate(lessThanPos);
+ ComplexTypeEntry *source = nullptr;
+ for (TypeEntry *e : findTypeRange(sourceName)) {
+ switch (e->type()) {
+ case TypeEntry::BasicValueType:
+ case TypeEntry::ContainerType:
+ case TypeEntry::ObjectType:
+ case TypeEntry::SmartPointerType:
+ source = dynamic_cast<ComplexTypeEntry *>(e);
+ Q_ASSERT(source);
+ break;
+ default:
+ break;
+ }
+ }
+ if (!source) {
+ if (errorMessage)
+ *errorMessage = QLatin1String("Unable to resolve typedef \"")
+ + typedefEntry->sourceType() + QLatin1Char('"');
+ return nullptr;
+ }
+
+ auto *result = static_cast<ComplexTypeEntry *>(source->clone());
+ result->useAsTypedef(typedefEntry);
+ typedefEntry->setSource(source);
+ typedefEntry->setTarget(result);
+ m_typedefEntries.insert(typedefEntry->qualifiedCppName(), typedefEntry);
+ return result;
+}
+
+bool TypeDatabase::addType(TypeEntry *e, QString *errorMessage)
+{
+ if (e->type() == TypeEntry::TypedefType) {
+ e = resolveTypeDefEntry(static_cast<TypedefEntry *>(e), errorMessage);
+ if (Q_UNLIKELY(!e))
+ return false;
+ }
+ m_entries.insert(e->qualifiedCppName(), e);
+ return true;
+}
+
+// Add a dummy value entry for non-type template parameters
+ConstantValueTypeEntry *
+ TypeDatabase::addConstantValueTypeEntry(const QString &value,
+ const TypeEntry *parent)
+{
+ auto result = new ConstantValueTypeEntry(value, parent);
+ result->setCodeGeneration(TypeEntry::GenerateNothing);
+ addType(result);
+ return result;
+}
+
+bool TypeDatabase::isFunctionRejected(const QString& className, const QString& functionName,
+ QString *reason) const
+{
+ return findRejection(m_rejections, TypeRejection::Function, className, functionName, reason);
+}
+
+bool TypeDatabase::isFieldRejected(const QString& className, const QString& fieldName,
+ QString *reason) const
+{
+ return findRejection(m_rejections, TypeRejection::Field, className, fieldName, reason);
+}
+
+bool TypeDatabase::isArgumentTypeRejected(const QString& className, const QString& typeName,
+ QString *reason) const
+{
+ return findRejection(m_rejections, TypeRejection::ArgumentType, className, typeName, reason);
+}
+
+bool TypeDatabase::isReturnTypeRejected(const QString& className, const QString& typeName,
+ QString *reason) const
+{
+ return findRejection(m_rejections, TypeRejection::ReturnType, className, typeName, reason);
+}
+
+FlagsTypeEntry* TypeDatabase::findFlagsType(const QString &name) const
+{
+ TypeEntry *fte = findType(name);
+ if (!fte) {
+ fte = m_flagsEntries.value(name);
+ if (!fte) {
+ //last hope, search for flag without scope inside of flags hash
+ for (auto it = m_flagsEntries.cbegin(), end = m_flagsEntries.cend(); it != end; ++it) {
+ if (it.key().endsWith(name)) {
+ fte = it.value();
+ break;
+ }
+ }
+ }
+ }
+ return static_cast<FlagsTypeEntry *>(fte);
+}
+
+void TypeDatabase::addFlagsType(FlagsTypeEntry *fte)
+{
+ m_flagsEntries[fte->originalName()] = fte;
+}
+
+void TypeDatabase::addTemplate(TemplateEntry *t)
+{
+ m_templates[t->name()] = t;
+}
+
+void TypeDatabase::addGlobalUserFunctions(const AddedFunctionList &functions)
+{
+ m_globalUserFunctions << functions;
+}
+
+AddedFunctionList TypeDatabase::findGlobalUserFunctions(const QString& name) const
+{
+ AddedFunctionList addedFunctions;
+ for (const AddedFunctionPtr &func : m_globalUserFunctions) {
+ if (func->name() == name)
+ addedFunctions.append(func);
+ }
+ return addedFunctions;
+}
+
+void TypeDatabase::addGlobalUserFunctionModifications(const FunctionModificationList &functionModifications)
+{
+ m_functionMods << functionModifications;
+}
+
+QString TypeDatabase::globalNamespaceClassName(const TypeEntry * /*entry*/)
+{
+ return QLatin1String("Global");
+}
+
+FunctionModificationList TypeDatabase::functionModifications(const QString& signature) const
+{
+ FunctionModificationList lst;
+ for (int i = 0; i < m_functionMods.count(); ++i) {
+ const FunctionModification& mod = m_functionMods.at(i);
+ if (mod.matches(signature))
+ lst << mod;
+ }
+
+ return lst;
+}
+
+bool TypeDatabase::addSuppressedWarning(const QString &warning, QString *errorMessage)
+{
+ QString pattern;
+ if (warning.startsWith(QLatin1Char('^')) && warning.endsWith(QLatin1Char('$'))) {
+ pattern = warning;
+ } else {
+ // Legacy syntax: Use wildcards '*' (unless escaped by '\')
+ QVector<int> asteriskPositions;
+ const int warningSize = warning.size();
+ for (int i = 0; i < warningSize; ++i) {
+ if (warning.at(i) == QLatin1Char('\\'))
+ ++i;
+ else if (warning.at(i) == QLatin1Char('*'))
+ asteriskPositions.append(i);
+ }
+ asteriskPositions.append(warningSize);
+
+ pattern.append(QLatin1Char('^'));
+ int lastPos = 0;
+ for (int a = 0, aSize = asteriskPositions.size(); a < aSize; ++a) {
+ if (a)
+ pattern.append(QStringLiteral(".*"));
+ const int nextPos = asteriskPositions.at(a);
+ if (nextPos > lastPos)
+ pattern.append(QRegularExpression::escape(warning.mid(lastPos, nextPos - lastPos)));
+ lastPos = nextPos + 1;
+ }
+ pattern.append(QLatin1Char('$'));
+ }
+
+ QRegularExpression expression(pattern);
+ if (!expression.isValid()) {
+ *errorMessage = QLatin1String("Invalid message pattern \"") + warning
+ + QLatin1String("\": ") + expression.errorString();
+ return false;
+ }
+ expression.setPatternOptions(expression.patternOptions() | QRegularExpression::MultilineOption);
+
+ m_suppressedWarnings.append(expression);
+ return true;
+}
+
+bool TypeDatabase::isSuppressedWarning(QStringView s) const
+{
+ if (!m_suppressWarnings)
+ return false;
+ return std::any_of(m_suppressedWarnings.cbegin(), m_suppressedWarnings.end(),
+ [&s] (const QRegularExpression &e) {
+ return e.match(s).hasMatch();
+ });
+}
+
+QString TypeDatabase::modifiedTypesystemFilepath(const QString& tsFile, const QString &currentPath) const
+{
+ const QFileInfo tsFi(tsFile);
+ if (tsFi.isAbsolute()) // No point in further lookups
+ return tsFi.absoluteFilePath();
+ if (tsFi.isFile()) // Make path absolute
+ return tsFi.absoluteFilePath();
+ if (!currentPath.isEmpty()) {
+ const QFileInfo fi(currentPath + QLatin1Char('/') + tsFile);
+ if (fi.isFile())
+ return fi.absoluteFilePath();
+ }
+ for (const QString &path : m_typesystemPaths) {
+ const QFileInfo fi(path + QLatin1Char('/') + tsFile);
+ if (fi.isFile())
+ return fi.absoluteFilePath();
+ }
+ return tsFile;
+}
+
+bool TypeDatabase::parseFile(const QString &filename, bool generate)
+{
+ return parseFile(filename, QString(), generate);
+}
+
+bool TypeDatabase::parseFile(const QString &filename, const QString &currentPath, bool generate)
+{
+
+ QString filepath = modifiedTypesystemFilepath(filename, currentPath);
+ if (m_parsedTypesystemFiles.contains(filepath))
+ return m_parsedTypesystemFiles[filepath];
+
+ m_parsedTypesystemFiles[filepath] = true; // Prevent recursion when including self.
+
+ QFile file(filepath);
+ if (!file.exists()) {
+ m_parsedTypesystemFiles[filepath] = false;
+ QString message = QLatin1String("Can't find ") + filename;
+ if (!currentPath.isEmpty())
+ message += QLatin1String(", current path: ") + currentPath;
+ message += QLatin1String(", typesystem paths: ") + m_typesystemPaths.join(QLatin1String(", "));
+ qCWarning(lcShiboken).noquote().nospace() << message;
+ return false;
+ }
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ m_parsedTypesystemFiles[filepath] = false;
+ qCWarning(lcShiboken).noquote().nospace()
+ << "Can't open " << QDir::toNativeSeparators(filename) << ": " << file.errorString();
+ return false;
+ }
+
+ bool ok = parseFile(&file, generate);
+ m_parsedTypesystemFiles[filepath] = ok;
+ return ok;
+}
+
+bool TypeDatabase::parseFile(QIODevice* device, bool generate)
+{
+ QXmlStreamReader reader(device);
+ TypeSystemParser handler(this, generate);
+ const bool result = handler.parse(reader);
+ if (!result)
+ qCWarning(lcShiboken, "%s", qPrintable(handler.errorString()));
+ return result;
+}
+
+PrimitiveTypeEntry *TypeDatabase::findPrimitiveType(const QString& name) const
+{
+ const auto entries = findTypeRange(name);
+ for (TypeEntry *entry : entries) {
+ if (entry->isPrimitive()) {
+ auto *pe = static_cast<PrimitiveTypeEntry *>(entry);
+ if (pe->preferredTargetLangType())
+ return pe;
+ }
+ }
+
+ return nullptr;
+}
+
+ComplexTypeEntry* TypeDatabase::findComplexType(const QString& name) const
+{
+ const auto entries = findTypeRange(name);
+ for (TypeEntry *entry : entries) {
+ if (entry->isComplex() && useType(entry))
+ return static_cast<ComplexTypeEntry*>(entry);
+ }
+ return nullptr;
+}
+
+ObjectTypeEntry* TypeDatabase::findObjectType(const QString& name) const
+{
+ const auto entries = findTypeRange(name);
+ for (TypeEntry *entry : entries) {
+ if (entry && entry->isObject() && useType(entry))
+ return static_cast<ObjectTypeEntry*>(entry);
+ }
+ return nullptr;
+}
+
+NamespaceTypeEntryList TypeDatabase::findNamespaceTypes(const QString& name) const
+{
+ NamespaceTypeEntryList result;
+ const auto entries = findTypeRange(name);
+ for (TypeEntry *entry : entries) {
+ if (entry->isNamespace())
+ result.append(static_cast<NamespaceTypeEntry*>(entry));
+ }
+ return result;
+}
+
+NamespaceTypeEntry *TypeDatabase::findNamespaceType(const QString& name,
+ const QString &fileName) const
+{
+ const auto entries = findNamespaceTypes(name);
+ // Preferably check on matching file name first, if a pattern was given.
+ if (!fileName.isEmpty()) {
+ for (NamespaceTypeEntry *entry : entries) {
+ if (entry->hasPattern() && entry->matchesFile(fileName))
+ return entry;
+ }
+ }
+ for (NamespaceTypeEntry *entry : entries) {
+ if (!entry->hasPattern())
+ return entry;
+ }
+ return nullptr;
+}
+
+bool TypeDatabase::shouldDropTypeEntry(const QString& fullTypeName) const
+{
+ return m_dropTypeEntries.contains(fullTypeName);
+}
+
+void TypeDatabase::setDropTypeEntries(QStringList dropTypeEntries)
+{
+ m_dropTypeEntries = dropTypeEntries;
+ m_dropTypeEntries.sort();
+}
+
+static bool computeTypeIndexes = true;
+static int maxTypeIndex;
+
+static bool typeEntryLessThan(const TypeEntry* t1, const TypeEntry* t2)
+{
+ if (t1->revision() < t2->revision())
+ return true;
+ return t1->revision() == t2->revision()
+ && t1->qualifiedCppName() < t2->qualifiedCppName();
+}
+
+static void _computeTypeIndexes()
+{
+ TypeDatabase* tdb = TypeDatabase::instance();
+
+ TypeEntryList list;
+
+ // Group type entries by revision numbers
+ const auto &allEntries = tdb->entries();
+ list.reserve(allEntries.size());
+ for (auto tit = allEntries.cbegin(), end = allEntries.cend(); tit != end; ++tit) {
+ TypeEntry *entry = tit.value();
+ if (entry->isPrimitive()
+ || entry->isContainer()
+ || entry->isFunction()
+ || !entry->generateCode()
+ || entry->isEnumValue()
+ || entry->isVarargs()
+ || entry->isTypeSystem()
+ || entry->isVoid()
+ || entry->isCustom())
+ continue;
+ if (!list.contains(entry)) // Remove duplicates
+ list.append(entry);
+ }
+
+ // Sort the type entries by revision, name
+ std::sort(list.begin(), list.end(), typeEntryLessThan);
+
+ maxTypeIndex = 0;
+ for (TypeEntry *e : qAsConst(list))
+ e->setSbkIndex(maxTypeIndex++);
+ computeTypeIndexes = false;
+}
+
+// Build the C++ name excluding any inline namespaces
+// ("std::__1::shared_ptr" -> "std::shared_ptr"
+QString TypeEntry::shortName() const
+{
+ if (m_cachedShortName.isEmpty()) {
+ QVarLengthArray<const TypeEntry *> parents;
+ bool foundInlineNamespace = false;
+ for (auto p = m_parent; p != nullptr && p->type() != TypeEntry::TypeSystemType; p = p->parent()) {
+ if (p->type() == TypeEntry::NamespaceType
+ && static_cast<const NamespaceTypeEntry *>(p)->isInlineNamespace()) {
+ foundInlineNamespace = true;
+ } else {
+ parents.append(p);
+ }
+ }
+ if (foundInlineNamespace) {
+ m_cachedShortName.reserve(m_name.size());
+ for (int i = parents.size() - 1; i >= 0; --i) {
+ m_cachedShortName.append(parents.at(i)->entryName());
+ m_cachedShortName.append(QLatin1String("::"));
+ }
+ m_cachedShortName.append(m_entryName);
+ } else {
+ m_cachedShortName = m_name;
+ }
+ }
+ return m_cachedShortName;
+}
+
+void TypeEntry::setRevision(int r)
+{
+ if (m_revision != r) {
+ m_revision = r;
+ computeTypeIndexes = true;
+ }
+}
+
+int TypeEntry::sbkIndex() const
+{
+ if (computeTypeIndexes)
+ _computeTypeIndexes();
+ return m_sbkIndex;
+}
+
+int getMaxTypeIndex()
+{
+ if (computeTypeIndexes)
+ _computeTypeIndexes();
+ return maxTypeIndex;
+}
+
+void TypeDatabase::clearApiVersions()
+{
+ apiVersions()->clear();
+}
+
+bool TypeDatabase::setApiVersion(const QString& packageWildcardPattern, const QString &version)
+{
+ const QString packagePattern = wildcardToRegExp(packageWildcardPattern.trimmed());
+ const QVersionNumber versionNumber = QVersionNumber::fromString(version);
+ if (versionNumber.isNull())
+ return false;
+ ApiVersions &versions = *apiVersions();
+ for (int i = 0, size = versions.size(); i < size; ++i) {
+ if (versions.at(i).first.pattern() == packagePattern) {
+ versions[i].second = versionNumber;
+ return true;
+ }
+ }
+ const QRegularExpression packageRegex(packagePattern);
+ if (!packageRegex.isValid())
+ return false;
+ versions.append(qMakePair(packageRegex, versionNumber));
+ return true;
+}
+
+bool TypeDatabase::checkApiVersion(const QString &package,
+ const VersionRange &vr)
+{
+ const ApiVersions &versions = *apiVersions();
+ if (versions.isEmpty()) // Nothing specified: use latest.
+ return true;
+ for (int i = 0, size = versions.size(); i < size; ++i) {
+ if (versions.at(i).first.match(package).hasMatch())
+ return versions.at(i).second >= vr.since
+ && versions.at(i).second <= vr.until;
+ }
+ return false;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+#define FORMAT_BOOL(name, var) \
+ if (var) \
+ d << ", [" << name << ']';
+
+#define FORMAT_NONEMPTY_STRING(name, var) \
+ if (!var.isEmpty()) \
+ d << ", " << name << "=\"" << var << '"';
+
+#define FORMAT_LIST_SIZE(name, var) \
+ if (!var.isEmpty()) \
+ d << ", " << var.size() << ' ' << name;
+
+template <class Container, class Separator>
+static void formatList(QDebug &d, const char *name, const Container &c, Separator sep)
+{
+ if (const int size = c.size()) {
+ d << ", " << name << '[' << size << "]=(";
+ for (int i = 0; i < size; ++i) {
+ if (i)
+ d << sep;
+ d << c.at(i);
+ }
+ d << ')';
+ }
+}
+
+void TypeEntry::formatDebug(QDebug &d) const
+{
+ const QString cppName = qualifiedCppName();
+ d << '"' << m_name << '"';
+ if (m_name != cppName)
+ d << "\", cppName=\"" << cppName << '"';
+ d << ", type=" << m_type << ", codeGeneration="
+ << m_codeGeneration << ", target=\"" << targetLangName() << '"';
+ FORMAT_NONEMPTY_STRING("package", m_targetLangPackage)
+ FORMAT_BOOL("stream", m_stream)
+ FORMAT_LIST_SIZE("codeSnips", m_codeSnips)
+ FORMAT_NONEMPTY_STRING("conversionRule", m_conversionRule)
+ if (m_viewOn)
+ d << ", views=" << m_viewOn->name();
+ if (!m_version.isNull() && m_version > QVersionNumber(0, 0))
+ d << ", version=" << m_version;
+ if (m_revision)
+ d << ", revision=" << m_revision;
+ if (m_sbkIndex)
+ d << ", sbkIndex=" << m_sbkIndex;
+ if (m_include.isValid())
+ d << ", include=" << m_include;
+ formatList(d, "extraIncludes", m_extraIncludes, ", ");
+}
+
+void ComplexTypeEntry::formatDebug(QDebug &d) const
+{
+ TypeEntry::formatDebug(d);
+ FORMAT_BOOL("polymorphicBase", m_polymorphicBase)
+ FORMAT_BOOL("genericClass", m_genericClass)
+ FORMAT_BOOL("deleteInMainThread", m_deleteInMainThread)
+ if (m_typeFlags != 0)
+ d << ", typeFlags=" << m_typeFlags;
+ d << ", copyableFlag=" << m_copyableFlag
+ << ", except=" << int(m_exceptionHandling);
+ FORMAT_NONEMPTY_STRING("defaultSuperclass", m_defaultSuperclass)
+ FORMAT_NONEMPTY_STRING("polymorphicIdValue", m_polymorphicIdValue)
+ FORMAT_NONEMPTY_STRING("targetType", m_targetType)
+ FORMAT_NONEMPTY_STRING("hash", m_hashFunction)
+ FORMAT_LIST_SIZE("addedFunctions", m_addedFunctions)
+ formatList(d, "functionMods", m_functionMods, ", ");
+ FORMAT_LIST_SIZE("fieldMods", m_fieldMods)
+}
+
+void TypedefEntry::formatDebug(QDebug &d) const
+{
+ ComplexTypeEntry::formatDebug(d);
+ d << ", sourceType=\"" << m_sourceType << '"'
+ << ", source=" << m_source << ", target=" << m_target;
+}
+
+void EnumTypeEntry::formatDebug(QDebug &d) const
+{
+ TypeEntry::formatDebug(d);
+ if (m_flags)
+ d << ", flags=(" << m_flags << ')';
+}
+
+void NamespaceTypeEntry::formatDebug(QDebug &d) const
+{
+ ComplexTypeEntry::formatDebug(d);
+ auto pattern = m_filePattern.pattern();
+ FORMAT_NONEMPTY_STRING("pattern", pattern)
+ d << ",visibility=" << m_visibility;
+ if (m_inlineNamespace)
+ d << "[inline]";
+}
+
+void ContainerTypeEntry::formatDebug(QDebug &d) const
+{
+ ComplexTypeEntry::formatDebug(d);
+ d << ", type=" << m_containerKind << ",\"" << typeName() << '"';
+}
+
+void SmartPointerTypeEntry::formatDebug(QDebug &d) const
+{
+ ComplexTypeEntry::formatDebug(d);
+ if (!m_instantiations.isEmpty()) {
+ d << ", instantiations[" << m_instantiations.size() << "]=(";
+ for (auto i : m_instantiations)
+ d << i->name() << ',';
+ d << ')';
+ }
+}
+
+QDebug operator<<(QDebug d, const TypeEntry *te)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "TypeEntry(";
+ if (te)
+ te->formatDebug(d);
+ else
+ d << '0';
+ d << ')';
+ return d;
+}
+
+QDebug operator<<(QDebug d, const TemplateEntry *te)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "TemplateEntry(";
+ if (te) {
+ d << '"' << te->name() << '"';
+ } else {
+ d << '0';
+ }
+ d << ')';
+ return d;
+}
+
+void TypeDatabase::formatDebug(QDebug &d) const
+{
+ d << "TypeDatabase("
+ << "entries[" << m_entries.size() << "]=";
+ for (auto it = m_entries.cbegin(), end = m_entries.cend(); it != end; ++it)
+ d << " " << it.value() << '\n';
+ if (!m_typedefEntries.isEmpty()) {
+ d << "typedefs[" << m_typedefEntries.size() << "]=(";
+ const auto begin = m_typedefEntries.cbegin();
+ for (auto it = begin, end = m_typedefEntries.cend(); it != end; ++it) {
+ if (it != begin)
+ d << ", ";
+ d << " " << it.value() << '\n';
+ }
+ d << ")\n";
+ }
+ if (!m_templates.isEmpty()) {
+ d << "templates[" << m_templates.size() << "]=(";
+ const auto begin = m_templates.cbegin();
+ for (auto it = begin, end = m_templates.cend(); it != end; ++it) {
+ if (it != begin)
+ d << ", ";
+ d << it.value();
+ }
+ d << ")\n";
+ }
+ if (!m_flagsEntries.isEmpty()) {
+ d << "flags[" << m_flagsEntries.size() << "]=(";
+ const auto begin = m_flagsEntries.cbegin();
+ for (auto it = begin, end = m_flagsEntries.cend(); it != end; ++it) {
+ if (it != begin)
+ d << ", ";
+ d << it.value();
+ }
+ d << ")\n";
+ }
+ d <<"\nglobalUserFunctions=" << m_globalUserFunctions << '\n';
+ formatList(d, "globalFunctionMods", m_functionMods, '\n');
+ d << ')';
+}
+
+QDebug operator<<(QDebug d, const TypeDatabase &db)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ db.formatDebug(d);
+ return d;
+}
+#endif // !QT_NO_DEBUG_STREAM