diff options
Diffstat (limited to 'sources/shiboken2/generator/shiboken2/overloaddata.cpp')
-rw-r--r-- | sources/shiboken2/generator/shiboken2/overloaddata.cpp | 1091 |
1 files changed, 0 insertions, 1091 deletions
diff --git a/sources/shiboken2/generator/shiboken2/overloaddata.cpp b/sources/shiboken2/generator/shiboken2/overloaddata.cpp deleted file mode 100644 index bd39e9444..000000000 --- a/sources/shiboken2/generator/shiboken2/overloaddata.cpp +++ /dev/null @@ -1,1091 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <abstractmetalang.h> -#include <reporthandler.h> -#include <graph.h> -#include "overloaddata.h" -#include "indentor.h" -#include "shibokengenerator.h" - -#include <QtCore/QDir> -#include <QtCore/QFile> -#include <QtCore/QTemporaryFile> - -static const TypeEntry *getReferencedTypeEntry(const TypeEntry *typeEntry) -{ - if (typeEntry->isPrimitive()) { - auto pte = dynamic_cast<const PrimitiveTypeEntry *>(typeEntry); - while (pte->referencedTypeEntry()) - pte = pte->referencedTypeEntry(); - typeEntry = pte; - } - return typeEntry; -} - -static QString getTypeName(const AbstractMetaType *type) -{ - const TypeEntry *typeEntry = getReferencedTypeEntry(type->typeEntry()); - QString typeName = typeEntry->name(); - if (typeEntry->isContainer()) { - QStringList types; - const AbstractMetaTypeList &instantiations = type->instantiations(); - for (const AbstractMetaType *cType : instantiations) { - const TypeEntry *typeEntry = getReferencedTypeEntry(cType->typeEntry()); - types << typeEntry->name(); - } - typeName += QLatin1Char('<') + types.join(QLatin1Char(',')) + QLatin1String(" >"); - } - return typeName; -} - -static QString getTypeName(const OverloadData *ov) -{ - return ov->hasArgumentTypeReplace() ? ov->argumentTypeReplaced() : getTypeName(ov->argType()); -} - -static bool typesAreEqual(const AbstractMetaType *typeA, const AbstractMetaType *typeB) -{ - if (typeA->typeEntry() == typeB->typeEntry()) { - if (typeA->isContainer() || typeA->isSmartPointer()) { - if (typeA->instantiations().size() != typeB->instantiations().size()) - return false; - - for (int i = 0; i < typeA->instantiations().size(); ++i) { - if (!typesAreEqual(typeA->instantiations().at(i), typeB->instantiations().at(i))) - return false; - } - return true; - } - - return !(ShibokenGenerator::isCString(typeA) ^ ShibokenGenerator::isCString(typeB)); - } - return false; -} - - -/** - * OverloadSortData just helps writing clearer code in the - * OverloadData::sortNextOverloads method. - */ -struct OverloadSortData -{ - /** - * Adds a typeName into the type map without associating it with - * a OverloadData. This is done to express type dependencies that could - * or could not appear in overloaded signatures not processed yet. - */ - void mapType(const QString &typeName) - { - if (map.contains(typeName)) - return; - map[typeName] = counter; - if (!reverseMap.contains(counter)) - reverseMap[counter] = nullptr; - counter++; - } - - void mapType(OverloadData *overloadData) - { - QString typeName = getTypeName(overloadData); - map[typeName] = counter; - reverseMap[counter] = overloadData; - counter++; - } - - int lastProcessedItemId() { return counter - 1; } - - int counter = 0; - QHash<QString, int> map; // typeName -> id - QHash<int, OverloadData *> reverseMap; // id -> OverloadData; -}; - -/** - * Helper function that returns the name of a container get from containerType argument and - * an instantiation taken either from an implicit conversion expressed by the function argument, - * or from the string argument implicitConvTypeName. - */ -static QString getImplicitConversionTypeName(const AbstractMetaType *containerType, - const AbstractMetaType *instantiation, - const AbstractMetaFunction *function, - const QString &implicitConvTypeName = QString()) -{ - QString impConv; - if (!implicitConvTypeName.isEmpty()) - impConv = implicitConvTypeName; - else if (function->isConversionOperator()) - impConv = function->ownerClass()->typeEntry()->name(); - else - impConv = getTypeName(function->arguments().constFirst()->type()); - - QStringList types; - const AbstractMetaTypeList &instantiations = containerType->instantiations(); - for (const AbstractMetaType *otherType : instantiations) - types << (otherType == instantiation ? impConv : getTypeName(otherType)); - - return containerType->typeEntry()->qualifiedCppName() + QLatin1Char('<') - + types.join(QLatin1String(", ")) + QLatin1String(" >"); -} - -// overloaddata.cpp -static QString msgCyclicDependency(const QString &funcName, const QString &graphName, - const OverloadData::MetaFunctionList &involvedConversions) -{ - QString result; - QTextStream str(&result); - str << "Cyclic dependency found on overloaddata for \"" << funcName - << "\" method! The graph boy saved the graph at \"" << QDir::toNativeSeparators(graphName) - << "\"."; - if (const int count = involvedConversions.size()) { - str << " Implicit conversions (" << count << "): "; - for (int i = 0; i < count; ++i) { - if (i) - str << ", \""; - str << involvedConversions.at(i)->signature() << '"'; - if (const AbstractMetaClass *c = involvedConversions.at(i)->implementingClass()) - str << '(' << c->name() << ')'; - } - } - return result; -} - -/** - * Topologically sort the overloads by implicit convertion order - * - * This avoids using an implicit conversion if there's an explicit - * overload for the convertible type. So, if there's an implicit convert - * like TargetType(ConvertibleType foo) and both are in the overload list, - * ConvertibleType is checked before TargetType. - * - * Side effects: Modifies m_nextOverloadData - */ -void OverloadData::sortNextOverloads() -{ - OverloadSortData sortData; - bool checkPyObject = false; - int pyobjectIndex = 0; - bool checkPySequence = false; - int pySeqIndex = 0; - bool checkQString = false; - int qstringIndex = 0; - bool checkQVariant = false; - int qvariantIndex = 0; - bool checkPyBuffer = false; - int pyBufferIndex = 0; - - // Primitive types that are not int, long, short, - // char and their respective unsigned counterparts. - QStringList nonIntegerPrimitives; - nonIntegerPrimitives << QLatin1String("float") << QLatin1String("double") - << QLatin1String("bool"); - - // Signed integer primitive types. - QStringList signedIntegerPrimitives; - signedIntegerPrimitives << QLatin1String("int") << QLatin1String("short") - << QLatin1String("long"); - - // sort the children overloads - for (OverloadData *ov : qAsConst(m_nextOverloadData)) - ov->sortNextOverloads(); - - if (m_nextOverloadData.size() <= 1) - return; - - // Populates the OverloadSortData object containing map and reverseMap, to map type names to ids, - // these ids will be used by the topological sort algorithm, because is easier and faster to work - // with graph sorting using integers. - for (OverloadData *ov : qAsConst(m_nextOverloadData)) { - sortData.mapType(ov); - - const QString typeName(getTypeName(ov)); - - if (!checkPyObject && typeName.contains(QLatin1String("PyObject"))) { - checkPyObject = true; - pyobjectIndex = sortData.lastProcessedItemId(); - } else if (!checkPySequence && typeName == QLatin1String("PySequence")) { - checkPySequence = true; - pySeqIndex = sortData.lastProcessedItemId(); - } else if (!checkPyBuffer && typeName == QLatin1String("PyBuffer")) { - checkPyBuffer = true; - pyBufferIndex = sortData.lastProcessedItemId(); - } else if (!checkQVariant && typeName == QLatin1String("QVariant")) { - checkQVariant = true; - qvariantIndex = sortData.lastProcessedItemId(); - } else if (!checkQString && typeName == QLatin1String("QString")) { - checkQString = true; - qstringIndex = sortData.lastProcessedItemId(); - } - - const AbstractMetaTypeList &instantiations = ov->argType()->instantiations(); - for (const AbstractMetaType *instantiation : instantiations) { - // Add dependencies for type instantiation of container. - QString typeName = getTypeName(instantiation); - sortData.mapType(typeName); - - // Build dependency for implicit conversion types instantiations for base container. - // For example, considering signatures "method(list<PointF>)" and "method(list<Point>)", - // and being PointF implicitly convertible from Point, an list<T> instantiation with T - // as Point must come before the PointF instantiation, or else list<Point> will never - // be called. In the case of primitive types, list<double> must come before list<int>. - if (instantiation->isPrimitive() && (signedIntegerPrimitives.contains(instantiation->name()))) { - for (const QString &primitive : qAsConst(nonIntegerPrimitives)) - sortData.mapType(getImplicitConversionTypeName(ov->argType(), instantiation, nullptr, primitive)); - } else { - const AbstractMetaFunctionList &funcs = m_generator->implicitConversions(instantiation); - for (const AbstractMetaFunction *function : funcs) - sortData.mapType(getImplicitConversionTypeName(ov->argType(), instantiation, function)); - } - } - } - - - // Create the graph of type dependencies based on implicit conversions. - Graph graph(sortData.reverseMap.count()); - // All C++ primitive types, add any forgotten type AT THE END OF THIS LIST! - const char *primitiveTypes[] = {"int", - "unsigned int", - "long", - "unsigned long", - "short", - "unsigned short", - "bool", - "unsigned char", - "char", - "float", - "double", - "const char*" - }; - const int numPrimitives = sizeof(primitiveTypes)/sizeof(const char *); - bool hasPrimitive[numPrimitives]; - for (int i = 0; i < numPrimitives; ++i) - hasPrimitive[i] = sortData.map.contains(QLatin1String(primitiveTypes[i])); - - if (checkPySequence && checkPyObject) - graph.addEdge(pySeqIndex, pyobjectIndex); - - QStringList classesWithIntegerImplicitConversion; - - MetaFunctionList involvedConversions; - - for (OverloadData *ov : qAsConst(m_nextOverloadData)) { - const AbstractMetaType *targetType = ov->argType(); - const QString targetTypeEntryName(getTypeName(ov)); - int targetTypeId = sortData.map[targetTypeEntryName]; - - // Process implicit conversions - const AbstractMetaFunctionList &functions = m_generator->implicitConversions(targetType); - for (AbstractMetaFunction *function : functions) { - QString convertibleType; - if (function->isConversionOperator()) - convertibleType = function->ownerClass()->typeEntry()->name(); - else - convertibleType = getTypeName(function->arguments().constFirst()->type()); - - if (convertibleType == QLatin1String("int") || convertibleType == QLatin1String("unsigned int")) - classesWithIntegerImplicitConversion << targetTypeEntryName; - - if (!sortData.map.contains(convertibleType)) - continue; - - int convertibleTypeId = sortData.map[convertibleType]; - - // If a reverse pair already exists, remove it. Probably due to the - // container check (This happened to QVariant and QHash) - graph.removeEdge(targetTypeId, convertibleTypeId); - graph.addEdge(convertibleTypeId, targetTypeId); - involvedConversions.append(function); - } - - // Process inheritance relationships - if (targetType->isValue() || targetType->isObject()) { - const AbstractMetaClass *metaClass = AbstractMetaClass::findClass(m_generator->classes(), targetType->typeEntry()); - const AbstractMetaClassList &ancestors = m_generator->getAllAncestors(metaClass); - for (const AbstractMetaClass *ancestor : ancestors) { - QString ancestorTypeName = ancestor->typeEntry()->name(); - if (!sortData.map.contains(ancestorTypeName)) - continue; - int ancestorTypeId = sortData.map[ancestorTypeName]; - graph.removeEdge(ancestorTypeId, targetTypeId); - graph.addEdge(targetTypeId, ancestorTypeId); - } - } - - // Process template instantiations - const AbstractMetaTypeList &instantiations = targetType->instantiations(); - for (const AbstractMetaType *instantiation : instantiations) { - if (sortData.map.contains(getTypeName(instantiation))) { - int convertible = sortData.map[getTypeName(instantiation)]; - - if (!graph.containsEdge(targetTypeId, convertible)) // Avoid cyclic dependency. - graph.addEdge(convertible, targetTypeId); - - if (instantiation->isPrimitive() && (signedIntegerPrimitives.contains(instantiation->name()))) { - for (const QString &primitive : qAsConst(nonIntegerPrimitives)) { - QString convertibleTypeName = getImplicitConversionTypeName(ov->argType(), instantiation, nullptr, primitive); - if (!graph.containsEdge(targetTypeId, sortData.map[convertibleTypeName])) // Avoid cyclic dependency. - graph.addEdge(sortData.map[convertibleTypeName], targetTypeId); - } - - } else { - const AbstractMetaFunctionList &funcs = m_generator->implicitConversions(instantiation); - for (const AbstractMetaFunction *function : funcs) { - QString convertibleTypeName = getImplicitConversionTypeName(ov->argType(), instantiation, function); - if (!graph.containsEdge(targetTypeId, sortData.map[convertibleTypeName])) { // Avoid cyclic dependency. - graph.addEdge(sortData.map[convertibleTypeName], targetTypeId); - involvedConversions.append(function); - } - } - } - } - } - - - if ((checkPySequence || checkPyObject || checkPyBuffer) - && !targetTypeEntryName.contains(QLatin1String("PyObject")) - && !targetTypeEntryName.contains(QLatin1String("PyBuffer")) - && !targetTypeEntryName.contains(QLatin1String("PySequence"))) { - if (checkPySequence) { - // PySequence will be checked after all more specific types, but before PyObject. - graph.addEdge(targetTypeId, pySeqIndex); - } else if (checkPyBuffer) { - // PySequence will be checked after all more specific types, but before PyObject. - graph.addEdge(targetTypeId, pyBufferIndex); - } else { - // Add dependency on PyObject, so its check is the last one (too generic). - graph.addEdge(targetTypeId, pyobjectIndex); - } - } else if (checkQVariant && targetTypeEntryName != QLatin1String("QVariant")) { - if (!graph.containsEdge(qvariantIndex, targetTypeId)) // Avoid cyclic dependency. - graph.addEdge(targetTypeId, qvariantIndex); - } else if (checkQString && ShibokenGenerator::isPointer(ov->argType()) - && targetTypeEntryName != QLatin1String("QString") - && targetTypeEntryName != QLatin1String("QByteArray") - && (!checkPyObject || targetTypeId != pyobjectIndex)) { - if (!graph.containsEdge(qstringIndex, targetTypeId)) // Avoid cyclic dependency. - graph.addEdge(targetTypeId, qstringIndex); - } - - if (targetType->isEnum()) { - // Enum values must precede primitive types. - for (int i = 0; i < numPrimitives; ++i) { - if (hasPrimitive[i]) - graph.addEdge(targetTypeId, sortData.map[QLatin1String(primitiveTypes[i])]); - } - } - } - - // QByteArray args need to be checked after QString args - if (sortData.map.contains(QLatin1String("QString")) && sortData.map.contains(QLatin1String("QByteArray"))) - graph.addEdge(sortData.map[QLatin1String("QString")], sortData.map[QLatin1String("QByteArray")]); - - for (OverloadData *ov : qAsConst(m_nextOverloadData)) { - const AbstractMetaType *targetType = ov->argType(); - if (!targetType->isEnum()) - continue; - - QString targetTypeEntryName = getTypeName(targetType); - // Enum values must precede types implicitly convertible from "int" or "unsigned int". - for (const QString &implicitFromInt : qAsConst(classesWithIntegerImplicitConversion)) - graph.addEdge(sortData.map[targetTypeEntryName], sortData.map[implicitFromInt]); - } - - - // Special case for double(int i) (not tracked by m_generator->implicitConversions - for (const QString &signedIntegerName : qAsConst(signedIntegerPrimitives)) { - if (sortData.map.contains(signedIntegerName)) { - for (const QString &nonIntegerName : qAsConst(nonIntegerPrimitives)) { - if (sortData.map.contains(nonIntegerName)) - graph.addEdge(sortData.map[nonIntegerName], sortData.map[signedIntegerName]); - } - } - } - - // sort the overloads topologically based on the dependency graph. - const auto unmappedResult = graph.topologicalSort(); - if (unmappedResult.isEmpty()) { - QString funcName = referenceFunction()->name(); - if (referenceFunction()->ownerClass()) - funcName.prepend(referenceFunction()->ownerClass()->name() + QLatin1Char('.')); - - // Dump overload graph - QString graphName = QDir::tempPath() + QLatin1Char('/') + funcName + QLatin1String(".dot"); - QHash<int, QString> nodeNames; - for (auto it = sortData.map.cbegin(), end = sortData.map.cend(); it != end; ++it) - nodeNames.insert(it.value(), it.key()); - graph.dumpDot(nodeNames, graphName); - qCWarning(lcShiboken).noquote() << qPrintable(msgCyclicDependency(funcName, graphName, involvedConversions)); - } - - m_nextOverloadData.clear(); - for (int i : unmappedResult) { - if (!sortData.reverseMap[i]) - continue; - m_nextOverloadData << sortData.reverseMap[i]; - } -} - -/** - * Root constructor for OverloadData - * - * This constructor receives the list of overloads for a given function and iterates generating - * the graph of OverloadData instances. Each OverloadData instance references an argument/type - * combination. - * - * Example: - * addStuff(double, PyObject *) - * addStuff(double, int) - * - * Given these two overloads, there will be the following graph: - * - * addStuff - double - PyObject * - * \- int - * - */ -OverloadData::OverloadData(const AbstractMetaFunctionList &overloads, const ShibokenGenerator *generator) - : m_minArgs(256), m_maxArgs(0), m_argPos(-1), m_argType(nullptr), - m_headOverloadData(this), m_previousOverloadData(nullptr), m_generator(generator) -{ - for (const AbstractMetaFunction *func : overloads) { - m_overloads.append(func); - int argSize = func->arguments().size() - numberOfRemovedArguments(func); - if (m_minArgs > argSize) - m_minArgs = argSize; - else if (m_maxArgs < argSize) - m_maxArgs = argSize; - OverloadData *currentOverloadData = this; - const AbstractMetaArgumentList &arguments = func->arguments(); - for (const AbstractMetaArgument *arg : arguments) { - if (func->argumentRemoved(arg->argumentIndex() + 1)) - continue; - currentOverloadData = currentOverloadData->addOverloadData(func, arg); - } - } - - // Sort the overload possibilities so that the overload decisor code goes for the most - // important cases first, based on the topological order of the implicit conversions - sortNextOverloads(); - - // Fix minArgs - if (minArgs() > maxArgs()) - m_headOverloadData->m_minArgs = maxArgs(); -} - -OverloadData::OverloadData(OverloadData *headOverloadData, const AbstractMetaFunction *func, - const AbstractMetaType *argType, int argPos) - : m_minArgs(256), m_maxArgs(0), m_argPos(argPos), m_argType(argType), - m_headOverloadData(headOverloadData), m_previousOverloadData(nullptr), - m_generator(nullptr) -{ - if (func) - this->addOverload(func); -} - -void OverloadData::addOverload(const AbstractMetaFunction *func) -{ - int origNumArgs = func->arguments().size(); - int removed = numberOfRemovedArguments(func); - int numArgs = origNumArgs - removed; - - if (numArgs > m_headOverloadData->m_maxArgs) - m_headOverloadData->m_maxArgs = numArgs; - - if (numArgs < m_headOverloadData->m_minArgs) - m_headOverloadData->m_minArgs = numArgs; - - for (int i = 0; m_headOverloadData->m_minArgs > 0 && i < origNumArgs; i++) { - if (func->argumentRemoved(i + 1)) - continue; - if (func->arguments().at(i)->hasDefaultValueExpression()) { - int fixedArgIndex = i - removed; - if (fixedArgIndex < m_headOverloadData->m_minArgs) - m_headOverloadData->m_minArgs = fixedArgIndex; - } - } - - m_overloads.append(func); -} - -OverloadData *OverloadData::addOverloadData(const AbstractMetaFunction *func, - const AbstractMetaArgument *arg) -{ - const AbstractMetaType *argType = arg->type(); - OverloadData *overloadData = nullptr; - if (!func->isOperatorOverload()) { - for (OverloadData *tmp : qAsConst(m_nextOverloadData)) { - // TODO: 'const char *', 'char *' and 'char' will have the same TypeEntry? - - // If an argument have a type replacement, then we should create a new overloaddata - // for it, unless the next argument also have a identical type replacement. - QString replacedArg = func->typeReplaced(tmp->m_argPos + 1); - bool argsReplaced = !replacedArg.isEmpty() || !tmp->m_argTypeReplaced.isEmpty(); - if ((!argsReplaced && typesAreEqual(tmp->m_argType, argType)) - || (argsReplaced && replacedArg == tmp->argumentTypeReplaced())) { - tmp->addOverload(func); - overloadData = tmp; - } - } - } - - if (!overloadData) { - overloadData = new OverloadData(m_headOverloadData, func, argType, m_argPos + 1); - overloadData->m_previousOverloadData = this; - overloadData->m_generator = this->m_generator; - QString typeReplaced = func->typeReplaced(arg->argumentIndex() + 1); - - if (!typeReplaced.isEmpty()) - overloadData->m_argTypeReplaced = typeReplaced; - m_nextOverloadData.append(overloadData); - } - - return overloadData; -} - -QStringList OverloadData::returnTypes() const -{ - QSet<QString> retTypes; - for (const AbstractMetaFunction *func : m_overloads) { - if (!func->typeReplaced(0).isEmpty()) - retTypes << func->typeReplaced(0); - else if (func->type() && !func->argumentRemoved(0)) - retTypes << func->type()->cppSignature(); - else - retTypes << QLatin1String("void"); - } - return retTypes.values(); -} - -bool OverloadData::hasNonVoidReturnType() const -{ - QStringList retTypes = returnTypes(); - return !retTypes.contains(QLatin1String("void")) || retTypes.size() > 1; -} - -bool OverloadData::hasVarargs() const -{ - for (const AbstractMetaFunction *func : m_overloads) { - AbstractMetaArgumentList args = func->arguments(); - if (args.size() > 1 && args.constLast()->type()->isVarargs()) - return true; - } - return false; -} - -bool OverloadData::hasAllowThread() const -{ - for (const AbstractMetaFunction *func : m_overloads) { - if (func->allowThread()) - return true; - } - return false; -} - -bool OverloadData::hasStaticFunction(const AbstractMetaFunctionList &overloads) -{ - for (const AbstractMetaFunction *func : qAsConst(overloads)) { - if (func->isStatic()) - return true; - } - return false; -} - -bool OverloadData::hasStaticFunction() const -{ - for (const AbstractMetaFunction *func : m_overloads) { - if (func->isStatic()) - return true; - } - return false; -} - -bool OverloadData::hasInstanceFunction(const AbstractMetaFunctionList &overloads) -{ - for (const AbstractMetaFunction *func : qAsConst(overloads)) { - if (!func->isStatic()) - return true; - } - return false; -} - -bool OverloadData::hasInstanceFunction() const -{ - for (const AbstractMetaFunction *func : m_overloads) { - if (!func->isStatic()) - return true; - } - return false; -} - -bool OverloadData::hasStaticAndInstanceFunctions(const AbstractMetaFunctionList &overloads) -{ - return OverloadData::hasStaticFunction(overloads) && OverloadData::hasInstanceFunction(overloads); -} - -bool OverloadData::hasStaticAndInstanceFunctions() const -{ - return OverloadData::hasStaticFunction() && OverloadData::hasInstanceFunction(); -} - -const AbstractMetaFunction *OverloadData::referenceFunction() const -{ - return m_overloads.constFirst(); -} - -const AbstractMetaArgument *OverloadData::argument(const AbstractMetaFunction *func) const -{ - if (isHeadOverloadData() || !m_overloads.contains(func)) - return nullptr; - - int argPos = 0; - int removed = 0; - for (int i = 0; argPos <= m_argPos; i++) { - if (func->argumentRemoved(i + 1)) - removed++; - else - argPos++; - } - - return func->arguments().at(m_argPos + removed); -} - -OverloadDataList OverloadData::overloadDataOnPosition(OverloadData *overloadData, int argPos) const -{ - OverloadDataList overloadDataList; - if (overloadData->argPos() == argPos) { - overloadDataList.append(overloadData); - } else if (overloadData->argPos() < argPos) { - const OverloadDataList &data = overloadData->nextOverloadData(); - for (OverloadData *pd : data) - overloadDataList += overloadDataOnPosition(pd, argPos); - } - return overloadDataList; -} - -OverloadDataList OverloadData::overloadDataOnPosition(int argPos) const -{ - OverloadDataList overloadDataList; - overloadDataList += overloadDataOnPosition(m_headOverloadData, argPos); - return overloadDataList; -} - -bool OverloadData::nextArgumentHasDefaultValue() const -{ - for (OverloadData *overloadData : m_nextOverloadData) { - if (overloadData->getFunctionWithDefaultValue()) - return true; - } - return false; -} - -static OverloadData *_findNextArgWithDefault(OverloadData *overloadData) -{ - if (overloadData->getFunctionWithDefaultValue()) - return overloadData; - - OverloadData *result = nullptr; - const OverloadDataList &data = overloadData->nextOverloadData(); - for (OverloadData *odata : data) { - OverloadData *tmp = _findNextArgWithDefault(odata); - if (!result || (tmp && result->argPos() > tmp->argPos())) - result = tmp; - } - return result; -} - -OverloadData *OverloadData::findNextArgWithDefault() -{ - return _findNextArgWithDefault(this); -} - -bool OverloadData::isFinalOccurrence(const AbstractMetaFunction *func) const -{ - for (const OverloadData *pd : m_nextOverloadData) { - if (pd->overloads().contains(func)) - return false; - } - return true; -} - -OverloadData::MetaFunctionList OverloadData::overloadsWithoutRepetition() const -{ - MetaFunctionList overloads = m_overloads; - for (const AbstractMetaFunction *func : m_overloads) { - if (func->minimalSignature().endsWith(QLatin1String("const"))) - continue; - for (const AbstractMetaFunction *f : qAsConst(overloads)) { - if ((func->minimalSignature() + QLatin1String("const")) == f->minimalSignature()) { - overloads.removeOne(f); - break; - } - } - } - return overloads; -} - -const AbstractMetaFunction *OverloadData::getFunctionWithDefaultValue() const -{ - for (const AbstractMetaFunction *func : m_overloads) { - int removedArgs = 0; - for (int i = 0; i <= m_argPos + removedArgs; i++) { - if (func->argumentRemoved(i + 1)) - removedArgs++; - } - if (func->arguments().at(m_argPos + removedArgs)->hasDefaultValueExpression()) - return func; - } - return nullptr; -} - -QVector<int> OverloadData::invalidArgumentLengths() const -{ - QSet<int> validArgLengths; - - for (const AbstractMetaFunction *func : qAsConst(m_headOverloadData->m_overloads)) { - const AbstractMetaArgumentList args = func->arguments(); - int offset = 0; - for (int i = 0; i < args.size(); ++i) { - if (func->argumentRemoved(i+1)) { - offset++; - } else { - if (args.at(i)->hasDefaultValueExpression()) - validArgLengths << i-offset; - } - } - validArgLengths << args.size() - offset; - } - - QVector<int> invalidArgLengths; - for (int i = minArgs() + 1; i < maxArgs(); i++) { - if (!validArgLengths.contains(i)) - invalidArgLengths.append(i); - } - - return invalidArgLengths; -} - -int OverloadData::numberOfRemovedArguments(const AbstractMetaFunction *func, int finalArgPos) -{ - int removed = 0; - if (finalArgPos < 0) { - for (int i = 0; i < func->arguments().size(); i++) { - if (func->argumentRemoved(i + 1)) - removed++; - } - } else { - for (int i = 0; i < finalArgPos + removed; i++) { - if (func->argumentRemoved(i + 1)) - removed++; - } - } - return removed; -} - -QPair<int, int> OverloadData::getMinMaxArguments(const AbstractMetaFunctionList &overloads) -{ - int minArgs = 10000; - int maxArgs = 0; - for (const AbstractMetaFunction *func : overloads) { - int origNumArgs = func->arguments().size(); - int removed = numberOfRemovedArguments(func); - int numArgs = origNumArgs - removed; - if (maxArgs < numArgs) - maxArgs = numArgs; - if (minArgs > numArgs) - minArgs = numArgs; - for (int j = 0; j < origNumArgs; j++) { - if (func->argumentRemoved(j + 1)) - continue; - int fixedArgIndex = j - removed; - if (fixedArgIndex < minArgs && func->arguments().at(j)->hasDefaultValueExpression()) - minArgs = fixedArgIndex; - } - } - return {minArgs, maxArgs}; -} - -bool OverloadData::isSingleArgument(const AbstractMetaFunctionList &overloads) -{ - bool singleArgument = true; - for (const AbstractMetaFunction *func : overloads) { - if (func->arguments().size() - numberOfRemovedArguments(func) != 1) { - singleArgument = false; - break; - } - } - return singleArgument; -} - -void OverloadData::dumpGraph(const QString &filename) const -{ - QFile file(filename); - if (file.open(QFile::WriteOnly)) { - QTextStream s(&file); - s << m_headOverloadData->dumpGraph(); - } -} - -static inline QString toHtml(QString s) -{ - s.replace(QLatin1Char('<'), QLatin1String("<")); - s.replace(QLatin1Char('>'), QLatin1String(">")); - s.replace(QLatin1Char('&'), QLatin1String("&")); - return s; -} - -QString OverloadData::dumpGraph() const -{ - Indentor INDENT; - Indentation indent(INDENT); - QString result; - QTextStream s(&result); - if (m_argPos == -1) { - const AbstractMetaFunction *rfunc = referenceFunction(); - s << "digraph OverloadedFunction {\n"; - s << INDENT << "graph [fontsize=12 fontname=freemono labelloc=t splines=true overlap=false rankdir=LR];\n"; - - // Shows all function signatures - s << "legend [fontsize=9 fontname=freemono shape=rect label=\""; - for (const AbstractMetaFunction *func : m_overloads) { - s << "f" << functionNumber(func) << " : "; - if (func->type()) - s << toHtml(func->type()->cppSignature()); - else - s << "void"; - s << ' ' << toHtml(func->minimalSignature()) << "\\l"; - } - s << "\"];\n"; - - // Function box title - s << INDENT << '"' << rfunc->name() << "\" [shape=plaintext style=\"filled,bold\" margin=0 fontname=freemono fillcolor=white penwidth=1 "; - s << "label=<<table border=\"0\" cellborder=\"0\" cellpadding=\"3\" bgcolor=\"white\">"; - s << "<tr><td bgcolor=\"black\" align=\"center\" cellpadding=\"6\" colspan=\"2\"><font color=\"white\">"; - if (rfunc->ownerClass()) - s << rfunc->ownerClass()->name() << "::"; - s << toHtml(rfunc->name()) << "</font>"; - if (rfunc->isVirtual()) { - s << "<br/><font color=\"white\" point-size=\"10\"><<"; - if (rfunc->isAbstract()) - s << "pure "; - s << "virtual>></font>"; - } - s << "</td></tr>"; - - // Function return type - s << "<tr><td bgcolor=\"gray\" align=\"right\">original type</td><td bgcolor=\"gray\" align=\"left\">"; - if (rfunc->type()) - s << toHtml(rfunc->type()->cppSignature()); - else - s << "void"; - s << "</td></tr>"; - - // Shows type changes for all function signatures - for (const AbstractMetaFunction *func : m_overloads) { - if (func->typeReplaced(0).isEmpty()) - continue; - s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << functionNumber(func); - s << "-type</td><td bgcolor=\"gray\" align=\"left\">"; - s << toHtml(func->typeReplaced(0)) << "</td></tr>"; - } - - // Minimum and maximum number of arguments - s << "<tr><td bgcolor=\"gray\" align=\"right\">minArgs</td><td bgcolor=\"gray\" align=\"left\">"; - s << minArgs() << "</td></tr>"; - s << "<tr><td bgcolor=\"gray\" align=\"right\">maxArgs</td><td bgcolor=\"gray\" align=\"left\">"; - s << maxArgs() << "</td></tr>"; - - if (rfunc->ownerClass()) { - if (rfunc->implementingClass() != rfunc->ownerClass()) - s << "<tr><td align=\"right\">implementor</td><td align=\"left\">" << rfunc->implementingClass()->name() << "</td></tr>"; - if (rfunc->declaringClass() != rfunc->ownerClass() && rfunc->declaringClass() != rfunc->implementingClass()) - s << "<tr><td align=\"right\">declarator</td><td align=\"left\">" << rfunc->declaringClass()->name() << "</td></tr>"; - } - - // Overloads for the signature to present point - s << "<tr><td bgcolor=\"gray\" align=\"right\">overloads</td><td bgcolor=\"gray\" align=\"left\">"; - for (const AbstractMetaFunction *func : m_overloads) - s << 'f' << functionNumber(func) << ' '; - s << "</td></tr>"; - - s << "</table>> ];\n"; - - for (const OverloadData *pd : m_nextOverloadData) - s << INDENT << '"' << rfunc->name() << "\" -> " << pd->dumpGraph(); - - s << "}\n"; - } else { - QString argId = QLatin1String("arg_") + QString::number(quintptr(this)); - s << argId << ";\n"; - - s << INDENT << '"' << argId << "\" [shape=\"plaintext\" style=\"filled,bold\" margin=\"0\" fontname=\"freemono\" fillcolor=\"white\" penwidth=1 "; - s << "label=<<table border=\"0\" cellborder=\"0\" cellpadding=\"3\" bgcolor=\"white\">"; - - // Argument box title - s << "<tr><td bgcolor=\"black\" align=\"left\" cellpadding=\"2\" colspan=\"2\">"; - s << "<font color=\"white\" point-size=\"11\">arg #" << argPos() << "</font></td></tr>"; - - // Argument type information - QString type = hasArgumentTypeReplace() ? argumentTypeReplaced() : argType()->cppSignature(); - s << "<tr><td bgcolor=\"gray\" align=\"right\">type</td><td bgcolor=\"gray\" align=\"left\">"; - s << toHtml(type) << "</td></tr>"; - if (hasArgumentTypeReplace()) { - s << "<tr><td bgcolor=\"gray\" align=\"right\">orig. type</td><td bgcolor=\"gray\" align=\"left\">"; - s << toHtml(argType()->cppSignature()) << "</td></tr>"; - } - - // Overloads for the signature to present point - s << "<tr><td bgcolor=\"gray\" align=\"right\">overloads</td><td bgcolor=\"gray\" align=\"left\">"; - for (const AbstractMetaFunction *func : m_overloads) - s << 'f' << functionNumber(func) << ' '; - s << "</td></tr>"; - - // Show default values (original and modified) for various functions - for (const AbstractMetaFunction *func : m_overloads) { - const AbstractMetaArgument *arg = argument(func); - if (!arg) - continue; - QString argDefault = arg->defaultValueExpression(); - if (!argDefault.isEmpty() || - argDefault != arg->originalDefaultValueExpression()) { - s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << functionNumber(func); - s << "-default</td><td bgcolor=\"gray\" align=\"left\">"; - s << argDefault << "</td></tr>"; - } - if (argDefault != arg->originalDefaultValueExpression()) { - s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << functionNumber(func); - s << "-orig-default</td><td bgcolor=\"gray\" align=\"left\">"; - s << arg->originalDefaultValueExpression() << "</td></tr>"; - } - } - - s << "</table>>];\n"; - - for (const OverloadData *pd : m_nextOverloadData) - s << INDENT << argId << " -> " << pd->dumpGraph(); - } - return result; -} - -int OverloadData::functionNumber(const AbstractMetaFunction *func) const -{ - return m_headOverloadData->m_overloads.indexOf(func); -} - -OverloadData::~OverloadData() -{ - while (!m_nextOverloadData.isEmpty()) - delete m_nextOverloadData.takeLast(); -} - -bool OverloadData::hasArgumentTypeReplace() const -{ - return !m_argTypeReplaced.isEmpty(); -} - -QString OverloadData::argumentTypeReplaced() const -{ - return m_argTypeReplaced; -} - -bool OverloadData::hasArgumentWithDefaultValue(const AbstractMetaFunctionList &overloads) -{ - if (OverloadData::getMinMaxArguments(overloads).second == 0) - return false; - for (const AbstractMetaFunction *func : overloads) { - if (hasArgumentWithDefaultValue(func)) - return true; - } - return false; -} - -bool OverloadData::hasArgumentWithDefaultValue() const -{ - if (maxArgs() == 0) - return false; - for (const AbstractMetaFunction *func : m_overloads) { - if (hasArgumentWithDefaultValue(func)) - return true; - } - return false; -} - -bool OverloadData::hasArgumentWithDefaultValue(const AbstractMetaFunction *func) -{ - const AbstractMetaArgumentList &arguments = func->arguments(); - for (const AbstractMetaArgument *arg : arguments) { - if (func->argumentRemoved(arg->argumentIndex() + 1)) - continue; - if (arg->hasDefaultValueExpression()) - return true; - } - return false; -} - -AbstractMetaArgumentList OverloadData::getArgumentsWithDefaultValues(const AbstractMetaFunction *func) -{ - AbstractMetaArgumentList args; - const AbstractMetaArgumentList &arguments = func->arguments(); - for (AbstractMetaArgument *arg : arguments) { - if (!arg->hasDefaultValueExpression() - || func->argumentRemoved(arg->argumentIndex() + 1)) - continue; - args << arg; - } - return args; -} - -#ifndef QT_NO_DEBUG_STREAM -void OverloadData::formatDebug(QDebug &d) const -{ - const int count = m_overloads.size(); - d << "argType=" << m_argType << ", minArgs=" << m_minArgs << ", maxArgs=" << m_maxArgs - << ", argPos=" << m_argPos << ", argTypeReplaced=\"" << m_argTypeReplaced - << "\", overloads[" << count << "]=("; - const int oldVerbosity = d.verbosity(); - d.setVerbosity(3); - for (int i = 0; i < count; ++i) { - if (i) - d << '\n'; - d << m_overloads.at(i); - } - d << ')'; - d.setVerbosity(oldVerbosity); -} - -QDebug operator<<(QDebug d, const OverloadData *od) -{ - QDebugStateSaver saver(d); - d.noquote(); - d.nospace(); - d << "OverloadData("; - if (od) - od->formatDebug(d); - else - d << '0'; - d << ')'; - return d; -} -#endif // !QT_NO_DEBUG_STREAM |