diff options
Diffstat (limited to 'sources/shiboken6/ApiExtractor/abstractmetalang.cpp')
-rw-r--r-- | sources/shiboken6/ApiExtractor/abstractmetalang.cpp | 2251 |
1 files changed, 2251 insertions, 0 deletions
diff --git a/sources/shiboken6/ApiExtractor/abstractmetalang.cpp b/sources/shiboken6/ApiExtractor/abstractmetalang.cpp new file mode 100644 index 000000000..d88775356 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/abstractmetalang.cpp @@ -0,0 +1,2251 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractmetalang.h" +#include "messages.h" +#include "propertyspec.h" +#include "reporthandler.h" +#include "typedatabase.h" +#include "typesystem.h" + +#include <parser/codemodel.h> + +#ifndef QT_NO_DEBUG_STREAM +# include <QtCore/QMetaEnum> +# include <QtCore/QMetaObject> +#endif + +#include <QtCore/QRegularExpression> +#include <QtCore/QSharedData> +#include <QtCore/QStack> + +#include <algorithm> + +#include <algorithm> + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const AbstractMetaAttributes *aa) +{ + QDebugStateSaver saver(d); + d.noquote(); + d.nospace(); + d << "AbstractMetaAttributes("; + if (aa) + d << aa->attributes(); + else + d << '0'; + d << ')'; + return d; +} +#endif // !QT_NO_DEBUG_STREAM + +template <class MetaClass> +MetaClass *findByName(QVector<MetaClass *> haystack, QStringView needle) +{ + for (MetaClass *c : haystack) { + if (c->name() == needle) + return c; + } + return nullptr; +} + +// Helper for recursing the base classes of an AbstractMetaClass. +// Returns the class for which the predicate is true. +template <class Predicate> +const AbstractMetaClass *recurseClassHierarchy(const AbstractMetaClass *klass, + Predicate pred) +{ + if (pred(klass)) + return klass; + for (auto base : klass->baseClasses()) { + if (auto r = recurseClassHierarchy(base, pred)) + return r; + } + return nullptr; +} + +/******************************************************************************* + * AbstractMetaVariable + */ + +AbstractMetaVariable::AbstractMetaVariable() = default; + +AbstractMetaVariable::~AbstractMetaVariable() = default; + +void AbstractMetaVariable::assignMetaVariable(const AbstractMetaVariable &other) +{ + m_originalName = other.m_originalName; + m_name = other.m_name; + m_type = other.m_type; + m_hasName = other.m_hasName; + m_doc = other.m_doc; +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const AbstractMetaVariable *av) +{ + QDebugStateSaver saver(d); + d.noquote(); + d.nospace(); + d << "AbstractMetaVariable("; + if (av) { + d << av->type().name() << ' ' << av->name(); + } else { + d << '0'; + } + d << ')'; + return d; +} +#endif // !QT_NO_DEBUG_STREAM + +/******************************************************************************* + * AbstractMetaAttributes + */ + +AbstractMetaAttributes::AbstractMetaAttributes() = default; +AbstractMetaAttributes::~AbstractMetaAttributes() = default; + +void AbstractMetaAttributes::assignMetaAttributes(const AbstractMetaAttributes &other) +{ + m_attributes = other.m_attributes; + m_originalAttributes = other.m_originalAttributes; + m_doc = other.m_doc; +} + +/******************************************************************************* + * AbstractMetaFunction + */ + +AbstractMetaFunction::AbstractMetaFunction(const AddedFunctionPtr &addedFunc) : + AbstractMetaFunction() +{ + m_addedFunction = addedFunc; + setConstant(addedFunc->isConstant()); + setName(addedFunc->name()); + setOriginalName(addedFunc->name()); + auto atts = attributes() | AbstractMetaAttributes::FinalInTargetLang; + switch (addedFunc->access()) { + case AddedFunction::InvalidAccess: + break; + case AddedFunction::Protected: + atts |= AbstractMetaAttributes::Protected; + break; + case AddedFunction::Public: + atts |= AbstractMetaAttributes::Public; + break; + } + if (addedFunc->isStatic()) + atts |= AbstractMetaFunction::Static; + setAttributes(atts); +} + +AbstractMetaFunction::AbstractMetaFunction() + : m_constant(false), + m_reverse(false), + m_explicit(false), + m_pointerOperator(false), + m_isCallOperator(false) +{ +} + +AbstractMetaFunction::~AbstractMetaFunction() = default; + +/******************************************************************************* + * Indicates that this function has a modification that removes it + */ +bool AbstractMetaFunction::isModifiedRemoved(int types) const +{ + const FunctionModificationList &mods = modifications(implementingClass()); + for (const FunctionModification &mod : mods) { + if (!mod.isRemoveModifier()) + continue; + + if ((mod.removal & types) == types) + return true; + } + + return false; +} + +bool AbstractMetaFunction::operator<(const AbstractMetaFunction &other) const +{ + return compareTo(&other) & NameLessThan; +} + + +/*! + Returns a mask of CompareResult describing how this function is + compares to another function +*/ +AbstractMetaFunction::CompareResult AbstractMetaFunction::compareTo(const AbstractMetaFunction *other) const +{ + CompareResult result; + + // Enclosing class... + if (ownerClass() == other->ownerClass()) + result |= EqualImplementor; + + // Attributes + if (attributes() == other->attributes()) + result |= EqualAttributes; + + // Compare types + if (type().name() == other->type().name()) + result |= EqualReturnType; + + // Compare names + int cmp = originalName().compare(other->originalName()); + + if (cmp < 0) + result |= NameLessThan; + else if (!cmp) + result |= EqualName; + + // compare name after modification... + cmp = modifiedName().compare(other->modifiedName()); + if (!cmp) + result |= EqualModifiedName; + + // Compare arguments... + AbstractMetaArgumentList minArguments; + AbstractMetaArgumentList maxArguments; + if (arguments().size() < other->arguments().size()) { + minArguments = arguments(); + maxArguments = other->arguments(); + } else { + minArguments = other->arguments(); + maxArguments = arguments(); + } + + int minCount = minArguments.size(); + int maxCount = maxArguments.size(); + bool same = true; + for (int i = 0; i < maxCount; ++i) { + if (i < minCount) { + const AbstractMetaArgument &min_arg = minArguments.at(i); + const AbstractMetaArgument &max_arg = maxArguments.at(i); + if (min_arg.type().name() != max_arg.type().name() + && (min_arg.defaultValueExpression().isEmpty() || max_arg.defaultValueExpression().isEmpty())) { + same = false; + break; + } + } else { + if (maxArguments.at(i).defaultValueExpression().isEmpty()) { + same = false; + break; + } + } + } + + if (same) + result |= minCount == maxCount ? EqualArguments : EqualDefaultValueOverload; + + return result; +} + +AbstractMetaFunction *AbstractMetaFunction::copy() const +{ + auto *cpy = new AbstractMetaFunction; + cpy->assignMetaAttributes(*this); + cpy->setName(name()); + cpy->setOriginalName(originalName()); + cpy->setOwnerClass(ownerClass()); + cpy->setImplementingClass(implementingClass()); + cpy->setFunctionType(functionType()); + cpy->setDeclaringClass(declaringClass()); + cpy->setType(type()); + cpy->setConstant(isConstant()); + cpy->setExceptionSpecification(m_exceptionSpecification); + cpy->setAllowThreadModification(m_allowThreadModification); + cpy->setExceptionHandlingModification(m_exceptionHandlingModification); + cpy->m_addedFunction = m_addedFunction; + cpy->m_arguments = m_arguments; + + return cpy; +} + +bool AbstractMetaFunction::usesRValueReferences() const +{ + if (m_functionType == MoveConstructorFunction || m_functionType == MoveAssignmentOperatorFunction) + return true; + if (m_type.referenceType() == RValueReference) + return true; + for (const AbstractMetaArgument &a : m_arguments) { + if (a.type().referenceType() == RValueReference) + return true; + } + return false; +} + +QStringList AbstractMetaFunction::introspectionCompatibleSignatures(const QStringList &resolvedArguments) const +{ + AbstractMetaArgumentList arguments = this->arguments(); + if (arguments.size() == resolvedArguments.size()) { + QString signature = name() + QLatin1Char('(') + resolvedArguments.join(QLatin1Char(',')) + QLatin1Char(')'); + return QStringList(TypeDatabase::normalizedSignature(signature)); + } + QStringList returned; + + const AbstractMetaArgument &argument = arguments.at(resolvedArguments.size()); + QStringList minimalTypeSignature = argument.type().minimalSignature().split(QLatin1String("::")); + for (int i = 0; i < minimalTypeSignature.size(); ++i) { + returned += introspectionCompatibleSignatures(QStringList(resolvedArguments) + << QStringList(minimalTypeSignature.mid(minimalTypeSignature.size() - i - 1)).join(QLatin1String("::"))); + } + + return returned; +} + +QString AbstractMetaFunction::signature() const +{ + if (m_cachedSignature.isEmpty()) { + m_cachedSignature = m_originalName; + + m_cachedSignature += QLatin1Char('('); + + for (int i = 0; i < m_arguments.count(); ++i) { + const AbstractMetaArgument &a = m_arguments.at(i); + const AbstractMetaType &t = a.type(); + if (!t.isVoid()) { + if (i > 0) + m_cachedSignature += QLatin1String(", "); + m_cachedSignature += t.cppSignature(); + // We need to have the argument names in the qdoc files + m_cachedSignature += QLatin1Char(' '); + m_cachedSignature += a.name(); + } else { + qCWarning(lcShiboken).noquote().nospace() + << QString::fromLatin1("No abstract meta type found for argument '%1' while" + "constructing signature for function '%2'.") + .arg(a.name(), name()); + } + } + m_cachedSignature += QLatin1Char(')'); + + if (isConstant()) + m_cachedSignature += QLatin1String(" const"); + } + return m_cachedSignature; +} + +int AbstractMetaFunction::actualMinimumArgumentCount() const +{ + AbstractMetaArgumentList arguments = this->arguments(); + + int count = 0; + for (int i = 0; i < arguments.size(); ++i && ++count) { + if (argumentRemoved(i + 1)) + --count; + else if (!arguments.at(i).defaultValueExpression().isEmpty()) + break; + } + + return count; +} + +// Returns reference counts for argument at idx, or all arguments if idx == -2 +QVector<ReferenceCount> AbstractMetaFunction::referenceCounts(const AbstractMetaClass *cls, int idx) const +{ + QVector<ReferenceCount> returned; + + const FunctionModificationList &mods = this->modifications(cls); + for (const FunctionModification &mod : mods) { + for (const ArgumentModification &argumentMod : mod.argument_mods) { + if (argumentMod.index != idx && idx != -2) + continue; + returned += argumentMod.referenceCounts; + } + } + + return returned; +} + + +ArgumentOwner AbstractMetaFunction::argumentOwner(const AbstractMetaClass *cls, int idx) const +{ + const FunctionModificationList &mods = this->modifications(cls); + for (const FunctionModification &mod : mods) { + for (const ArgumentModification &argumentMod : mod.argument_mods) { + if (argumentMod.index != idx) + continue; + return argumentMod.owner; + } + } + return ArgumentOwner(); +} + +QString AbstractMetaFunction::conversionRule(TypeSystem::Language language, int key) const +{ + const FunctionModificationList &modifications = this->modifications(declaringClass()); + for (const FunctionModification &modification : modifications) { + for (const ArgumentModification &argumentModification : modification.argument_mods) { + if (argumentModification.index != key) + continue; + + for (const CodeSnip &snip : argumentModification.conversion_rules) { + if (snip.language == language && !snip.code().isEmpty()) + return snip.code(); + } + } + } + + return QString(); +} + +// FIXME If we remove a arg. in the method at the base class, it will not reflect here. +bool AbstractMetaFunction::argumentRemoved(int key) const +{ + const FunctionModificationList &modifications = this->modifications(declaringClass()); + for (const FunctionModification &modification : modifications) { + for (const ArgumentModification &argumentModification : modification.argument_mods) { + if (argumentModification.index == key) { + if (argumentModification.removed) + return true; + } + } + } + + return false; +} + +const AbstractMetaClass *AbstractMetaFunction::targetLangOwner() const +{ + return m_class && m_class->isInvisibleNamespace() + ? m_class->targetLangEnclosingClass() : m_class; +} + +bool AbstractMetaFunction::isDeprecated() const +{ + const FunctionModificationList &modifications = this->modifications(declaringClass()); + for (const FunctionModification &modification : modifications) { + if (modification.isDeprecated()) + return true; + } + return false; +} + +// Auto-detect whether a function should be wrapped into +// Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS, that is, temporarily release +// the GIL (global interpreter lock). Doing so is required for any thread-wait +// functions, anything that might call a virtual function (potentially +// reimplemented in Python), and recommended for lengthy I/O or similar. +// It has performance costs, though. +bool AbstractMetaFunction::autoDetectAllowThread() const +{ + // Disallow for simple getter functions. + const bool maybeGetter = m_constant != 0 && !isVoid() && m_arguments.isEmpty(); + return !maybeGetter; +} + +SourceLocation AbstractMetaFunction::sourceLocation() const +{ + return m_sourceLocation; +} + +void AbstractMetaFunction::setSourceLocation(const SourceLocation &sourceLocation) +{ + m_sourceLocation = sourceLocation; +} + +static inline TypeSystem::AllowThread allowThreadMod(const AbstractMetaClass *klass) +{ + return klass->typeEntry()->allowThread(); +} + +static inline bool hasAllowThreadMod(const AbstractMetaClass *klass) +{ + return allowThreadMod(klass) != TypeSystem::AllowThread::Unspecified; +} + +bool AbstractMetaFunction::allowThread() const +{ + auto allowThreadModification = m_allowThreadModification; + // If there is no modification on the function, check for a base class. + if (m_class && allowThreadModification == TypeSystem::AllowThread::Unspecified) { + if (auto base = recurseClassHierarchy(m_class, hasAllowThreadMod)) + allowThreadModification = allowThreadMod(base); + } + + bool result = true; + switch (allowThreadModification) { + case TypeSystem::AllowThread::Disallow: + result = false; + break; + case TypeSystem::AllowThread::Allow: + break; + case TypeSystem::AllowThread::Auto: + result = autoDetectAllowThread(); + break; + case TypeSystem::AllowThread::Unspecified: + result = false; + break; + } + if (!result && ReportHandler::isDebug(ReportHandler::MediumDebug)) + qCInfo(lcShiboken).noquote() << msgDisallowThread(this); + return result; +} + +TypeSystem::Ownership AbstractMetaFunction::ownership(const AbstractMetaClass *cls, TypeSystem::Language language, int key) const +{ + const FunctionModificationList &modifications = this->modifications(cls); + for (const FunctionModification &modification : modifications) { + for (const ArgumentModification &argumentModification : modification.argument_mods) { + if (argumentModification.index == key) + return argumentModification.ownerships.value(language, TypeSystem::InvalidOwnership); + } + } + + return TypeSystem::InvalidOwnership; +} + +bool AbstractMetaFunction::isRemovedFromAllLanguages(const AbstractMetaClass *cls) const +{ + return isRemovedFrom(cls, TypeSystem::All); +} + +bool AbstractMetaFunction::isRemovedFrom(const AbstractMetaClass *cls, TypeSystem::Language language) const +{ + const FunctionModificationList &modifications = this->modifications(cls); + for (const FunctionModification &modification : modifications) { + if ((modification.removal & language) == language) + return true; + } + + return false; + +} + +QString AbstractMetaFunction::typeReplaced(int key) const +{ + const FunctionModificationList &modifications = this->modifications(declaringClass()); + for (const FunctionModification &modification : modifications) { + for (const ArgumentModification &argumentModification : modification.argument_mods) { + if (argumentModification.index == key + && !argumentModification.modified_type.isEmpty()) { + return argumentModification.modified_type; + } + } + } + + return QString(); +} + +bool AbstractMetaFunction::isModifiedToArray(int argumentIndex) const +{ + const FunctionModificationList &modifications = this->modifications(declaringClass()); + for (const FunctionModification &modification : modifications) { + for (const ArgumentModification &argumentModification : modification.argument_mods) { + if (argumentModification.index == argumentIndex && argumentModification.array != 0) + return true; + } + } + return false; +} + +QString AbstractMetaFunction::minimalSignature() const +{ + if (!m_cachedMinimalSignature.isEmpty()) + return m_cachedMinimalSignature; + + QString minimalSignature = originalName() + QLatin1Char('('); + AbstractMetaArgumentList arguments = this->arguments(); + + for (int i = 0; i < arguments.count(); ++i) { + const AbstractMetaType &t = arguments.at(i).type(); + if (!t.isVoid()) { + if (i > 0) + minimalSignature += QLatin1Char(','); + minimalSignature += t.minimalSignature(); + } else { + qCWarning(lcShiboken).noquote().nospace() + << QString::fromLatin1("No abstract meta type found for argument '%1' while constructing" + " minimal signature for function '%2'.") + .arg(arguments.at(i).name(), name()); + } + } + minimalSignature += QLatin1Char(')'); + if (isConstant()) + minimalSignature += QLatin1String("const"); + + minimalSignature = TypeDatabase::normalizedSignature(minimalSignature); + m_cachedMinimalSignature = minimalSignature; + + return minimalSignature; +} + +QString AbstractMetaFunction::debugSignature() const +{ + QString result; + const bool isOverride = attributes() & AbstractMetaFunction::OverriddenCppMethod; + const bool isFinal = attributes() & AbstractMetaFunction::FinalCppMethod; + if (!isOverride && !isFinal && (attributes() & AbstractMetaFunction::VirtualCppMethod)) + result += QLatin1String("virtual "); + result += minimalSignature(); + if (isOverride) + result += QLatin1String(" override"); + if (isFinal) + result += QLatin1String(" final"); + return result; +} + +FunctionModificationList AbstractMetaFunction::modifications(const AbstractMetaClass *implementor) const +{ + if (!m_addedFunction.isNull()) + return m_addedFunction->modifications; + if (!implementor) + implementor = ownerClass(); + + if (!implementor) + return TypeDatabase::instance()->functionModifications(minimalSignature()); + + FunctionModificationList mods; + while (implementor) { + mods += implementor->typeEntry()->functionModifications(minimalSignature()); + if ((implementor == implementor->baseClass()) || + (implementor == implementingClass() && !mods.isEmpty())) { + break; + } + implementor = implementor->baseClass(); + } + return mods; +} + +QString AbstractMetaFunction::argumentName(int index, + bool /* create */, + const AbstractMetaClass * /* implementor */) const +{ + return m_arguments[--index].name(); +} + +bool AbstractMetaFunction::isCallOperator() const +{ + return m_name == QLatin1String("operator()"); +} + +bool AbstractMetaFunction::hasInjectedCode() const +{ + const FunctionModificationList &mods = modifications(ownerClass()); + for (const FunctionModification &mod : mods) { + if (mod.isCodeInjection()) + return true; + } + return false; +} + +CodeSnipList AbstractMetaFunction::injectedCodeSnips(TypeSystem::CodeSnipPosition position, TypeSystem::Language language) const +{ + CodeSnipList result; + const FunctionModificationList &mods = modifications(ownerClass()); + for (const FunctionModification &mod : mods) { + if (mod.isCodeInjection()) { + for (const CodeSnip &snip : mod.snips) { + if ((snip.language & language) && (snip.position == position || position == TypeSystem::CodeSnipPositionAny)) + result << snip; + } + } + } + return result; +} + +bool AbstractMetaFunction::hasSignatureModifications() const +{ + const FunctionModificationList &mods = modifications(); + for (const FunctionModification &mod : mods) { + if (mod.isRenameModifier()) + return true; + for (const ArgumentModification &argmod : mod.argument_mods) { + // since zero represents the return type and we're + // interested only in checking the function arguments, + // it will be ignored. + if (argmod.index > 0) + return true; + } + } + return false; +} + +bool AbstractMetaFunction::isConversionOperator(const QString &funcName) +{ + static const QRegularExpression opRegEx(QStringLiteral("^operator(?:\\s+(?:const|volatile))?\\s+(\\w+\\s*)&?$")); + Q_ASSERT(opRegEx.isValid()); + return opRegEx.match(funcName).hasMatch(); +} + +ExceptionSpecification AbstractMetaFunction::exceptionSpecification() const +{ + return m_exceptionSpecification; +} + +void AbstractMetaFunction::setExceptionSpecification(ExceptionSpecification e) +{ + m_exceptionSpecification = e; +} + +static inline TypeSystem::ExceptionHandling exceptionMod(const AbstractMetaClass *klass) +{ + return klass->typeEntry()->exceptionHandling(); +} + +static inline bool hasExceptionMod(const AbstractMetaClass *klass) +{ + return exceptionMod(klass) != TypeSystem::ExceptionHandling::Unspecified; +} + +bool AbstractMetaFunction::generateExceptionHandling() const +{ + switch (m_functionType) { + case AbstractMetaFunction::CopyConstructorFunction: + case AbstractMetaFunction::MoveConstructorFunction: + case AbstractMetaFunction::AssignmentOperatorFunction: + case AbstractMetaFunction::MoveAssignmentOperatorFunction: + case AbstractMetaFunction::DestructorFunction: + return false; + default: + break; + } + + auto exceptionHandlingModification = m_exceptionHandlingModification; + // If there is no modification on the function, check for a base class. + if (m_class && exceptionHandlingModification == TypeSystem::ExceptionHandling::Unspecified) { + if (auto base = recurseClassHierarchy(m_class, hasExceptionMod)) + exceptionHandlingModification = exceptionMod(base); + } + + bool result = false; + switch (exceptionHandlingModification) { + case TypeSystem::ExceptionHandling::On: + result = true; + break; + case TypeSystem::ExceptionHandling::AutoDefaultToOn: + result = m_exceptionSpecification != ExceptionSpecification::NoExcept; + break; + case TypeSystem::ExceptionHandling::AutoDefaultToOff: + result = m_exceptionSpecification == ExceptionSpecification::Throws; + break; + case TypeSystem::ExceptionHandling::Unspecified: + case TypeSystem::ExceptionHandling::Off: + break; + } + return result; +} + +bool AbstractMetaFunction::isOperatorOverload(const QString &funcName) +{ + if (isConversionOperator(funcName)) + return true; + + static const QRegularExpression opRegEx(QLatin1String("^operator([+\\-\\*/%=&\\|\\^\\<>!][=]?" + "|\\+\\+|\\-\\-|&&|\\|\\||<<[=]?|>>[=]?|~" + "|\\[\\]|\\s+delete\\[?\\]?" + "|\\(\\)" + "|\\s+new\\[?\\]?)$")); + Q_ASSERT(opRegEx.isValid()); + return opRegEx.match(funcName).hasMatch(); +} + +bool AbstractMetaFunction::isCastOperator() const +{ + return originalName().startsWith(QLatin1String("operator ")); +} + +bool AbstractMetaFunction::isArithmeticOperator() const +{ + if (!isOperatorOverload()) + return false; + + QString name = originalName(); + + // It's a dereference operator! + if (name == QLatin1String("operator*") && m_arguments.isEmpty()) + return false; + + return name == QLatin1String("operator+") || name == QLatin1String("operator+=") + || name == QLatin1String("operator-") || name == QLatin1String("operator-=") + || name == QLatin1String("operator*") || name == QLatin1String("operator*=") + || name == QLatin1String("operator/") || name == QLatin1String("operator/=") + || name == QLatin1String("operator%") || name == QLatin1String("operator%=") + || name == QLatin1String("operator++") || name == QLatin1String("operator--"); +} + +bool AbstractMetaFunction::isBitwiseOperator() const +{ + if (!isOperatorOverload()) + return false; + + QString name = originalName(); + return name == QLatin1String("operator<<") || name == QLatin1String("operator<<=") + || name == QLatin1String("operator>>") || name == QLatin1String("operator>>=") + || name == QLatin1String("operator&") || name == QLatin1String("operator&=") + || name == QLatin1String("operator|") || name == QLatin1String("operator|=") + || name == QLatin1String("operator^") || name == QLatin1String("operator^=") + || name == QLatin1String("operator~"); +} + +bool AbstractMetaFunction::isComparisonOperator() const +{ + if (!isOperatorOverload()) + return false; + + QString name = originalName(); + return name == QLatin1String("operator<") || name == QLatin1String("operator<=") + || name == QLatin1String("operator>") || name == QLatin1String("operator>=") + || name == QLatin1String("operator==") || name == QLatin1String("operator!="); +} + +bool AbstractMetaFunction::isLogicalOperator() const +{ + if (!isOperatorOverload()) + return false; + + QString name = originalName(); + return name == QLatin1String("operator!") + || name == QLatin1String("operator&&") + || name == QLatin1String("operator||"); +} + +bool AbstractMetaFunction::isSubscriptOperator() const +{ + if (!isOperatorOverload()) + return false; + + return originalName() == QLatin1String("operator[]"); +} + +bool AbstractMetaFunction::isAssignmentOperator() const +{ + return m_functionType == AssignmentOperatorFunction + || m_functionType == MoveAssignmentOperatorFunction; +} + +bool AbstractMetaFunction::isOtherOperator() const +{ + if (!isOperatorOverload()) + return false; + + return !isArithmeticOperator() + && !isBitwiseOperator() + && !isComparisonOperator() + && !isLogicalOperator() + && !isConversionOperator() + && !isSubscriptOperator() + && !isAssignmentOperator(); +} + +int AbstractMetaFunction::arityOfOperator() const +{ + if (!isOperatorOverload() || isCallOperator()) + return -1; + + int arity = m_arguments.size(); + + // Operator overloads that are class members + // implicitly includes the instance and have + // one parameter less than their arity, + // so we increment it. + if (ownerClass() && arity < 2) + arity++; + + return arity; +} + +bool AbstractMetaFunction::isInplaceOperator() const +{ + if (!isOperatorOverload()) + return false; + + QString name = originalName(); + return name == QLatin1String("operator+=") || name == QLatin1String("operator&=") + || name == QLatin1String("operator-=") || name == QLatin1String("operator|=") + || name == QLatin1String("operator*=") || name == QLatin1String("operator^=") + || name == QLatin1String("operator/=") || name == QLatin1String("operator<<=") + || name == QLatin1String("operator%=") || name == QLatin1String("operator>>="); +} + +bool AbstractMetaFunction::isVirtual() const +{ + return attributes() & AbstractMetaAttributes::VirtualCppMethod; +} + +QString AbstractMetaFunction::modifiedName() const +{ + if (m_cachedModifiedName.isEmpty()) { + const FunctionModificationList &mods = modifications(implementingClass()); + for (const FunctionModification &mod : mods) { + if (mod.isRenameModifier()) { + m_cachedModifiedName = mod.renamedToName; + break; + } + } + if (m_cachedModifiedName.isEmpty()) + m_cachedModifiedName = name(); + } + return m_cachedModifiedName; +} + +bool function_sorter(AbstractMetaFunction *a, AbstractMetaFunction *b) +{ + return a->signature() < b->signature(); +} + +AbstractMetaFunction * +AbstractMetaFunction::find(const AbstractMetaFunctionList &haystack, + const QString &needle) +{ + return findByName(haystack, needle); +} + +int AbstractMetaFunction::overloadNumber() const +{ + if (m_cachedOverloadNumber == TypeSystem::OverloadNumberUnset) { + m_cachedOverloadNumber = TypeSystem::OverloadNumberDefault; + const FunctionModificationList &mods = modifications(implementingClass()); + for (const FunctionModification &mod : mods) { + if (mod.overloadNumber() != TypeSystem::OverloadNumberUnset) { + m_cachedOverloadNumber = mod.overloadNumber(); + break; + } + } + } + return m_cachedOverloadNumber; +} + +#ifndef QT_NO_DEBUG_STREAM +static inline void formatMetaFunctionBrief(QDebug &d, const AbstractMetaFunction *af) +{ + d << '"' << af->debugSignature() << '"'; +} + +void AbstractMetaFunction::formatDebugVerbose(QDebug &d) const +{ + d << m_functionType << ' ' << m_type << ' ' << m_name; + switch (m_exceptionSpecification) { + case ExceptionSpecification::Unknown: + break; + case ExceptionSpecification::NoExcept: + d << " noexcept"; + break; + case ExceptionSpecification::Throws: + d << " throw(...)"; + break; + } + if (m_exceptionHandlingModification != TypeSystem::ExceptionHandling::Unspecified) + d << " exeption-mod " << int(m_exceptionHandlingModification); + d << '('; + for (int i = 0, count = m_arguments.size(); i < count; ++i) { + if (i) + d << ", "; + d << m_arguments.at(i); + } + d << "), signature=\"" << minimalSignature() << '"'; + if (m_constant) + d << " [const]"; + if (m_reverse) + d << " [reverse]"; + if (isUserAdded()) + d << " [userAdded]"; + if (m_explicit) + d << " [explicit]"; + if (attributes().testFlag(AbstractMetaAttributes::Deprecated)) + d << " [deprecated]"; + if (m_pointerOperator) + d << " [operator->]"; + if (m_isCallOperator) + d << " [operator()]"; + if (m_class) + d << " class: " << m_class->name(); + if (m_implementingClass) + d << " implementing class: " << m_implementingClass->name(); + if (m_declaringClass) + d << " declaring class: " << m_declaringClass->name(); +} + +QDebug operator<<(QDebug d, const AbstractMetaFunction *af) +{ + QDebugStateSaver saver(d); + d.noquote(); + d.nospace(); + d << "AbstractMetaFunction("; + if (af) { + if (d.verbosity() > 2) { + af->formatDebugVerbose(d); + } else { + d << "signature="; + formatMetaFunctionBrief(d, af); + } + } else { + d << '0'; + } + d << ')'; + return d; +} +#endif // !QT_NO_DEBUG_STREAM + +/******************************************************************************* + * AbstractMetaClass + */ + +AbstractMetaClass::AbstractMetaClass() + : m_hasVirtuals(false), + m_isPolymorphic(false), + m_hasNonpublic(false), + m_hasNonPrivateConstructor(false), + m_hasPrivateConstructor(false), + m_functionsFixed(false), + m_hasPrivateDestructor(false), + m_hasProtectedDestructor(false), + m_hasVirtualDestructor(false), + m_hasHashFunction(false), + m_hasEqualsOperator(false), + m_hasCloneOperator(false), + m_isTypeDef(false), + m_hasToStringCapability(false) +{ +} + +AbstractMetaClass::~AbstractMetaClass() +{ + qDeleteAll(m_functions); + qDeleteAll(m_fields); + qDeleteAll(m_enums); + qDeleteAll(m_propertySpecs); +} + +/******************************************************************************* + * Returns true if this class is a subclass of the given class + */ +bool AbstractMetaClass::inheritsFrom(const AbstractMetaClass *cls) const +{ + Q_ASSERT(cls); + + const AbstractMetaClass *clazz = this; + while (clazz) { + if (clazz == cls) + return true; + + clazz = clazz->baseClass(); + } + + return false; +} + +/******************************************************************************* + * Returns a list of all the functions with a given name + */ +AbstractMetaFunctionList AbstractMetaClass::queryFunctionsByName(const QString &name) const +{ + AbstractMetaFunctionList returned; + for (AbstractMetaFunction *function : m_functions) { + if (function->name() == name) + returned.append(function); + } + + return returned; +} + +/******************************************************************************* + * Returns a list of all the functions retrieved during parsing which should + * be added to the API. + */ +AbstractMetaFunctionList AbstractMetaClass::functionsInTargetLang() const +{ + FunctionQueryOptions default_flags = NormalFunctions | Visible | NotRemovedFromTargetLang; + + // Only public functions in final classes + // default_flags |= isFinal() ? WasPublic : 0; + FunctionQueryOptions public_flags; + if (isFinalInTargetLang()) + public_flags |= WasPublic; + + // Constructors + AbstractMetaFunctionList returned = queryFunctions(Constructors | default_flags | public_flags); + + // Final functions + returned += queryFunctions(FinalInTargetLangFunctions | NonStaticFunctions | default_flags | public_flags); + + // Virtual functions + returned += queryFunctions(VirtualInTargetLangFunctions | NonStaticFunctions | default_flags | public_flags); + + // Static functions + returned += queryFunctions(StaticFunctions | default_flags | public_flags); + + // Empty, private functions, since they aren't caught by the other ones + returned += queryFunctions(Empty | Invisible); + + return returned; +} + +AbstractMetaFunctionList AbstractMetaClass::implicitConversions() const +{ + if (!hasCloneOperator() && !hasExternalConversionOperators()) + return AbstractMetaFunctionList(); + + AbstractMetaFunctionList returned; + const AbstractMetaFunctionList list = queryFunctions(Constructors) + externalConversionOperators(); + + // Exclude anything that uses rvalue references, be it a move + // constructor "QPolygon(QPolygon &&)" or something else like + // "QPolygon(QVector<QPoint> &&)". + for (AbstractMetaFunction *f : list) { + if ((f->actualMinimumArgumentCount() == 1 || f->arguments().size() == 1 || f->isConversionOperator()) + && !f->isExplicit() + && f->functionType() != AbstractMetaFunction::CopyConstructorFunction + && !f->usesRValueReferences() + && !f->isModifiedRemoved() + && (f->originalAttributes() & Public)) { + returned += f; + } + } + return returned; +} + +AbstractMetaFunctionList AbstractMetaClass::operatorOverloads(OperatorQueryOptions query) const +{ + const AbstractMetaFunctionList &list = queryFunctions(OperatorOverloads | Visible); + AbstractMetaFunctionList returned; + for (AbstractMetaFunction *f : list) { + if (((query & ArithmeticOp) && f->isArithmeticOperator()) + || ((query & BitwiseOp) && f->isBitwiseOperator()) + || ((query & ComparisonOp) && f->isComparisonOperator()) + || ((query & LogicalOp) && f->isLogicalOperator()) + || ((query & SubscriptionOp) && f->isSubscriptOperator()) + || ((query & AssignmentOp) && f->isAssignmentOperator()) + || ((query & ConversionOp) && f->isConversionOperator()) + || ((query & OtherOp) && f->isOtherOperator())) + returned += f; + } + + return returned; +} + +bool AbstractMetaClass::hasArithmeticOperatorOverload() const +{ + for (const AbstractMetaFunction *f : m_functions) { + if (f->ownerClass() == f->implementingClass() && f->isArithmeticOperator() && !f->isPrivate()) + return true; + } + return false; +} + +bool AbstractMetaClass::hasBitwiseOperatorOverload() const +{ + for (const AbstractMetaFunction *f : m_functions) { + if (f->ownerClass() == f->implementingClass() && f->isBitwiseOperator() && !f->isPrivate()) + return true; + } + return false; +} + +bool AbstractMetaClass::hasComparisonOperatorOverload() const +{ + for (const AbstractMetaFunction *f : m_functions) { + if (f->ownerClass() == f->implementingClass() && f->isComparisonOperator() && !f->isPrivate()) + return true; + } + return false; +} + +bool AbstractMetaClass::hasLogicalOperatorOverload() const +{ + for (const AbstractMetaFunction *f : m_functions) { + if (f->ownerClass() == f->implementingClass() && f->isLogicalOperator() && !f->isPrivate()) + return true; + } + return false; +} + +void AbstractMetaClass::sortFunctions() +{ + std::sort(m_functions.begin(), m_functions.end(), function_sorter); +} + +void AbstractMetaClass::setFunctions(const AbstractMetaFunctionList &functions) +{ + m_functions = functions; + + // Functions must be sorted by name before next loop + sortFunctions(); + + for (AbstractMetaFunction *f : qAsConst(m_functions)) { + f->setOwnerClass(this); + if (!f->isPublic()) + m_hasNonpublic = true; + } +} + +bool AbstractMetaClass::hasDefaultToStringFunction() const +{ + const AbstractMetaFunctionList &funcs = queryFunctionsByName(QLatin1String("toString")); + for (const AbstractMetaFunction *f : funcs) { + if (!f->actualMinimumArgumentCount()) + return true; + } + return false; +} + +void AbstractMetaClass::addFunction(AbstractMetaFunction *function) +{ + Q_ASSERT(!function->signature().startsWith(QLatin1Char('('))); + function->setOwnerClass(this); + + if (!function->isDestructor()) + m_functions << function; + else + Q_ASSERT(false); //memory leak + + m_hasVirtuals |= function->isVirtual(); + m_isPolymorphic |= m_hasVirtuals; + m_hasNonpublic |= !function->isPublic(); +} + +bool AbstractMetaClass::hasSignal(const AbstractMetaFunction *other) const +{ + if (!other->isSignal()) + return false; + + for (const AbstractMetaFunction *f : m_functions) { + if (f->isSignal() && f->compareTo(other) & AbstractMetaFunction::EqualName) + return other->modifiedName() == f->modifiedName(); + } + + return false; +} + + +QString AbstractMetaClass::name() const +{ + return m_typeEntry->targetLangEntryName(); +} + +void AbstractMetaClass::addBaseClass(AbstractMetaClass *baseClass) +{ + Q_ASSERT(baseClass); + m_baseClasses.append(baseClass); + m_isPolymorphic |= baseClass->isPolymorphic(); +} + +void AbstractMetaClass::setBaseClass(AbstractMetaClass *baseClass) +{ + if (baseClass) { + m_baseClasses.prepend(baseClass); + m_isPolymorphic |= baseClass->isPolymorphic(); + } +} + +QString AbstractMetaClass::package() const +{ + return m_typeEntry->targetLangPackage(); +} + +bool AbstractMetaClass::isNamespace() const +{ + return m_typeEntry->isNamespace(); +} + +// Is an invisible namespaces whose functions/enums +// should be mapped to the global space. +bool AbstractMetaClass::isInvisibleNamespace() const +{ + return m_typeEntry->isNamespace() && m_typeEntry->generateCode() + && !NamespaceTypeEntry::isVisibleScope(m_typeEntry); +} + +static bool qObjectPredicate(const AbstractMetaClass *c) +{ + return c->qualifiedCppName() == QLatin1String("QObject"); +} + +bool AbstractMetaClass::isQObject() const +{ + return qObjectPredicate(this) || recurseClassHierarchy(this, qObjectPredicate) != nullptr; +} + +QString AbstractMetaClass::qualifiedCppName() const +{ + return m_typeEntry->qualifiedCppName(); +} + +bool AbstractMetaClass::hasFunction(const QString &str) const +{ + return findFunction(str); +} + +const AbstractMetaFunction *AbstractMetaClass::findFunction(const QString &functionName) const +{ + return AbstractMetaFunction::find(m_functions, functionName); +} + +bool AbstractMetaClass::hasProtectedFunctions() const +{ + for (AbstractMetaFunction *func : m_functions) { + if (func->isProtected()) + return true; + } + return false; +} + +bool AbstractMetaClass::hasProtectedFields() const +{ + for (const AbstractMetaField *field : m_fields) { + if (field->isProtected()) + return true; + } + return false; +} + +bool AbstractMetaClass::hasProtectedMembers() const +{ + return hasProtectedFields() || hasProtectedFunctions(); +} + +QPropertySpec *AbstractMetaClass::propertySpecByName(const QString &name) const +{ + for (auto propertySpec : m_propertySpecs) { + if (name == propertySpec->name()) + return propertySpec; + } + return nullptr; +} + +QPropertySpec *AbstractMetaClass::propertySpecForRead(const QString &name) const +{ + for (const auto &propertySpec : m_propertySpecs) { + if (name == propertySpec->read()) + return propertySpec; + } + return nullptr; +} + +QPropertySpec *AbstractMetaClass::propertySpecForWrite(const QString &name) const +{ + for (const auto &propertySpec : m_propertySpecs) { + if (name == propertySpec->write()) + return propertySpec; + } + return nullptr; +} + +QPropertySpec *AbstractMetaClass::propertySpecForReset(const QString &name) const +{ + for (const auto &propertySpec : m_propertySpecs) { + if (name == propertySpec->reset()) + return propertySpec; + } + return nullptr; +} + +bool AbstractMetaClass::hasTemplateBaseClassInstantiations() const +{ + return m_templateBaseClass != nullptr && !m_baseTemplateInstantiations.isEmpty(); +} + +const AbstractMetaTypeList &AbstractMetaClass::templateBaseClassInstantiations() const +{ + return m_baseTemplateInstantiations; +} + +void AbstractMetaClass::setTemplateBaseClassInstantiations(const AbstractMetaTypeList &instantiations) +{ + Q_ASSERT(m_templateBaseClass != nullptr); + m_baseTemplateInstantiations = instantiations; +} + +// Does any of the base classes require deletion in the main thread? +bool AbstractMetaClass::deleteInMainThread() const +{ + return typeEntry()->deleteInMainThread() + || (!m_baseClasses.isEmpty() && m_baseClasses.constFirst()->deleteInMainThread()); +} + +static bool functions_contains(const AbstractMetaFunctionList &l, const AbstractMetaFunction *func) +{ + for (const AbstractMetaFunction *f : l) { + if ((f->compareTo(func) & AbstractMetaFunction::PrettySimilar) == AbstractMetaFunction::PrettySimilar) + return true; + } + return false; +} + +AbstractMetaField::AbstractMetaField() = default; + +AbstractMetaField *AbstractMetaField::copy() const +{ + auto *returned = new AbstractMetaField; + returned->assignMetaVariable(*this); + returned->assignMetaAttributes(*this); + returned->setEnclosingClass(nullptr); + return returned; +} + +AbstractMetaField *AbstractMetaField::find(const AbstractMetaFieldList &haystack, + const QString &needle) +{ + return findByName(haystack, needle); +} +/******************************************************************************* + * Indicates that this field has a modification that removes it + */ +bool AbstractMetaField::isModifiedRemoved(int types) const +{ + const FieldModificationList &mods = modifications(); + for (const FieldModification &mod : mods) { + if (!mod.isRemoveModifier()) + continue; + + if ((mod.removal & types) == types) + return true; + } + + return false; +} + +FieldModificationList AbstractMetaField::modifications() const +{ + const FieldModificationList &mods = enclosingClass()->typeEntry()->fieldModifications(); + FieldModificationList returned; + + for (const FieldModification &mod : mods) { + if (mod.name == name()) + returned += mod; + } + + return returned; +} + +const AbstractMetaClass *EnclosingClassMixin::targetLangEnclosingClass() const +{ + auto result = m_enclosingClass; + while (result && !NamespaceTypeEntry::isVisibleScope(result->typeEntry())) + result = result->enclosingClass(); + return result; +} + +#ifndef QT_NO_DEBUG_STREAM +static void formatMetaAttributes(QDebug &d, AbstractMetaAttributes::Attributes value) +{ + static const int meIndex = AbstractMetaAttributes::staticMetaObject.indexOfEnumerator("Attribute"); + Q_ASSERT(meIndex >= 0); + const QMetaEnum me = AbstractMetaAttributes::staticMetaObject.enumerator(meIndex); + d << me.valueToKeys(value); +} + +static void formatMetaField(QDebug &d, const AbstractMetaField *af) +{ + formatMetaAttributes(d, af->attributes()); + d << ' ' << af->type().name() << " \"" << af->name() << '"'; +} + +QDebug operator<<(QDebug d, const AbstractMetaField *af) +{ + QDebugStateSaver saver(d); + d.noquote(); + d.nospace(); + d << "AbstractMetaField("; + if (af) + formatMetaField(d, af); + else + d << '0'; + d << ')'; + return d; +} + +static void formatMetaEnumValue(QDebug &d, const AbstractMetaEnumValue *v) +{ + const QString &name = v->stringValue(); + if (!name.isEmpty()) + d << name << '='; + d << v->value(); +} + +QDebug operator<<(QDebug d, const AbstractMetaEnumValue *v) +{ + QDebugStateSaver saver(d); + d.noquote(); + d.nospace(); + d << "AbstractMetaEnumValue("; + if (v) + formatMetaEnumValue(d, v); + else + d << '0'; + d << ')'; + return d; +} + +QDebug operator<<(QDebug d, const AbstractMetaEnum *ae) +{ + QDebugStateSaver saver(d); + d.noquote(); + d.nospace(); + d << "AbstractMetaEnum("; + if (ae) { + d << ae->fullName(); + if (!ae->isSigned()) + d << " (unsigned) "; + d << '['; + const AbstractMetaEnumValueList &values = ae->values(); + for (int i = 0, count = values.size(); i < count; ++i) { + if (i) + d << ' '; + formatMetaEnumValue(d, values.at(i)); + } + d << ']'; + } else { + d << '0'; + } + d << ')'; + return d; +} +#endif // !QT_NO_DEBUG_STREAM + +bool AbstractMetaClass::hasConstructors() const +{ + return AbstractMetaClass::queryFirstFunction(m_functions, Constructors) != nullptr; +} + +const AbstractMetaFunction *AbstractMetaClass::copyConstructor() const +{ + for (const AbstractMetaFunction *f : m_functions) { + if (f->functionType() == AbstractMetaFunction::CopyConstructorFunction) + return f; + } + return nullptr; +} + +bool AbstractMetaClass::hasPrivateCopyConstructor() const +{ + const AbstractMetaFunction *copyCt = copyConstructor(); + return copyCt && copyCt->isPrivate(); +} + +void AbstractMetaClass::addDefaultConstructor() +{ + auto *f = new AbstractMetaFunction; + f->setType(AbstractMetaType::createVoid()); + f->setOriginalName(name()); + f->setName(name()); + f->setOwnerClass(this); + f->setFunctionType(AbstractMetaFunction::ConstructorFunction); + f->setArguments(AbstractMetaArgumentList()); + f->setDeclaringClass(this); + + f->setAttributes(Public | FinalInTargetLang | AddedMethod); + f->setImplementingClass(this); + f->setOriginalAttributes(f->attributes()); + + addFunction(f); + this->setHasNonPrivateConstructor(true); +} + +void AbstractMetaClass::addDefaultCopyConstructor(bool isPrivate) +{ + auto f = new AbstractMetaFunction; + f->setType(AbstractMetaType::createVoid()); + f->setOriginalName(name()); + f->setName(name()); + f->setOwnerClass(this); + f->setFunctionType(AbstractMetaFunction::CopyConstructorFunction); + f->setDeclaringClass(this); + + AbstractMetaType argType(typeEntry()); + argType.setReferenceType(LValueReference); + argType.setConstant(true); + argType.setTypeUsagePattern(AbstractMetaType::ValuePattern); + + AbstractMetaArgument arg; + arg.setType(argType); + arg.setName(name()); + f->addArgument(arg); + + AbstractMetaAttributes::Attributes attr = FinalInTargetLang | AddedMethod; + if (isPrivate) + attr |= AbstractMetaAttributes::Private; + else + attr |= AbstractMetaAttributes::Public; + f->setAttributes(attr); + f->setImplementingClass(this); + f->setOriginalAttributes(f->attributes()); + + addFunction(f); +} + +void AbstractMetaClass::setHasVirtualDestructor(bool value) +{ + m_hasVirtualDestructor = value; + if (value) + m_hasVirtuals = m_isPolymorphic = 1; +} + +bool AbstractMetaClass::hasFunction(const AbstractMetaFunction *f) const +{ + return functions_contains(m_functions, f); +} + +bool AbstractMetaClass::generateExceptionHandling() const +{ + return queryFirstFunction(m_functions, AbstractMetaClass::Visible + | AbstractMetaClass::GenerateExceptionHandling) != nullptr; +} +/* Goes through the list of functions and returns a list of all + functions matching all of the criteria in \a query. + */ + +bool AbstractMetaClass::queryFunction(const AbstractMetaFunction *f, FunctionQueryOptions query) +{ + if ((query & NotRemovedFromTargetLang) + && f->isRemovedFrom(f->implementingClass(), TypeSystem::TargetLangCode)) { + return false; + } + + if ((query & NotRemovedFromTargetLang) && f->isVirtual() + && f->isRemovedFrom(f->declaringClass(), TypeSystem::TargetLangCode)) { + return false; + } + + if ((query & Visible) && f->isPrivate()) + return false; + + if ((query & VirtualInTargetLangFunctions) && f->isFinalInTargetLang()) + return false; + + if ((query & Invisible) && !f->isPrivate()) + return false; + + if ((query & Empty) && !f->isEmptyFunction()) + return false; + + if ((query & WasPublic) && !f->wasPublic()) + return false; + + if ((query & ClassImplements) && f->ownerClass() != f->implementingClass()) + return false; + + if ((query & FinalInTargetLangFunctions) && !f->isFinalInTargetLang()) + return false; + + if ((query & VirtualInCppFunctions) && !f->isVirtual()) + return false; + + if ((query & Signals) && (!f->isSignal())) + return false; + + if ((query & Constructors) && (!f->isConstructor() || f->ownerClass() != f->implementingClass())) + return false; + + if (!(query & Constructors) && f->isConstructor()) + return false; + + // Destructors are never included in the functions of a class currently + /* + if ((query & Destructors) && (!f->isDestructor() + || f->ownerClass() != f->implementingClass()) + || f->isDestructor() && (query & Destructors) == 0) { + return false; + }*/ + + if ((query & StaticFunctions) && (!f->isStatic() || f->isSignal())) + return false; + + if ((query & NonStaticFunctions) && (f->isStatic())) + return false; + + if ((query & NormalFunctions) && (f->isSignal())) + return false; + + if ((query & OperatorOverloads) && !f->isOperatorOverload()) + return false; + + if ((query & GenerateExceptionHandling) && !f->generateExceptionHandling()) + return false; + + if (query.testFlag(GetAttroFunction) + && f->functionType() != AbstractMetaFunction::GetAttroFunction) { + return false; + } + + if (query.testFlag(SetAttroFunction) + && f->functionType() != AbstractMetaFunction::SetAttroFunction) { + return false; + } + + return true; +} + +AbstractMetaFunctionList AbstractMetaClass::queryFunctionList(const AbstractMetaFunctionList &list, + FunctionQueryOptions query) +{ + AbstractMetaFunctionList result; + for (AbstractMetaFunction *f : list) { + if (queryFunction(f, query)) + result.append(f); + } + return result; +} + +const AbstractMetaFunction *AbstractMetaClass::queryFirstFunction(const AbstractMetaFunctionList &list, + FunctionQueryOptions query) +{ + AbstractMetaFunctionList result; + for (AbstractMetaFunction *f : list) { + if (queryFunction(f, query)) + return f; + } + return nullptr; +} + +AbstractMetaFunctionList AbstractMetaClass::queryFunctions(FunctionQueryOptions query) const +{ + return AbstractMetaClass::queryFunctionList(m_functions, query); +} + +bool AbstractMetaClass::hasSignals() const +{ + return queryFirstFunction(m_functions, Signals | Visible | NotRemovedFromTargetLang) != nullptr; +} + +AbstractMetaFunctionList AbstractMetaClass::cppSignalFunctions() const +{ + return queryFunctions(Signals | Visible | NotRemovedFromTargetLang); +} + +AbstractMetaField *AbstractMetaClass::findField(const QString &name) const +{ + return AbstractMetaField::find(m_fields, name); +} + +AbstractMetaEnum *AbstractMetaClass::findEnum(const QString &enumName) +{ + if (AbstractMetaEnum *e = findByName(m_enums, enumName)) + return e; + return nullptr; +} + +/*! Recursively searches for the enum value named \a enumValueName in + this class and its superclasses and interfaces. +*/ +AbstractMetaEnumValue *AbstractMetaClass::findEnumValue(const QString &enumValueName) +{ + for (AbstractMetaEnum *e : qAsConst(m_enums)) { + if (AbstractMetaEnumValue *v = e->findEnumValue(enumValueName)) + return v; + } + if (baseClass()) + return baseClass()->findEnumValue(enumValueName); + + return nullptr; +} + +void AbstractMetaClass::getEnumsToBeGenerated(AbstractMetaEnumList *enumList) const +{ + for (AbstractMetaEnum *metaEnum : m_enums) { + if (!metaEnum->isPrivate() && metaEnum->typeEntry()->generateCode()) + enumList->append(metaEnum); + } +} + +void AbstractMetaClass::getEnumsFromInvisibleNamespacesToBeGenerated(AbstractMetaEnumList *enumList) const +{ + if (isNamespace()) { + invisibleNamespaceRecursion([enumList](AbstractMetaClass *c) { + c->getEnumsToBeGenerated(enumList); + }); + } +} + +void AbstractMetaClass::getFunctionsFromInvisibleNamespacesToBeGenerated(AbstractMetaFunctionList *funcList) const +{ + if (isNamespace()) { + invisibleNamespaceRecursion([funcList](AbstractMetaClass *c) { + funcList->append(c->functions()); + }); + } +} + +static void addExtraIncludeForType(AbstractMetaClass *metaClass, const AbstractMetaType &type) +{ + + Q_ASSERT(metaClass); + const TypeEntry *entry = type.typeEntry(); + if (entry && entry->isComplex()) { + const auto *centry = static_cast<const ComplexTypeEntry *>(entry); + ComplexTypeEntry *class_entry = metaClass->typeEntry(); + if (class_entry && centry->include().isValid()) + class_entry->addExtraInclude(centry->include()); + } + + if (type.hasInstantiations()) { + for (const AbstractMetaType &instantiation : type.instantiations()) + addExtraIncludeForType(metaClass, instantiation); + } +} + +static void addExtraIncludesForFunction(AbstractMetaClass *metaClass, const AbstractMetaFunction *meta_function) +{ + Q_ASSERT(metaClass); + Q_ASSERT(meta_function); + addExtraIncludeForType(metaClass, meta_function->type()); + + const AbstractMetaArgumentList &arguments = meta_function->arguments(); + for (const AbstractMetaArgument &argument : arguments) + addExtraIncludeForType(metaClass, argument.type()); +} + +void AbstractMetaClass::fixFunctions() +{ + if (m_functionsFixed) + return; + + m_functionsFixed = true; + + AbstractMetaFunctionList funcs = functions(); + + for (auto superClass : m_baseClasses) { + superClass->fixFunctions(); + // Since we always traverse the complete hierarchy we are only + // interrested in what each super class implements, not what + // we may have propagated from their base classes again. + AbstractMetaFunctionList superFuncs; + // Super classes can never be final + if (superClass->isFinalInTargetLang()) { + qCWarning(lcShiboken).noquote().nospace() + << "Final class '" << superClass->name() << "' set to non-final, as it is extended by other classes"; + *superClass -= AbstractMetaAttributes::FinalInTargetLang; + } + superFuncs = superClass->queryFunctions(AbstractMetaClass::ClassImplements); + AbstractMetaFunctionList virtuals = superClass->queryFunctions(AbstractMetaClass::VirtualInCppFunctions); + superFuncs += virtuals; + + QSet<AbstractMetaFunction *> funcsToAdd; + for (auto sf : qAsConst(superFuncs)) { + if (sf->isRemovedFromAllLanguages(sf->implementingClass())) + continue; + + // skip functions added in base classes + if (sf->isUserAdded() && sf->declaringClass() != this) + continue; + + // we generally don't care about private functions, but we have to get the ones that are + // virtual in case they override abstract functions. + bool add = (sf->isNormal() || sf->isSignal() || sf->isEmptyFunction()); + for (AbstractMetaFunction *f : funcs) { + if (f->isRemovedFromAllLanguages(f->implementingClass())) + continue; + + + const AbstractMetaFunction::CompareResult cmp = f->compareTo(sf); + + if (cmp & AbstractMetaFunction::EqualModifiedName) { + add = false; + if (cmp & AbstractMetaFunction::EqualArguments) { + // Same function, propegate virtual... + if (!(cmp & AbstractMetaFunction::EqualAttributes)) { + if (!f->isEmptyFunction()) { + if (!sf->isFinalInTargetLang() && f->isFinalInTargetLang()) { + *f -= AbstractMetaAttributes::FinalInTargetLang; + } +#if 0 + if (!f->isFinalInTargetLang() && f->isPrivate()) { + f->setFunctionType(AbstractMetaFunction::EmptyFunction); + f->setVisibility(AbstractMetaAttributes::Protected); + *f += AbstractMetaAttributes::FinalInTargetLang; + qCWarning(lcShiboken).noquote().nospace() + << QStringLiteral("private virtual function '%1' in '%2'") + .arg(f->signature(), f->implementingClass()->name()); + } +#endif + } + } + + if (f->visibility() != sf->visibility()) { + QString warn = QStringLiteral("visibility of function '%1' modified in class '%2'") + .arg(f->name(), name()); + qCWarning(lcShiboken).noquote().nospace() << warn; +#if 0 + // If new visibility is private, we can't + // do anything. If it isn't, then we + // prefer the parent class's visibility + // setting for the function. + if (!f->isPrivate() && !sf->isPrivate()) + f->setVisibility(sf->visibility()); +#endif + // Private overrides of abstract functions have to go into the class or + // the subclasses will not compile as non-abstract classes. + // But they don't need to be implemented, since they can never be called. + if (f->isPrivate()) { + f->setFunctionType(AbstractMetaFunction::EmptyFunction); + *f += AbstractMetaAttributes::FinalInTargetLang; + } + } + + // Set the class which first declares this function, afawk + f->setDeclaringClass(sf->declaringClass()); + + if (sf->isFinalInTargetLang() && !sf->isPrivate() && !f->isPrivate() && !sf->isStatic() && !f->isStatic()) { + // Shadowed funcion, need to make base class + // function non-virtual + if (f->implementingClass() != sf->implementingClass() && f->implementingClass()->inheritsFrom(sf->implementingClass())) { + + // Check whether the superclass method has been redefined to non-final + + bool hasNonFinalModifier = false; + bool isBaseImplPrivate = false; + const FunctionModificationList &mods = sf->modifications(sf->implementingClass()); + for (const FunctionModification &mod : mods) { + if (mod.isNonFinal()) { + hasNonFinalModifier = true; + break; + } + if (mod.isPrivate()) { + isBaseImplPrivate = true; + break; + } + } + + if (!hasNonFinalModifier && !isBaseImplPrivate) { + qCWarning(lcShiboken).noquote().nospace() + << QStringLiteral("Shadowing: %1::%2 and %3::%4") + .arg(sf->implementingClass()->name(), sf->signature(), + f->implementingClass()->name(), f->signature()); + } + } + } + + } + + if (cmp & AbstractMetaFunction::EqualDefaultValueOverload) { + AbstractMetaArgumentList arguments; + if (f->arguments().size() < sf->arguments().size()) + arguments = sf->arguments(); + else + arguments = f->arguments(); + //TODO: fix this + //for (int i=0; i<arguments.size(); ++i) + // arguments[i]->setDefaultValueExpression("<#>" + QString()); + } + + + // Otherwise we have function shadowing and we can + // skip the thing... + } else if (cmp & AbstractMetaFunction::EqualName && !sf->isSignal()) { + // In the case of function shadowing where the function name has been altered to + // avoid conflict, we don't copy in the original. + add = false; + } + } + + if (add) + funcsToAdd << sf; + } + + for (AbstractMetaFunction *f : qAsConst(funcsToAdd)) { + AbstractMetaFunction *copy = f->copy(); + (*copy) += AddedMethod; + funcs.append(copy); + } + } + + bool hasPrivateConstructors = false; + bool hasPublicConstructors = false; + for (AbstractMetaFunction *func : qAsConst(funcs)) { + const FunctionModificationList &mods = func->modifications(this); + for (const FunctionModification &mod : mods) { + if (mod.isRenameModifier()) { + func->setName(mod.renamedTo()); + } + } + + // Make sure class is abstract if one of the functions is + if (func->isAbstract()) { + (*this) += AbstractMetaAttributes::Abstract; + (*this) -= AbstractMetaAttributes::FinalInTargetLang; + } + + if (func->isConstructor()) { + if (func->isPrivate()) + hasPrivateConstructors = true; + else + hasPublicConstructors = true; + } + + + + // Make sure that we include files for all classes that are in use + + if (!func->isRemovedFrom(this, TypeSystem::ShellCode)) + addExtraIncludesForFunction(this, func); + } + + if (hasPrivateConstructors && !hasPublicConstructors) { + (*this) += AbstractMetaAttributes::Abstract; + (*this) -= AbstractMetaAttributes::FinalInTargetLang; + } + + setFunctions(funcs); +} + +/******************************************************************************* + * Other stuff... + */ + + +AbstractMetaEnum *AbstractMetaClass::findEnum(const AbstractMetaClassList &classes, + const EnumTypeEntry *entry) +{ + Q_ASSERT(entry->isEnum()); + + QString qualifiedName = entry->qualifiedCppName(); + int pos = qualifiedName.lastIndexOf(QLatin1String("::")); + + QString enumName; + QString className; + + if (pos > 0) { + enumName = qualifiedName.mid(pos + 2); + className = qualifiedName.mid(0, pos); + } else { + enumName = qualifiedName; + className = TypeDatabase::globalNamespaceClassName(entry); + } + + AbstractMetaClass *metaClass = AbstractMetaClass::findClass(classes, className); + if (!metaClass) { + qCWarning(lcShiboken).noquote().nospace() + << QStringLiteral("AbstractMeta::findEnum(), unknown class '%1' in '%2'") + .arg(className, entry->qualifiedCppName()); + return nullptr; + } + + return metaClass->findEnum(enumName); +} + +AbstractMetaEnumValue *AbstractMetaClass::findEnumValue(const AbstractMetaClassList &classes, + const QString &name) +{ + const auto lst = QStringView{name}.split(u"::"); + + if (lst.size() > 1) { + const auto &prefixName = lst.at(0); + const auto &enumName = lst.at(1); + if (AbstractMetaClass *cl = findClass(classes, prefixName.toString())) + return cl->findEnumValue(enumName.toString()); + } + + for (AbstractMetaClass *metaClass : classes) { + if (AbstractMetaEnumValue *enumValue = metaClass->findEnumValue(name)) + return enumValue; + } + + qCWarning(lcShiboken).noquote().nospace() + << QStringLiteral("no matching enum '%1'").arg(name); + return nullptr; +} + +/*! + * Searches the list after a class that mathces \a name; either as + * C++, Target language base name or complete Target language package.class name. + */ + +AbstractMetaClass *AbstractMetaClass::findClass(const AbstractMetaClassList &classes, + const QString &name) +{ + if (name.isEmpty()) + return nullptr; + + for (AbstractMetaClass *c : classes) { + if (c->qualifiedCppName() == name) + return c; + } + + for (AbstractMetaClass *c : classes) { + if (c->fullName() == name) + return c; + } + + for (AbstractMetaClass *c : classes) { + if (c->name() == name) + return c; + } + + return nullptr; +} + +AbstractMetaClass *AbstractMetaClass::findClass(const AbstractMetaClassList &classes, + const TypeEntry *typeEntry) +{ + for (AbstractMetaClass *c : classes) { + if (c->typeEntry() == typeEntry) + return c; + } + return nullptr; +} + +#ifndef QT_NO_DEBUG_STREAM + +void AbstractMetaClass::format(QDebug &d) const +{ + if (d.verbosity() > 2) + d << static_cast<const void *>(this) << ", "; + d << '"' << qualifiedCppName(); + if (const int count = m_templateArgs.size()) { + for (int i = 0; i < count; ++i) + d << (i ? ',' : '<') << m_templateArgs.at(i)->qualifiedCppName(); + d << '>'; + } + d << '"'; + if (isNamespace()) + d << " [namespace]"; + if (attributes() & AbstractMetaAttributes::FinalCppClass) + d << " [final]"; + if (attributes().testFlag(AbstractMetaAttributes::Deprecated)) + d << " [deprecated]"; + if (!m_baseClasses.isEmpty()) { + d << ", inherits "; + for (auto b : m_baseClasses) + d << " \"" << b->name() << '"'; + } + if (auto templateBase = templateBaseClass()) { + const auto &instantiatedTypes = templateBaseClassInstantiations(); + d << ", instantiates \"" << templateBase->name(); + for (int i = 0, count = instantiatedTypes.size(); i < count; ++i) + d << (i ? ',' : '<') << instantiatedTypes.at(i).name(); + d << ">\""; + } + if (const int count = m_propertySpecs.size()) { + d << ", properties (" << count << "): ["; + for (int i = 0; i < count; ++i) { + if (i) + d << ", "; + m_propertySpecs.at(i)->formatDebug(d); + } + d << ']'; + } +} + +void AbstractMetaClass::formatMembers(QDebug &d) const +{ + if (!m_enums.isEmpty()) + d << ", enums[" << m_enums.size() << "]=" << m_enums; + if (!m_functions.isEmpty()) { + const int count = m_functions.size(); + d << ", functions=[" << count << "]("; + for (int i = 0; i < count; ++i) { + if (i) + d << ", "; + formatMetaFunctionBrief(d, m_functions.at(i)); + } + d << ')'; + } + if (const int count = m_fields.size()) { + d << ", fields=[" << count << "]("; + for (int i = 0; i < count; ++i) { + if (i) + d << ", "; + formatMetaField(d, m_fields.at(i)); + } + d << ')'; + } +} + +SourceLocation AbstractMetaClass::sourceLocation() const +{ + return m_sourceLocation; +} + +void AbstractMetaClass::setSourceLocation(const SourceLocation &sourceLocation) +{ + m_sourceLocation = sourceLocation; +} + +QDebug operator<<(QDebug d, const AbstractMetaClass *ac) +{ + QDebugStateSaver saver(d); + d.noquote(); + d.nospace(); + d << "AbstractMetaClass("; + if (ac) { + ac->format(d); + if (d.verbosity() > 2) + ac->formatMembers(d); + } else { + d << '0'; + } + d << ')'; + return d; +} +#endif // !QT_NO_DEBUG_STREAM + +/******************************************************************************* +* AbstractMetaEnum +*/ + +AbstractMetaEnum::AbstractMetaEnum() : + m_hasQenumsDeclaration(false), m_signed(true) +{ +} + +AbstractMetaEnum::~AbstractMetaEnum() +{ + qDeleteAll(m_enumValues); +} + +template <class String> +AbstractMetaEnumValue *findMatchingEnumValue(const AbstractMetaEnumValueList &list, const String &value) +{ + for (AbstractMetaEnumValue *enumValue : list) { + if (enumValue->name() == value) + return enumValue; + } + return nullptr; +} + +// Find enum values for "enum Enum { e1 }" either for "e1" or "Enum::e1" +AbstractMetaEnumValue *AbstractMetaEnum::findEnumValue(const QString &value) const +{ + if (isAnonymous()) + return findMatchingEnumValue(m_enumValues, value); + const int sepPos = value.indexOf(QLatin1String("::")); + if (sepPos == -1) + return findMatchingEnumValue(m_enumValues, value); + return name() == QStringView{value}.left(sepPos) + ? findMatchingEnumValue(m_enumValues, QStringView{value}.right(value.size() - sepPos - 2)) + : nullptr; +} + +QString AbstractMetaEnum::name() const +{ + return m_typeEntry->targetLangEntryName(); +} + +QString AbstractMetaEnum::qualifier() const +{ + return m_typeEntry->targetLangQualifier(); +} + +QString AbstractMetaEnum::package() const +{ + return m_typeEntry->targetLangPackage(); +} + |