diff options
Diffstat (limited to 'sources/shiboken6/generator/generator.cpp')
-rw-r--r-- | sources/shiboken6/generator/generator.cpp | 701 |
1 files changed, 701 insertions, 0 deletions
diff --git a/sources/shiboken6/generator/generator.cpp b/sources/shiboken6/generator/generator.cpp new file mode 100644 index 000000000..b224858c5 --- /dev/null +++ b/sources/shiboken6/generator/generator.cpp @@ -0,0 +1,701 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "generator.h" +#include "defaultvalue.h" +#include "generatorcontext.h" +#include "apiextractorresult.h" +#include "abstractmetaargument.h" +#include "abstractmetaenum.h" +#include "abstractmetafunction.h" +#include "abstractmetalang.h" +#include "messages.h" +#include <optionsparser.h> +#include "reporthandler.h" +#include "fileout.h" +#include "arraytypeentry.h" +#include "enumtypeentry.h" +#include "enumvaluetypeentry.h" +#include "namespacetypeentry.h" +#include "primitivetypeentry.h" +#include "typesystemtypeentry.h" +#include <typedatabase.h> + +#include "qtcompat.h" + +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QRegularExpression> + +using namespace Qt::StringLiterals; + +static constexpr auto ENABLE_PYSIDE_EXTENSIONS = "enable-pyside-extensions"_L1; +static constexpr auto AVOID_PROTECTED_HACK = "avoid-protected-hack"_L1; + +struct GeneratorOptions +{ + bool usePySideExtensions = false; + bool avoidProtectedHack = false; +}; + +struct Generator::GeneratorPrivate +{ + ApiExtractorResult api; + QString outDir; + // License comment + QString licenseComment; + AbstractMetaClassCList m_invisibleTopNamespaces; + bool m_hasPrivateClasses = false; + static GeneratorOptions m_options; +}; + +GeneratorOptions Generator::GeneratorPrivate::m_options; + +// Kept as a variable for a potential Qt-in-namespace support +QString Generator::m_gsp = "::"_L1; + +Generator::Generator() : m_d(new GeneratorPrivate) +{ +} + +Generator::~Generator() +{ + delete m_d; +} + +bool Generator::setup(const ApiExtractorResult &api) +{ + m_d->api = api; + const auto moduleEntry = TypeDatabase::instance()->defaultTypeSystemType(); + if (!moduleEntry) { + qCWarning(lcShiboken,"Couldn't find the package name!!"); + return false; + } + if (!moduleEntry->generateCode()) { + qCWarning(lcShiboken, "Code generation of root typesystem is disabled!!"); + return false; + } + + for (const auto &c : api.classes()) { + if (c->enclosingClass() == nullptr && c->isInvisibleNamespace()) { + m_d->m_invisibleTopNamespaces.append(c); + c->invisibleNamespaceRecursion([&](const AbstractMetaClassCPtr &ic) { + m_d->m_invisibleTopNamespaces.append(ic); + }); + } + } + + return doSetup(); +} + +QList<OptionDescription> Generator::options() +{ + return { + {AVOID_PROTECTED_HACK, + u"Avoid the use of the '#define protected public' hack."_s}, + {ENABLE_PYSIDE_EXTENSIONS, + u"Enable PySide extensions, such as support for signal/slots,\n" + "use this if you are creating a binding for a Qt-based library."_s} + }; +} + +class GeneratorOptionsParser : public OptionsParser +{ +public: + explicit GeneratorOptionsParser(GeneratorOptions *o) : m_options(o) {} + + bool handleBoolOption(const QString &key, OptionSource source) override; + +private: + GeneratorOptions *m_options; +}; + +bool GeneratorOptionsParser::handleBoolOption(const QString & key, OptionSource source) +{ + if (source == OptionSource::CommandLineSingleDash) + return false; + if (key == ENABLE_PYSIDE_EXTENSIONS) + return ( m_options->usePySideExtensions = true); + if (key == AVOID_PROTECTED_HACK) + return ( m_options->avoidProtectedHack = true); + return false; +} + +std::shared_ptr<OptionsParser> Generator::createOptionsParser() +{ + return std::make_shared<GeneratorOptionsParser>(&GeneratorPrivate::m_options); +} + +QString Generator::fileNameForContextHelper(const GeneratorContext &context, + const QString &suffix, + FileNameFlags flags) + +{ + if (!context.forSmartPointer()) { + const auto metaClass = context.metaClass(); + QString fileNameBase = flags.testFlag(FileNameFlag::UnqualifiedName) + ? metaClass->name() : metaClass->qualifiedCppName(); + if (!flags.testFlag(FileNameFlag::KeepCase)) + fileNameBase = fileNameBase.toLower(); + fileNameBase.replace(u"::"_s, u"_"_s); + return fileNameBase + suffix; + } + + // FIXME: PYSIDE7: Use the above code path for all types. Note the file + // names will then change to reflect the namespaces of the pointee + // (smart/integer2). + const AbstractMetaType &smartPointerType = context.preciseType(); + QString fileNameBase = getFileNameBaseForSmartPointer(smartPointerType); + return fileNameBase + suffix; +} + +const AbstractMetaClassCList &Generator::invisibleTopNamespaces() const +{ + return m_d->m_invisibleTopNamespaces; +} + +PrimitiveTypeEntryCList Generator::primitiveTypes() +{ + return TypeDatabase::instance()->primitiveTypes(); +} + +ContainerTypeEntryCList Generator::containerTypes() +{ + return TypeDatabase::instance()->containerTypes(); +} + +QString Generator::licenseComment() const +{ + return m_d->licenseComment; +} + +void Generator::setLicenseComment(const QString &licenseComment) +{ + m_d->licenseComment = licenseComment; +} + +QString Generator::packageName() +{ + return TypeDatabase::instance()->defaultPackageName(); +} + +static QString getModuleName() +{ + QString result = TypeDatabase::instance()->defaultPackageName(); + result.remove(0, result.lastIndexOf(u'.') + 1); + return result; +} + +QString Generator::moduleName() +{ + static const QString result = getModuleName(); + return result; +} + +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 auto cls = context.metaClass(); + auto typeEntry = cls->typeEntry(); + + if (!shouldGenerate(typeEntry)) + return true; + + const QString fileName = fileNameForContext(context); + if (fileName.isEmpty()) + return true; + + QString filePath = outputDirectory() + u'/' + + subDirectoryForPackage(typeEntry->targetLangPackage()) + + u'/' + fileName; + FileOut fileOut(filePath); + + generateClass(fileOut.stream, context); + + fileOut.done(); + return true; +} + +QString Generator::getFileNameBaseForSmartPointer(const AbstractMetaType &smartPointerType) +{ + const AbstractMetaType innerType = smartPointerType.getSmartPointerInnerType(); + smartPointerType.typeEntry()->qualifiedCppName(); + QString fileName = smartPointerType.typeEntry()->qualifiedCppName().toLower(); + fileName.replace(u"::"_s, u"_"_s); + fileName.append(u"_"_s); + fileName.append(innerType.name().toLower()); + + return fileName; +} + +GeneratorContext Generator::contextForClass(const AbstractMetaClassCPtr &c) const +{ + GeneratorContext result; + result.m_metaClass = c; + return result; +} + +GeneratorContext + Generator::contextForSmartPointer(const AbstractMetaClassCPtr &c, + const AbstractMetaType &t, + const AbstractMetaClassCPtr &pointeeClass) +{ + GeneratorContext result; + result.m_metaClass = c; + result.m_preciseClassType = t; + result.m_type = GeneratorContext::SmartPointer; + result.m_pointeeClass = pointeeClass; + return result; +} + +bool Generator::generate() +{ + for (const auto &cls : m_d->api.classes()) { + if (!generateFileForContext(contextForClass(cls))) + return false; + auto te = cls->typeEntry(); + if (shouldGenerate(te) && te->isPrivate()) + m_d->m_hasPrivateClasses = true; + } + + for (const auto &smp: m_d->api.instantiatedSmartPointers()) { + AbstractMetaClassCPtr pointeeClass; + const auto instantiatedType = smp.type.instantiations().constFirst().typeEntry(); + if (instantiatedType->isComplex()) // not a C++ primitive + pointeeClass = AbstractMetaClass::findClass(m_d->api.classes(), instantiatedType); + if (!generateFileForContext(contextForSmartPointer(smp.specialized, smp.type, + pointeeClass))) { + return false; + } + } + return finishGeneration(); +} + +bool Generator::shouldGenerate(const TypeEntryCPtr &typeEntry) const +{ + return typeEntry->shouldGenerate(); +} + +const ApiExtractorResult &Generator::api() const +{ + return m_d->api; +} + +bool Generator::hasPrivateClasses() const +{ + return m_d->m_hasPrivateClasses; +} + +bool Generator::usePySideExtensions() +{ + return GeneratorPrivate::m_options.usePySideExtensions; +} + +bool Generator::avoidProtectedHack() +{ + return GeneratorPrivate::m_options.avoidProtectedHack; +} + +QString Generator::getFullTypeName(TypeEntryCPtr type) +{ + QString result = type->qualifiedCppName(); + if (type->isArray()) + type = std::static_pointer_cast<const ArrayTypeEntry>(type)->nestedTypeEntry(); + return isCppPrimitive(type) ? result : addGlobalScopePrefix(result); +} + +QString Generator::getFullTypeName(const AbstractMetaType &type) +{ + if (type.isCString()) + return u"const char*"_s; + if (type.isVoidPointer()) + return u"void*"_s; + if (type.typeEntry()->isContainer()) + return addGlobalScopePrefix(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 AbstractMetaClassCPtr &metaClass) +{ + const QString &qualName = metaClass->qualifiedCppName(); + // Typedefs are generated into the global namespace + return metaClass->isTypeDef() ? qualName : addGlobalScopePrefix(qualName); +} + +QString Generator::getFullTypeNameWithoutModifiers(const AbstractMetaType &type) +{ + if (type.isCString()) + return u"const char*"_s; + if (type.isVoidPointer()) + return u"void*"_s; + 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(u'*') || typeName.endsWith(u' ')) + typeName.chop(1); + return addGlobalScopePrefix(typeName); +} + +std::optional<DefaultValue> + Generator::minimalConstructor(const ApiExtractorResult &api, + const AbstractMetaType &type, + QString *errorString) +{ + if (type.referenceType() == LValueReference && type.isObjectType()) + return {}; + + if (type.isContainer()) { + QString ctor = type.cppSignature(); + if (ctor.endsWith(u'*')) { + ctor.chop(1); + return DefaultValue(DefaultValue::Pointer, ctor.trimmed()); + } + if (ctor.startsWith(u"const ")) + ctor.remove(0, sizeof("const ") / sizeof(char) - 1); + if (ctor.endsWith(u'&')) { + ctor.chop(1); + ctor = ctor.trimmed(); + } + return DefaultValue(DefaultValue::DefaultConstructor, u"::"_s + ctor); + } + + if (type.isNativePointer()) + return DefaultValue(DefaultValue::Pointer, type.typeEntry()->qualifiedCppName()); + if (type.isPointer()) + return DefaultValue(DefaultValue::Pointer, getFullTypeName(type.typeEntry())); + + if (type.typeEntry()->isSmartPointer()) + return minimalConstructor(api, type.typeEntry()); + + if (type.typeEntry()->isComplex()) { + auto cType = std::static_pointer_cast<const ComplexTypeEntry>(type.typeEntry()); + if (cType->hasDefaultConstructor()) + return DefaultValue(DefaultValue::Custom, cType->defaultConstructor()); + auto klass = AbstractMetaClass::findClass(api.classes(), cType); + if (!klass) { + if (errorString != nullptr) + *errorString = msgClassNotFound(cType); + return {}; + } + auto ctorO = minimalConstructor(api, klass); + if (ctorO.has_value() && type.hasInstantiations()) { + auto ctor = ctorO.value(); + QString v = ctor.value(); + v.replace(getFullTypeName(cType), getFullTypeNameWithoutModifiers(type)); + ctor.setValue(v); + return ctor; + } + return ctorO; + } + + return minimalConstructor(api, type.typeEntry(), errorString); +} + +std::optional<DefaultValue> + Generator::minimalConstructor(const ApiExtractorResult &api, + const TypeEntryCPtr &type, + QString *errorString) +{ + if (!type) + return {}; + + if (isCppPrimitive(type)) { + const QString &name = type->qualifiedCppName(); + return name == u"bool" + ? DefaultValue(DefaultValue::Boolean) + : DefaultValue(DefaultValue::CppScalar, name); + } + + if (type->isEnum()) { + const auto enumEntry = std::static_pointer_cast<const EnumTypeEntry>(type); + if (const auto nullValue = enumEntry->nullValue()) + return DefaultValue(DefaultValue::Enum, nullValue->name()); + return DefaultValue(DefaultValue::Custom, + "static_cast< "_L1 + getFullTypeName(type) + ">(0)"_L1); + } + + if (type->isFlags()) { + return DefaultValue(DefaultValue::Custom, + type->qualifiedCppName() + u"(0)"_s); + } + + if (type->isPrimitive()) { + QString ctor = std::static_pointer_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, u"::"_s + + type->qualifiedCppName()) + : DefaultValue(DefaultValue::Custom, ctor); + } + + if (type->isSmartPointer()) + return DefaultValue(DefaultValue::DefaultConstructor, type->qualifiedCppName()); + + if (type->isComplex()) { + auto klass = AbstractMetaClass::findClass(api.classes(), type); + if (!klass) { + if (errorString != nullptr) + *errorString = msgClassNotFound(type); + return {}; + } + return minimalConstructor(api, klass, errorString); + } + + if (errorString != nullptr) + *errorString = u"No default value could be determined."_s; + return {}; +} + +static QString constructorCall(const QString &qualifiedCppName, const QStringList &args) +{ + return u"::"_s + qualifiedCppName + u'(' + + args.join(u", "_s) + u')'; +} + +std::optional<DefaultValue> + Generator::minimalConstructor(const ApiExtractorResult &api, + const AbstractMetaClassCPtr &metaClass, + QString *errorString) +{ + if (!metaClass) + return {}; + + auto cType = std::static_pointer_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 AbstractMetaFunctionCPtr> candidates; + const auto &constructors = metaClass->queryFunctions(FunctionQueryOption::Constructors); + for (const auto &ctor : constructors) { + if (!ctor->isUserAdded() && !ctor->isPrivate() + && (ctor->isPublic() || !api.flags().testFlag(ApiExtractorFlag::AvoidProtectedHack))) { + // No arguments: Default constructible + const auto &arguments = ctor->arguments(); + if (arguments.isEmpty()) { + return DefaultValue(DefaultValue::DefaultConstructor, + u"::"_s + qualifiedCppName); + } + // First argument has unmodified default: Default constructible with values + if (arguments.constFirst().hasUnmodifiedDefaultValueExpression()) { + return DefaultValue(DefaultValue::DefaultConstructorWithDefaultValues, + u"::"_s + qualifiedCppName); + } + // Examine arguments, exclude functions taking a self parameter + bool simple = true; + bool suitable = true; + for (qsizetype i = 0, size = arguments.size(); + suitable && i < size && !arguments.at(i).hasOriginalDefaultValueExpression(); ++i) { + const AbstractMetaArgument &arg = arguments.at(i); + TypeEntryCPtr aType = arg.type().typeEntry(); + suitable &= aType != cType; + simple &= isCppPrimitive(aType) || aType->isEnum() || arg.type().isPointer(); + } + 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; + for (const auto &arg : arguments) { + if (arg.hasModifiedDefaultValueExpression()) { + args << arg.defaultValueExpression(); // Spell out modified values + break; + } + if (arg.hasOriginalDefaultValueExpression()) + break; + auto argValue = minimalConstructor(api, arg.type(), errorString); + if (!argValue.has_value()) + return {}; + args << argValue->constructorParameter(); + } + return DefaultValue(DefaultValue::Custom, constructorCall(qualifiedCppName, args)); + } + + return {}; +} + +QString Generator::translateType(AbstractMetaType cType, + const AbstractMetaClassCPtr &context, + Options options) const +{ + QString s; + + if (context && + context->typeEntry()->isGenericClass() && + cType.originalTemplateType()) { + cType = *cType.originalTemplateType(); + } + + if (cType.isVoid()) { + s = u"void"_s; + } else if (cType.isArray()) { + s = translateType(*cType.arrayElementType(), context, options) + u"[]"_s; + } else { + AbstractMetaType copyType = cType; + if (options & Generator::ExcludeConst || options & Generator::ExcludeReference) { + if (options & Generator::ExcludeConst) + copyType.setConstant(false); + if (options & Generator::ExcludeReference) + copyType.setReferenceType(NoReference); + } + + s = copyType.cppSignature(); + const auto te = copyType.typeEntry(); + if (!te->isVoid() && !isCppPrimitive(te)) { // Add scope resolution + const auto pos = s.indexOf(te->qualifiedCppName()); // Skip const/volatile + Q_ASSERT(pos >= 0); + s.insert(pos, u"::"_s); + } + } + + return s; +} + +static const QHash<QString, QString> &pythonOperators() +{ + static const QHash<QString, QString> result = { + // call operator + {u"operator()"_s, u"__call__"_s}, + // Arithmetic operators + {u"operator+"_s, u"__add__"_s}, + {u"operator-"_s, u"__sub__"_s}, + {u"operator*"_s, u"__mul__"_s}, + {u"operator/"_s, u"__div__"_s}, + {u"operator%"_s, u"__mod__"_s}, + // Inplace arithmetic operators + {u"operator+="_s, u"__iadd__"_s}, + {u"operator-="_s, u"__isub__"_s}, + {u"operator++"_s, u"__iadd__"_s}, + {u"operator--"_s, u"__isub__"_s}, + {u"operator*="_s, u"__imul__"_s}, + {u"operator%="_s, u"__imod__"_s}, + // Bitwise operators + {u"operator&"_s, u"__and__"_s}, + {u"operator^"_s, u"__xor__"_s}, + {u"operator|"_s, u"__or__"_s}, + {u"operator<<"_s, u"__lshift__"_s}, + {u"operator>>"_s, u"__rshift__"_s}, + {u"operator~"_s, u"__invert__"_s}, + // Inplace bitwise operators + {u"operator&="_s, u"__iand__"_s}, + {u"operator^="_s, u"__ixor__"_s}, + {u"operator|="_s, u"__ior__"_s}, + {u"operator<<="_s, u"__ilshift__"_s}, + {u"operator>>="_s, u"__irshift__"_s}, + // Comparison operators + {u"operator=="_s, u"__eq__"_s}, + {u"operator!="_s, u"__ne__"_s}, + {u"operator<"_s, u"__lt__"_s}, + {u"operator>"_s, u"__gt__"_s}, + {u"operator<="_s, u"__le__"_s}, + {u"operator>="_s, u"__ge__"_s}, + // Conversion (note bool has special handling with heuristics) + {u"operator int"_s, u"__int__"_s}, + {u"operator double"_s, u"__float__"_s} + }; + return result; +} + +QString Generator::pythonOperatorFunctionName(const QString &cppOpFuncName) +{ + return pythonOperators().value(cppOpFuncName); +} + +bool Generator::isPythonOperatorFunctionName(const QString &cppOpFuncName) +{ + return pythonOperators().contains(cppOpFuncName); +} + +QString Generator::subDirectoryForPackage(QString packageNameIn) const +{ + if (packageNameIn.isEmpty()) + packageNameIn = packageName(); + packageNameIn.replace(u'.', QDir::separator()); + return packageNameIn; +} + +QString Generator::addGlobalScopePrefix(const QString &t) +{ + return t.startsWith("std::"_L1) ? t : m_gsp + t; +} + +QString Generator::globalScopePrefix(const GeneratorContext &classContext) +{ + return classContext.useWrapper() ? QString{} : m_gsp; +} + +template<typename T> +static QString getClassTargetFullName_(T t, bool includePackageName) +{ + QString name = t->name(); + AbstractMetaClassCPtr 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(u'.'); + name.prepend(context->name()); + } + context = context->enclosingClass(); + } + if (includePackageName) { + name.prepend(u'.'); + name.prepend(t->package()); + } + return name; +} + +QString getClassTargetFullName(const AbstractMetaClassCPtr &metaClass, + bool includePackageName) +{ + return getClassTargetFullName_(metaClass, includePackageName); +} + +QString getClassTargetFullName(const AbstractMetaEnum &metaEnum, + bool includePackageName) +{ + return getClassTargetFullName_(&metaEnum, includePackageName); +} + +QString getFilteredCppSignatureString(QString signature) +{ + signature.replace(u"::"_s, u"_"_s); + signature.replace(u'<', u'_'); + signature.replace(u'>', u'_'); + signature.replace(u' ', u'_'); + return signature; +} |