diff options
Diffstat (limited to 'sources/shiboken2/generator/generator.cpp')
-rw-r--r-- | sources/shiboken2/generator/generator.cpp | 413 |
1 files changed, 241 insertions, 172 deletions
diff --git a/sources/shiboken2/generator/generator.cpp b/sources/shiboken2/generator/generator.cpp index 1e2f03932..ec227bd83 100644 --- a/sources/shiboken2/generator/generator.cpp +++ b/sources/shiboken2/generator/generator.cpp @@ -28,6 +28,7 @@ #include "generator.h" #include "abstractmetalang.h" +#include "messages.h" #include "reporthandler.h" #include "fileout.h" #include "apiextractor.h" @@ -40,8 +41,118 @@ #include <QDebug> #include <typedatabase.h> -struct Generator::GeneratorPrivate { - const ApiExtractor* apiextractor; +/** + * 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("()"); +} + +struct Generator::GeneratorPrivate +{ + const ApiExtractor* apiextractor = nullptr; QString outDir; // License comment QString licenseComment; @@ -62,20 +173,17 @@ Generator::~Generator() delete m_d; } -bool Generator::setup(const ApiExtractor& extractor, const QMap< QString, QString > args) +bool Generator::setup(const ApiExtractor& extractor) { m_d->apiextractor = &extractor; - TypeEntryHash allEntries = TypeDatabase::instance()->allEntries(); + const auto &allEntries = TypeDatabase::instance()->entries(); TypeEntry* entryFound = 0; - for (TypeEntryHash::const_iterator it = allEntries.cbegin(), end = allEntries.cend(); it != end; ++it) { - for (TypeEntry *entry : it.value()) { - if (entry->type() == TypeEntry::TypeSystemType && entry->generateCode()) { - entryFound = entry; - break; - } - } - if (entryFound) + for (auto it = allEntries.cbegin(), end = allEntries.cend(); it != end; ++it) { + TypeEntry *entry = it.value(); + if (entry->type() == TypeEntry::TypeSystemType && entry->generateCode()) { + entryFound = entry; break; + } } if (entryFound) m_d->packageName = entryFound->name(); @@ -84,7 +192,7 @@ bool Generator::setup(const ApiExtractor& extractor, const QMap< QString, QStrin collectInstantiatedContainersAndSmartPointers(); - return doSetup(args); + return doSetup(); } QString Generator::getSimplifiedContainerTypeName(const AbstractMetaType* type) @@ -197,6 +305,11 @@ Generator::OptionDescriptions Generator::options() const return OptionDescriptions(); } +bool Generator::handleOption(const QString & /* key */, const QString & /* value */) +{ + return false; +} + AbstractMetaClassList Generator::classes() const { return m_d->apiextractor->classes(); @@ -227,24 +340,14 @@ ContainerTypeEntryList Generator::containerTypes() const return m_d->apiextractor->containerTypes(); } -const AbstractMetaEnum* Generator::findAbstractMetaEnum(const EnumTypeEntry* typeEntry) const -{ - return m_d->apiextractor->findAbstractMetaEnum(typeEntry); -} - const AbstractMetaEnum* Generator::findAbstractMetaEnum(const TypeEntry* typeEntry) const { return m_d->apiextractor->findAbstractMetaEnum(typeEntry); } -const AbstractMetaEnum* Generator::findAbstractMetaEnum(const FlagsTypeEntry* typeEntry) const -{ - return m_d->apiextractor->findAbstractMetaEnum(typeEntry); -} - const AbstractMetaEnum* Generator::findAbstractMetaEnum(const AbstractMetaType* metaType) const { - return m_d->apiextractor->findAbstractMetaEnum(metaType); + return m_d->apiextractor->findAbstractMetaEnum(metaType->typeEntry()); } QString Generator::licenseComment() const @@ -278,21 +381,6 @@ void Generator::setOutputDirectory(const QString &outDir) m_d->outDir = outDir; } -inline void touchFile(const QString &filePath) -{ - QFile toucher(filePath); - qint64 size = toucher.size(); - if (!toucher.open(QIODevice::ReadWrite)) { - qCWarning(lcShiboken).noquote().nospace() - << QStringLiteral("Failed to touch file '%1'") - .arg(QDir::toNativeSeparators(filePath)); - return; - } - toucher.resize(size+1); - toucher.resize(size); - toucher.close(); -} - bool Generator::generateFileForContext(GeneratorContext &context) { AbstractMetaClass *cls = context.metaClass(); @@ -312,20 +400,7 @@ bool Generator::generateFileForContext(GeneratorContext &context) generateClass(fileOut.stream, context); - FileOut::State state = fileOut.done(); - switch (state) { - case FileOut::Failure: - return false; - case FileOut::Unchanged: - // Even if contents is unchanged, the last file modification time should be updated, - // so that the build system can rely on the fact the generated file is up-to-date. - touchFile(filePath); - break; - case FileOut::Success: - break; - } - - return true; + return fileOut.done() != FileOut::Failure; } QString Generator::getFileNameBaseForSmartPointer(const AbstractMetaType *smartPointerType, @@ -369,9 +444,9 @@ bool Generator::shouldGenerate(const AbstractMetaClass* metaClass) const return shouldGenerateTypeEntry(metaClass->typeEntry()); } -void verifyDirectoryFor(const QFile &file) +void verifyDirectoryFor(const QString &file) { - QDir dir = QFileInfo(file).dir(); + QDir dir = QFileInfo(file).absoluteDir(); if (!dir.exists()) { if (!dir.mkpath(dir.absolutePath())) { qCWarning(lcShiboken).noquote().nospace() @@ -463,7 +538,7 @@ AbstractMetaFunctionList Generator::implicitConversions(const AbstractMetaType* bool Generator::isObjectType(const TypeEntry* type) { if (type->isComplex()) - return Generator::isObjectType((const ComplexTypeEntry*)type); + return Generator::isObjectType(static_cast<const ComplexTypeEntry *>(type)); return type->isObject(); } bool Generator::isObjectType(const ComplexTypeEntry* type) @@ -557,184 +632,178 @@ QString Generator::getFullTypeNameWithoutModifiers(const AbstractMetaType* type) return QLatin1String("::") + typeName; } -QString Generator::minimalConstructor(const AbstractMetaType* type) const +DefaultValue Generator::minimalConstructor(const AbstractMetaType* type) const { if (!type || (type->referenceType() == LValueReference && Generator::isObjectType(type))) - return QString(); + return DefaultValue(DefaultValue::Error); if (type->isContainer()) { QString ctor = type->cppSignature(); - if (ctor.endsWith(QLatin1Char('*'))) - return QLatin1String("0"); + 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 QLatin1String("::") + ctor + QLatin1String("()"); + return DefaultValue(DefaultValue::DefaultConstructor, QLatin1String("::") + ctor); } if (type->isNativePointer()) - return QLatin1String("static_cast<") + type->typeEntry()->qualifiedCppName() + QLatin1String(" *>(0)"); + return DefaultValue(DefaultValue::Pointer, type->typeEntry()->qualifiedCppName()); if (Generator::isPointer(type)) - return QLatin1String("static_cast< ::") + type->typeEntry()->qualifiedCppName() + QLatin1String(" *>(0)"); + return DefaultValue(DefaultValue::Pointer, QLatin1String("::") + type->typeEntry()->qualifiedCppName()); if (type->typeEntry()->isComplex()) { - const ComplexTypeEntry* cType = reinterpret_cast<const ComplexTypeEntry*>(type->typeEntry()); - QString ctor = cType->defaultConstructor(); - if (!ctor.isEmpty()) - return ctor; - ctor = minimalConstructor(AbstractMetaClass::findClass(classes(), cType)); - if (type->hasInstantiations()) - ctor = ctor.replace(getFullTypeName(cType), getFullTypeNameWithoutModifiers(type)); + const ComplexTypeEntry* 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()); } -QString Generator::minimalConstructor(const TypeEntry* type) const +DefaultValue Generator::minimalConstructor(const TypeEntry* type) const { if (!type) - return QString(); + return DefaultValue(DefaultValue::Error); if (type->isCppPrimitive()) { const QString &name = type->qualifiedCppName(); return name == QLatin1String("bool") - ? QLatin1String("false") : name + QLatin1String("(0)"); + ? DefaultValue(DefaultValue::Boolean) + : DefaultValue(DefaultValue::CppScalar, name); } - if (type->isEnum()) - return QLatin1String("static_cast< ::") + type->qualifiedCppName() + QLatin1String(">(0)"); + 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 type->qualifiedCppName() + QLatin1String("(0)"); + if (type->isFlags()) { + return DefaultValue(DefaultValue::Custom, + type->qualifiedCppName() + QLatin1String("(0)")); + } if (type->isPrimitive()) { - QString ctor = reinterpret_cast<const PrimitiveTypeEntry*>(type)->defaultConstructor(); + 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() - ? (QLatin1String("::") + type->qualifiedCppName() + QLatin1String("()")) - : ctor; + ? DefaultValue(DefaultValue::DefaultConstructorWithDefaultValues, QLatin1String("::") + + type->qualifiedCppName()) + : DefaultValue(DefaultValue::Custom, ctor); } if (type->isComplex()) return minimalConstructor(AbstractMetaClass::findClass(classes(), type)); - return QString(); + return DefaultValue(DefaultValue::Error); } -QString Generator::minimalConstructor(const AbstractMetaClass* metaClass) const +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 QString(); + return DefaultValue(DefaultValue::Error); - const ComplexTypeEntry* cType = reinterpret_cast<const ComplexTypeEntry*>(metaClass->typeEntry()); + const ComplexTypeEntry* cType = static_cast<const ComplexTypeEntry*>(metaClass->typeEntry()); if (cType->hasDefaultConstructor()) - return cType->defaultConstructor(); + 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); - int maxArgs = 0; for (const AbstractMetaFunction *ctor : constructors) { - if (ctor->isUserAdded() || ctor->isPrivate() || ctor->functionType() != AbstractMetaFunction::ConstructorFunction) - continue; - - int numArgs = ctor->arguments().size(); - if (numArgs == 0) { - maxArgs = 0; - break; - } - if (numArgs > maxArgs) - maxArgs = numArgs; - } - - QString qualifiedCppName = metaClass->typeEntry()->qualifiedCppName(); - QStringList templateTypes; - const QVector<TypeEntry *> &templateArguments = metaClass->templateArguments(); - for (TypeEntry *templateType : templateArguments) - templateTypes << templateType->qualifiedCppName(); - - // Empty constructor. - if (maxArgs == 0) - return QLatin1String("::") + qualifiedCppName + QLatin1String("()"); - - QVector<const AbstractMetaFunction *> candidates; - - // Constructors with C++ primitive types, enums or pointers only. - // Start with the ones with fewer arguments. - for (int i = 1; i <= maxArgs; ++i) { - for (const AbstractMetaFunction *ctor : constructors) { - if (ctor->isUserAdded() || ctor->isPrivate() || ctor->functionType() != AbstractMetaFunction::ConstructorFunction) - continue; - - const AbstractMetaArgumentList &arguments = ctor->arguments(); - if (arguments.size() != i) - continue; - - QStringList args; - for (const AbstractMetaArgument *arg : arguments) { - const TypeEntry* type = arg->type()->typeEntry(); - if (type == metaClass->typeEntry()) { - args.clear(); - break; - } - - if (!arg->originalDefaultValueExpression().isEmpty()) { - if (!arg->defaultValueExpression().isEmpty() - && arg->defaultValueExpression() != arg->originalDefaultValueExpression()) { - args << arg->defaultValueExpression(); - } - break; - } - - if (type->isCppPrimitive() || type->isEnum() || isPointer(arg->type())) { - QString argValue = minimalConstructor(arg->type()); - if (argValue.isEmpty()) { - args.clear(); - break; - } - args << argValue; - } else { - args.clear(); - break; - } + 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); } - - if (!args.isEmpty()) - return QString::fromLatin1("::%1(%2)").arg(qualifiedCppName, args.join(QLatin1String(", "))); - - candidates << ctor; + // 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)->hasDefaultValueExpression(); ++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); } } - // Constructors with C++ primitive types, enums, pointers, value types, - // and user defined primitive types. - // Builds the minimal constructor recursively. - for (const AbstractMetaFunction *ctor : qAsConst(candidates)) { + for (auto it = candidates.cbegin(), end = candidates.cend(); it != end; ++it) { + const AbstractMetaArgumentList &arguments = it.value()->arguments(); QStringList args; - const AbstractMetaArgumentList &arguments = ctor->arguments(); - for (const AbstractMetaArgument *arg : arguments) { - if (arg->type()->typeEntry() == metaClass->typeEntry()) { - args.clear(); - break; - } - QString argValue = minimalConstructor(arg->type()); - if (argValue.isEmpty()) { - args.clear(); + bool ok = true; + for (int i =0, size = arguments.size(); ok && i < size; ++i) { + const AbstractMetaArgument *arg = arguments.at(i); + if (arg->hasDefaultValueExpression()) { + if (arg->hasModifiedDefaultValueExpression()) + args << arg->defaultValueExpression(); // Spell out modified values break; } - args << argValue; - } - if (!args.isEmpty()) { - return QString::fromLatin1("::%1(%2)").arg(qualifiedCppName, args.join(QLatin1String(", "))); + auto argValue = minimalConstructor(arg->type()); + ok &= argValue.isValid(); + args << argValue.constructorParameter(); } + if (ok) + return DefaultValue(DefaultValue::Custom, constructorCall(qualifiedCppName, args)); } - return QString(); + 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(const AbstractMetaType *cType, @@ -754,7 +823,7 @@ QString Generator::translateType(const AbstractMetaType *cType, s = QLatin1String("void"); } else if (cType->isArray()) { s = translateType(cType->arrayElementType(), context, options) + QLatin1String("[]"); - } else if (options & Generator::EnumAsInts && (cType->isEnum() || cType->isFlags())) { + } else if ((options & Generator::EnumAsInts) && useEnumAsIntForProtectedHack(cType)) { s = QLatin1String("int"); } else { if (options & Generator::OriginalName) { |