diff options
Diffstat (limited to 'sources/shiboken6/generator/generator.cpp')
-rw-r--r-- | sources/shiboken6/generator/generator.cpp | 972 |
1 files changed, 972 insertions, 0 deletions
diff --git a/sources/shiboken6/generator/generator.cpp b/sources/shiboken6/generator/generator.cpp new file mode 100644 index 000000000..72a5b9a2e --- /dev/null +++ b/sources/shiboken6/generator/generator.cpp @@ -0,0 +1,972 @@ +/**************************************************************************** +** +** 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 "generator.h" +#include "ctypenames.h" +#include "abstractmetalang.h" +#include "parser/codemodel.h" +#include "messages.h" +#include "reporthandler.h" +#include "fileout.h" +#include "apiextractor.h" +#include "typesystem.h" + +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QRegularExpression> +#include <QDebug> +#include <typedatabase.h> + +/** + * DefaultValue is used for storing default values of types for which code is + * generated in different contexts: + * + * Context | Example: "Class *" | Example: "Class" with default Constructor + * --------------------+-------------------------------+------------------------------------------ + * Variable | var{nullptr}; | var; + * initializations | | + * --------------------+-------------------------------+------------------------------------------ + * Return values | return nullptr; | return {} + * --------------------+-------------------------------+------------------------------------------ + * constructor | static_cast<Class *>(nullptr) | Class() + * arguments lists | | + * (recursive, precise | | + * matching). | | + */ + +DefaultValue::DefaultValue(Type t, QString value) : + m_type(t), m_value(std::move(value)) +{ +} + +DefaultValue::DefaultValue(QString customValue) : + m_type(Custom), m_value(std::move(customValue)) +{ +} + +QString DefaultValue::returnValue() const +{ + switch (m_type) { + case DefaultValue::Error: + return QLatin1String("#error"); + case DefaultValue::Boolean: + return QLatin1String("false"); + case DefaultValue::CppScalar: + return QLatin1String("0"); + case DefaultValue::Custom: + case DefaultValue::Enum: + return m_value; + case DefaultValue::Pointer: + return QLatin1String("nullptr"); + case DefaultValue::Void: + return QString(); + case DefaultValue::DefaultConstructorWithDefaultValues: + return m_value + QLatin1String("()"); + case DefaultValue::DefaultConstructor: + break; + } + return QLatin1String("{}"); +} + +QString DefaultValue::initialization() const +{ + switch (m_type) { + case DefaultValue::Error: + return QLatin1String("#error"); + case DefaultValue::Boolean: + return QLatin1String("{false}"); + case DefaultValue::CppScalar: + return QLatin1String("{0}"); + case DefaultValue::Custom: + return QLatin1String(" = ") + m_value; + case DefaultValue::Enum: + return QLatin1Char('{') + m_value + QLatin1Char('}'); + case DefaultValue::Pointer: + return QLatin1String("{nullptr}"); + case DefaultValue::Void: + Q_ASSERT(false); + break; + case DefaultValue::DefaultConstructor: + case DefaultValue::DefaultConstructorWithDefaultValues: + break; + } + return QString(); +} + +QString DefaultValue::constructorParameter() const +{ + switch (m_type) { + case DefaultValue::Error: + return QLatin1String("#error"); + case DefaultValue::Boolean: + return QLatin1String("false"); + case DefaultValue::CppScalar: { + // PYSIDE-846: Use static_cast in case of "unsigned long" and similar + const QString cast = m_value.contains(QLatin1Char(' ')) + ? QLatin1String("static_cast<") + m_value + QLatin1Char('>') + : m_value; + return cast + QLatin1String("(0)"); + } + case DefaultValue::Custom: + case DefaultValue::Enum: + return m_value; + case DefaultValue::Pointer: + // Be precise here to be able to differentiate between constructors + // taking different pointer types, cf + // QTreeWidgetItemIterator(QTreeWidget *) and + // QTreeWidgetItemIterator(QTreeWidgetItemIterator *). + return QLatin1String("static_cast<") + m_value + QLatin1String("*>(nullptr)"); + case DefaultValue::Void: + Q_ASSERT(false); + break; + case DefaultValue::DefaultConstructor: + case DefaultValue::DefaultConstructorWithDefaultValues: + break; + } + return m_value + QLatin1String("()"); +} + +QString GeneratorContext::smartPointerWrapperName() const +{ + Q_ASSERT(m_type == SmartPointer); + return m_preciseClassType.cppSignature(); +} + +struct Generator::GeneratorPrivate +{ + const ApiExtractor *apiextractor = nullptr; + QString outDir; + // License comment + QString licenseComment; + QString moduleName; + QStringList instantiatedContainersNames; + QVector<AbstractMetaType> instantiatedContainers; + QVector<AbstractMetaType> instantiatedSmartPointers; + AbstractMetaClassList m_invisibleTopNamespaces; +}; + +Generator::Generator() : m_d(new GeneratorPrivate) +{ +} + +Generator::~Generator() +{ + delete m_d; +} + +bool Generator::setup(const ApiExtractor &extractor) +{ + m_d->apiextractor = &extractor; + const auto moduleEntry = TypeDatabase::instance()->defaultTypeSystemType(); + if (!moduleEntry || !moduleEntry->generateCode()) { + qCWarning(lcShiboken) << "Couldn't find the package name!!"; + return false; + } + + collectInstantiatedContainersAndSmartPointers(); + + for (auto c : classes()) { + if (c->enclosingClass() == nullptr && c->isInvisibleNamespace()) { + m_d->m_invisibleTopNamespaces.append(c); + c->invisibleNamespaceRecursion([&](AbstractMetaClass *ic) { + m_d->m_invisibleTopNamespaces.append(ic); + }); + } + } + + return doSetup(); +} + +QString Generator::getSimplifiedContainerTypeName(const AbstractMetaType &type) +{ + const QString signature = type.cppSignature(); + if (!type.typeEntry()->isContainer() && !type.typeEntry()->isSmartPointer()) + return signature; + QString typeName = signature; + if (type.isConstant()) + typeName.remove(0, sizeof("const ") / sizeof(char) - 1); + switch (type.referenceType()) { + case NoReference: + break; + case LValueReference: + typeName.chop(1); + break; + case RValueReference: + typeName.chop(2); + break; + } + while (typeName.endsWith(QLatin1Char('*')) || typeName.endsWith(QLatin1Char(' '))) + typeName.chop(1); + return typeName; +} + +// Strip a "const QSharedPtr<const Foo> &" or similar to "QSharedPtr<Foo>" (PYSIDE-1016/454) +AbstractMetaType canonicalSmartPtrInstantiation(const AbstractMetaType &type) +{ + const AbstractMetaTypeList &instantiations = type.instantiations(); + Q_ASSERT(instantiations.size() == 1); + const bool needsFix = type.isConstant() || type.referenceType() != NoReference; + const bool pointeeNeedsFix = instantiations.constFirst().isConstant(); + if (!needsFix && !pointeeNeedsFix) + return type; + auto fixedType = type; + fixedType.setReferenceType(NoReference); + fixedType.setConstant(false); + if (pointeeNeedsFix) { + auto fixedPointeeType = instantiations.constFirst(); + fixedPointeeType.setConstant(false); + fixedType.setInstantiations(AbstractMetaTypeList(1, fixedPointeeType)); + } + return fixedType; +} + +static inline const TypeEntry *pointeeTypeEntry(const AbstractMetaType &smartPtrType) +{ + return smartPtrType.instantiations().constFirst().typeEntry(); +} + +void Generator::addInstantiatedContainersAndSmartPointers(const AbstractMetaType &type, + const QString &context) +{ + for (const auto &t : type.instantiations()) + addInstantiatedContainersAndSmartPointers(t, context); + const auto typeEntry = type.typeEntry(); + const bool isContainer = typeEntry->isContainer(); + if (!isContainer + && !(typeEntry->isSmartPointer() && typeEntry->generateCode())) { + return; + } + if (type.hasTemplateChildren()) { + QString piece = isContainer ? QStringLiteral("container") : QStringLiteral("smart pointer"); + QString warning = + QString::fromLatin1("Skipping instantiation of %1 '%2' because it has template" + " arguments.").arg(piece, type.originalTypeDescription()); + if (!context.isEmpty()) + warning.append(QStringLiteral(" Calling context: %1").arg(context)); + + qCWarning(lcShiboken).noquote().nospace() << warning; + return; + + } + QString typeName = getSimplifiedContainerTypeName(type); + if (isContainer) { + if (!m_d->instantiatedContainersNames.contains(typeName)) { + m_d->instantiatedContainersNames.append(typeName); + m_d->instantiatedContainers.append(type); + } + } else { + // Is smart pointer. Check if the (const?) pointee is already known + auto pt = pointeeTypeEntry(type); + const bool present = + std::any_of(m_d->instantiatedSmartPointers.cbegin(), m_d->instantiatedSmartPointers.cend(), + [pt] (const AbstractMetaType &t) { + return pointeeTypeEntry(t) == pt; + }); + if (!present) + m_d->instantiatedSmartPointers.append(canonicalSmartPtrInstantiation(type)); + } + +} + +void Generator::collectInstantiatedContainersAndSmartPointers(const AbstractMetaFunction *func) +{ + addInstantiatedContainersAndSmartPointers(func->type(), func->signature()); + const AbstractMetaArgumentList &arguments = func->arguments(); + for (const AbstractMetaArgument &arg : arguments) + addInstantiatedContainersAndSmartPointers(arg.type(), func->signature()); +} + +void Generator::collectInstantiatedContainersAndSmartPointers(const AbstractMetaClass *metaClass) +{ + if (!metaClass->typeEntry()->generateCode()) + return; + const AbstractMetaFunctionList &funcs = metaClass->functions(); + for (const AbstractMetaFunction *func : funcs) + collectInstantiatedContainersAndSmartPointers(func); + const AbstractMetaFieldList &fields = metaClass->fields(); + for (const AbstractMetaField *field : fields) + addInstantiatedContainersAndSmartPointers(field->type(), field->name()); + const AbstractMetaClassList &innerClasses = metaClass->innerClasses(); + for (AbstractMetaClass *innerClass : innerClasses) + collectInstantiatedContainersAndSmartPointers(innerClass); +} + +void Generator::collectInstantiatedContainersAndSmartPointers() +{ + for (const AbstractMetaFunction *func : globalFunctions()) + collectInstantiatedContainersAndSmartPointers(func); + for (const AbstractMetaClass *metaClass : classes()) + collectInstantiatedContainersAndSmartPointers(metaClass); +} + +QVector<AbstractMetaType> Generator::instantiatedContainers() const +{ + return m_d->instantiatedContainers; +} + +QVector<AbstractMetaType> Generator::instantiatedSmartPointers() const +{ + return m_d->instantiatedSmartPointers; +} + +Generator::OptionDescriptions Generator::options() const +{ + return OptionDescriptions(); +} + +bool Generator::handleOption(const QString & /* key */, const QString & /* value */) +{ + return false; +} + +const AbstractMetaClassList &Generator::classes() const +{ + return m_d->apiextractor->classes(); +} + +const AbstractMetaClassList &Generator::invisibleTopNamespaces() const +{ + return m_d->m_invisibleTopNamespaces; +} + +AbstractMetaClassList Generator::classesTopologicalSorted(const Dependencies &additionalDependencies) const +{ + return m_d->apiextractor->classesTopologicalSorted(additionalDependencies); +} + +const AbstractMetaFunctionList &Generator::globalFunctions() const +{ + return m_d->apiextractor->globalFunctions(); +} + +const AbstractMetaEnumList &Generator::globalEnums() const +{ + return m_d->apiextractor->globalEnums(); +} + +PrimitiveTypeEntryList Generator::primitiveTypes() const +{ + return m_d->apiextractor->primitiveTypes(); +} + +ContainerTypeEntryList Generator::containerTypes() const +{ + return m_d->apiextractor->containerTypes(); +} + +const AbstractMetaEnum *Generator::findAbstractMetaEnum(const TypeEntry *typeEntry) const +{ + return m_d->apiextractor->findAbstractMetaEnum(typeEntry); +} + +const AbstractMetaEnum *Generator::findAbstractMetaEnum(const AbstractMetaType &metaType) const +{ + return m_d->apiextractor->findAbstractMetaEnum(metaType.typeEntry()); +} + +QString Generator::licenseComment() const +{ + return m_d->licenseComment; +} + +void Generator::setLicenseComment(const QString &licenseComment) +{ + m_d->licenseComment = licenseComment; +} + +QString Generator::packageName() const +{ + return TypeDatabase::instance()->defaultPackageName(); +} + +QString Generator::moduleName() const +{ + if (m_d->moduleName.isEmpty()) { + m_d->moduleName = packageName(); + m_d->moduleName.remove(0, m_d->moduleName.lastIndexOf(QLatin1Char('.')) + 1); + } + return m_d->moduleName; +} + +QString Generator::outputDirectory() const +{ + return m_d->outDir; +} + +void Generator::setOutputDirectory(const QString &outDir) +{ + m_d->outDir = outDir; +} + +bool Generator::generateFileForContext(const GeneratorContext &context) +{ + const AbstractMetaClass *cls = context.metaClass(); + + if (!shouldGenerate(cls)) + return true; + + const QString fileName = fileNameForContext(context); + if (fileName.isEmpty()) + return true; + + QString filePath = outputDirectory() + QLatin1Char('/') + subDirectoryForClass(cls) + + QLatin1Char('/') + fileName; + FileOut fileOut(filePath); + + generateClass(fileOut.stream, context); + + return fileOut.done() != FileOut::Failure; +} + +QString Generator::getFileNameBaseForSmartPointer(const AbstractMetaType &smartPointerType, + const AbstractMetaClass *smartPointerClass) const +{ + const AbstractMetaType innerType = smartPointerType.getSmartPointerInnerType(); + QString fileName = smartPointerClass->qualifiedCppName().toLower(); + fileName.replace(QLatin1String("::"), QLatin1String("_")); + fileName.append(QLatin1String("_")); + fileName.append(innerType.name().toLower()); + + return fileName; +} + +GeneratorContext Generator::contextForClass(const AbstractMetaClass *c) const +{ + GeneratorContext result; + result.m_metaClass = c; + return result; +} + +GeneratorContext Generator::contextForSmartPointer(const AbstractMetaClass *c, + const AbstractMetaType &t) const +{ + GeneratorContext result; + result.m_metaClass = c; + result.m_preciseClassType = t; + result.m_type = GeneratorContext::SmartPointer; + return result; +} + +bool Generator::generate() +{ + const AbstractMetaClassList &classList = m_d->apiextractor->classes(); + for (AbstractMetaClass *cls : classList) { + if (!generateFileForContext(contextForClass(cls))) + return false; + } + + const auto smartPointers = m_d->apiextractor->smartPointers(); + for (const AbstractMetaType &type : qAsConst(m_d->instantiatedSmartPointers)) { + AbstractMetaClass *smartPointerClass = + AbstractMetaClass::findClass(smartPointers, type.typeEntry()); + if (!smartPointerClass) { + qCWarning(lcShiboken, "%s", + qPrintable(msgCannotFindSmartPointer(type.cppSignature(), + smartPointers))); + return false; + } + if (!generateFileForContext(contextForSmartPointer(smartPointerClass, type))) + return false; + } + return finishGeneration(); +} + +bool Generator::shouldGenerateTypeEntry(const TypeEntry *type) const +{ + return type->generateCode() && NamespaceTypeEntry::isVisibleScope(type); +} + +bool Generator::shouldGenerate(const AbstractMetaClass *metaClass) const +{ + return shouldGenerateTypeEntry(metaClass->typeEntry()); +} + +void verifyDirectoryFor(const QString &file) +{ + QDir dir = QFileInfo(file).absoluteDir(); + if (!dir.exists()) { + if (!dir.mkpath(dir.absolutePath())) { + qCWarning(lcShiboken).noquote().nospace() + << QStringLiteral("unable to create directory '%1'").arg(dir.absolutePath()); + } + } +} + +void Generator::replaceTemplateVariables(QString &code, const AbstractMetaFunction *func) +{ + const AbstractMetaClass *cpp_class = func->ownerClass(); + if (cpp_class) + code.replace(QLatin1String("%TYPE"), cpp_class->name()); + + const AbstractMetaArgumentList &argument = func->arguments(); + for (const AbstractMetaArgument &arg : argument) + code.replace(QLatin1Char('%') + QString::number(arg.argumentIndex() + 1), arg.name()); + + //template values + code.replace(QLatin1String("%RETURN_TYPE"), translateType(func->type(), cpp_class)); + code.replace(QLatin1String("%FUNCTION_NAME"), func->originalName()); + + if (code.contains(QLatin1String("%ARGUMENT_NAMES"))) { + QString str; + QTextStream aux_stream(&str); + writeArgumentNames(aux_stream, func, Generator::SkipRemovedArguments); + code.replace(QLatin1String("%ARGUMENT_NAMES"), str); + } + + if (code.contains(QLatin1String("%ARGUMENTS"))) { + QString str; + QTextStream aux_stream(&str); + writeFunctionArguments(aux_stream, func, Options(SkipDefaultValues) | SkipRemovedArguments); + code.replace(QLatin1String("%ARGUMENTS"), str); + } +} + +QTextStream &formatCode(QTextStream &s, const QString &code, Indentor &indentor) +{ + const auto lines= QStringView{code}.split(QLatin1Char('\n')); + for (const auto &line : lines) { + // Do not indent preprocessor lines + if (!line.isEmpty() && !line.startsWith(QLatin1Char('#'))) + s << indentor; + s << line << '\n'; + } + return s; +} + +AbstractMetaFunctionList Generator::implicitConversions(const TypeEntry *type) const +{ + if (type->isValue()) { + if (const AbstractMetaClass *metaClass = AbstractMetaClass::findClass(classes(), type)) + return metaClass->implicitConversions(); + } + return AbstractMetaFunctionList(); +} + +AbstractMetaFunctionList Generator::implicitConversions(const AbstractMetaType &metaType) const +{ + return implicitConversions(metaType.typeEntry()); +} + +bool Generator::isObjectType(const TypeEntry *type) +{ + if (type->isComplex()) + return Generator::isObjectType(static_cast<const ComplexTypeEntry *>(type)); + return type->isObject(); +} +bool Generator::isObjectType(const ComplexTypeEntry *type) +{ + return type->isObject(); +} +bool Generator::isObjectType(const AbstractMetaClass *metaClass) +{ + return Generator::isObjectType(metaClass->typeEntry()); +} +bool Generator::isObjectType(const AbstractMetaType &metaType) +{ + return isObjectType(metaType.typeEntry()); +} + +bool Generator::isPointer(const AbstractMetaType &type) +{ + return type.indirections() > 0 + || type.isNativePointer() + || type.isValuePointer(); +} + +bool Generator::isCString(const AbstractMetaType &type) +{ + return type.isNativePointer() + && type.indirections() == 1 + && type.name() == QLatin1String("char"); +} + +bool Generator::isVoidPointer(const AbstractMetaType &type) +{ + return type.isNativePointer() + && type.indirections() == 1 + && type.name() == QLatin1String("void"); +} + +QString Generator::getFullTypeName(const TypeEntry *type) const +{ + QString result = type->qualifiedCppName(); + if (type->isArray()) + type = static_cast<const ArrayTypeEntry *>(type)->nestedTypeEntry(); + if (!type->isCppPrimitive()) + result.prepend(QLatin1String("::")); + return result; +} + +QString Generator::getFullTypeName(const AbstractMetaType &type) const +{ + if (isCString(type)) + return QLatin1String("const char*"); + if (isVoidPointer(type)) + return QLatin1String("void*"); + if (type.typeEntry()->isContainer()) + return QLatin1String("::") + type.cppSignature(); + QString typeName; + if (type.typeEntry()->isComplex() && type.hasInstantiations()) + typeName = getFullTypeNameWithoutModifiers(type); + else + typeName = getFullTypeName(type.typeEntry()); + return typeName + QString::fromLatin1("*").repeated(type.indirections()); +} + +QString Generator::getFullTypeName(const AbstractMetaClass *metaClass) const +{ + return QLatin1String("::") + metaClass->qualifiedCppName(); +} + +QString Generator::getFullTypeNameWithoutModifiers(const AbstractMetaType &type) const +{ + if (isCString(type)) + return QLatin1String("const char*"); + if (isVoidPointer(type)) + return QLatin1String("void*"); + if (!type.hasInstantiations()) + return getFullTypeName(type.typeEntry()); + QString typeName = type.cppSignature(); + if (type.isConstant()) + typeName.remove(0, sizeof("const ") / sizeof(char) - 1); + switch (type.referenceType()) { + case NoReference: + break; + case LValueReference: + typeName.chop(1); + break; + case RValueReference: + typeName.chop(2); + break; + } + while (typeName.endsWith(QLatin1Char('*')) || typeName.endsWith(QLatin1Char(' '))) + typeName.chop(1); + return QLatin1String("::") + typeName; +} + +DefaultValue Generator::minimalConstructor(const AbstractMetaType &type) const +{ + if (type.referenceType() == LValueReference && Generator::isObjectType(type)) + return DefaultValue(DefaultValue::Error); + + if (type.isContainer()) { + QString ctor = type.cppSignature(); + if (ctor.endsWith(QLatin1Char('*'))) { + ctor.chop(1); + return DefaultValue(DefaultValue::Pointer, ctor.trimmed()); + } + if (ctor.startsWith(QLatin1String("const "))) + ctor.remove(0, sizeof("const ") / sizeof(char) - 1); + if (ctor.endsWith(QLatin1Char('&'))) { + ctor.chop(1); + ctor = ctor.trimmed(); + } + return DefaultValue(DefaultValue::DefaultConstructor, QLatin1String("::") + ctor); + } + + if (type.isNativePointer()) + return DefaultValue(DefaultValue::Pointer, type.typeEntry()->qualifiedCppName()); + if (Generator::isPointer(type)) + return DefaultValue(DefaultValue::Pointer, QLatin1String("::") + type.typeEntry()->qualifiedCppName()); + + if (type.typeEntry()->isSmartPointer()) + return minimalConstructor(type.typeEntry()); + + if (type.typeEntry()->isComplex()) { + auto cType = static_cast<const ComplexTypeEntry *>(type.typeEntry()); + if (cType->hasDefaultConstructor()) + return DefaultValue(DefaultValue::Custom, cType->defaultConstructor()); + auto ctor = minimalConstructor(AbstractMetaClass::findClass(classes(), cType)); + if (ctor.isValid() && type.hasInstantiations()) { + QString v = ctor.value(); + v.replace(getFullTypeName(cType), getFullTypeNameWithoutModifiers(type)); + ctor.setValue(v); + } + return ctor; + } + + return minimalConstructor(type.typeEntry()); +} + +DefaultValue Generator::minimalConstructor(const TypeEntry *type) const +{ + if (!type) + return DefaultValue(DefaultValue::Error); + + if (type->isCppPrimitive()) { + const QString &name = type->qualifiedCppName(); + return name == QLatin1String("bool") + ? DefaultValue(DefaultValue::Boolean) + : DefaultValue(DefaultValue::CppScalar, name); + } + + if (type->isEnum()) { + const auto enumEntry = static_cast<const EnumTypeEntry *>(type); + if (const auto *nullValue = enumEntry->nullValue()) + return DefaultValue(DefaultValue::Enum, nullValue->name()); + return DefaultValue(DefaultValue::Custom, + QLatin1String("static_cast< ::") + type->qualifiedCppName() + + QLatin1String(">(0)")); + } + + if (type->isFlags()) { + return DefaultValue(DefaultValue::Custom, + type->qualifiedCppName() + QLatin1String("(0)")); + } + + if (type->isPrimitive()) { + QString ctor = static_cast<const PrimitiveTypeEntry *>(type)->defaultConstructor(); + // If a non-C++ (i.e. defined by the user) primitive type does not have + // a default constructor defined by the user, the empty constructor is + // heuristically returned. If this is wrong the build of the generated + // bindings will tell. + return ctor.isEmpty() + ? DefaultValue(DefaultValue::DefaultConstructorWithDefaultValues, QLatin1String("::") + + type->qualifiedCppName()) + : DefaultValue(DefaultValue::Custom, ctor); + } + + if (type->isSmartPointer()) + return DefaultValue(DefaultValue::DefaultConstructor, type->qualifiedCppName()); + + if (type->isComplex()) + return minimalConstructor(AbstractMetaClass::findClass(classes(), type)); + + return DefaultValue(DefaultValue::Error); +} + +static QString constructorCall(const QString &qualifiedCppName, const QStringList &args) +{ + return QLatin1String("::") + qualifiedCppName + QLatin1Char('(') + + args.join(QLatin1String(", ")) + QLatin1Char(')'); +} + +DefaultValue Generator::minimalConstructor(const AbstractMetaClass *metaClass) const +{ + if (!metaClass) + return DefaultValue(DefaultValue::Error); + + auto cType = static_cast<const ComplexTypeEntry *>(metaClass->typeEntry()); + if (cType->hasDefaultConstructor()) + return DefaultValue(DefaultValue::Custom, cType->defaultConstructor()); + + const QString qualifiedCppName = cType->qualifiedCppName(); + // Obtain a list of constructors sorted by complexity and number of arguments + QMultiMap<int, const AbstractMetaFunction *> candidates; + const AbstractMetaFunctionList &constructors = metaClass->queryFunctions(AbstractMetaClass::Constructors); + for (const AbstractMetaFunction *ctor : constructors) { + if (!ctor->isUserAdded() && !ctor->isPrivate() + && ctor->functionType() == AbstractMetaFunction::ConstructorFunction) { + // No arguments: Default constructible + const auto &arguments = ctor->arguments(); + if (arguments.isEmpty()) { + return DefaultValue(DefaultValue::DefaultConstructor, + QLatin1String("::") + qualifiedCppName); + } + // First argument has unmodified default: Default constructible with values + if (arguments.constFirst().hasUnmodifiedDefaultValueExpression()) { + return DefaultValue(DefaultValue::DefaultConstructorWithDefaultValues, + QLatin1String("::") + qualifiedCppName); + } + // Examine arguments, exclude functions taking a self parameter + bool simple = true; + bool suitable = true; + for (int i = 0, size = arguments.size(); + suitable && i < size && !arguments.at(i).hasOriginalDefaultValueExpression(); ++i) { + const AbstractMetaArgument &arg = arguments.at(i); + const TypeEntry *aType = arg.type().typeEntry(); + suitable &= aType != cType; + simple &= aType->isCppPrimitive() || aType->isEnum() || isPointer(arg.type()); + } + if (suitable) + candidates.insert(arguments.size() + (simple ? 0 : 100), ctor); + } + } + + for (auto it = candidates.cbegin(), end = candidates.cend(); it != end; ++it) { + const AbstractMetaArgumentList &arguments = it.value()->arguments(); + QStringList args; + bool ok = true; + for (int i =0, size = arguments.size(); ok && i < size; ++i) { + const AbstractMetaArgument &arg = arguments.at(i); + if (arg.hasModifiedDefaultValueExpression()) { + args << arg.defaultValueExpression(); // Spell out modified values + break; + } + if (arg.hasOriginalDefaultValueExpression()) + break; + auto argValue = minimalConstructor(arg.type()); + ok &= argValue.isValid(); + args << argValue.constructorParameter(); + } + if (ok) + return DefaultValue(DefaultValue::Custom, constructorCall(qualifiedCppName, args)); + } + + return DefaultValue(DefaultValue::Error); +} + +// Should int be used for a (protected) enum when generating the public wrapper? +bool Generator::useEnumAsIntForProtectedHack(const AbstractMetaType &metaType) const +{ + if (metaType.isFlags()) + return true; + if (!metaType.isEnum()) + return false; + const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(metaType); + if (!metaEnum) + return true; + if (metaEnum->attributes() & AbstractMetaAttributes::Public) // No reason, type is public + return false; + // Only ordinary C-enums can be used as int, scoped enums fail when used + // as function arguments. + if (metaEnum->enumKind() == EnumKind::EnumClass) + qWarning(lcShiboken, "%s", qPrintable(msgCannotUseEnumAsInt(metaEnum->name()))); + return true; +} + +QString Generator::translateType(AbstractMetaType cType, + const AbstractMetaClass *context, + Options options) const +{ + QString s; + static int constLen = strlen("const"); + + if (context && + context->typeEntry()->isGenericClass() && + cType.originalTemplateType()) { + cType = *cType.originalTemplateType(); + } + + if (cType.isVoid()) { + s = QLatin1String("void"); + } else if (cType.isArray()) { + s = translateType(*cType.arrayElementType(), context, options) + QLatin1String("[]"); + } else if ((options & Generator::EnumAsInts) && useEnumAsIntForProtectedHack(cType)) { + s = intT(); + } else { + if (options & Generator::OriginalName) { + s = cType.originalTypeDescription().trimmed(); + if ((options & Generator::ExcludeReference) && s.endsWith(QLatin1Char('&'))) + s.chop(1); + + // remove only the last const (avoid remove template const) + if (options & Generator::ExcludeConst) { + int index = s.lastIndexOf(QLatin1String("const")); + + if (index >= (s.size() - (constLen + 1))) // (VarType const) or (VarType const[*|&]) + s = s.remove(index, constLen); + } + } else if (options & Generator::ExcludeConst || options & Generator::ExcludeReference) { + AbstractMetaType copyType = cType; + + if (options & Generator::ExcludeConst) + copyType.setConstant(false); + + if (options & Generator::ExcludeReference) + copyType.setReferenceType(NoReference); + + s = copyType.cppSignature(); + if (!copyType.typeEntry()->isVoid() && !copyType.typeEntry()->isCppPrimitive()) + s.prepend(QLatin1String("::")); + } else { + s = cType.cppSignature(); + } + } + + return s; +} + + +QString Generator::subDirectoryForClass(const AbstractMetaClass *clazz) const +{ + return subDirectoryForPackage(clazz->package()); +} + +QString Generator::subDirectoryForPackage(QString packageNameIn) const +{ + if (packageNameIn.isEmpty()) + packageNameIn = packageName(); + packageNameIn.replace(QLatin1Char('.'), QDir::separator()); + return packageNameIn; +} + +template<typename T> +static QString getClassTargetFullName_(const T *t, bool includePackageName) +{ + QString name = t->name(); + const AbstractMetaClass *context = t->enclosingClass(); + while (context) { + // If the type was marked as 'visible=false' we should not use it in + // the type name + if (NamespaceTypeEntry::isVisibleScope(context->typeEntry())) { + name.prepend(QLatin1Char('.')); + name.prepend(context->name()); + } + context = context->enclosingClass(); + } + if (includePackageName) { + name.prepend(QLatin1Char('.')); + name.prepend(t->package()); + } + return name; +} + +QString getClassTargetFullName(const AbstractMetaClass *metaClass, bool includePackageName) +{ + return getClassTargetFullName_(metaClass, includePackageName); +} + +QString getClassTargetFullName(const AbstractMetaEnum *metaEnum, bool includePackageName) +{ + return getClassTargetFullName_(metaEnum, includePackageName); +} + +QString getClassTargetFullName(const AbstractMetaType &metaType, bool includePackageName) +{ + QString name = metaType.cppSignature(); + name.replace(QLatin1String("::"), QLatin1String("_")); + name.replace(QLatin1Char('<'), QLatin1Char('_')); + name.remove(QLatin1Char('>')); + name.remove(QLatin1Char(' ')); + if (includePackageName) { + name.prepend(QLatin1Char('.')); + name.prepend(metaType.package()); + } + return name; +} + +QString getFilteredCppSignatureString(QString signature) +{ + signature.replace(QLatin1String("::"), QLatin1String("_")); + signature.replace(QLatin1Char('<'), QLatin1Char('_')); + signature.replace(QLatin1Char('>'), QLatin1Char('_')); + signature.replace(QLatin1Char(' '), QLatin1Char('_')); + return signature; +} |