From 2bfd1de3495b18c0ecc251260442a9a46009861e Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 20 Jul 2018 14:52:21 +0200 Subject: shiboken: Add a typedef typesystem entry The intention is be able to specify typedef std::optional OptionalInt in the typesystem file and generate code for it (without having a typedef in C++). Task-number: PYSIDE-725 Change-Id: I5847a3c3f68556ac1d0ea3635f65a29caa6cb208 Reviewed-by: Christian Tismer --- .../shiboken2/ApiExtractor/abstractmetabuilder.cpp | 18 +++++ .../shiboken2/ApiExtractor/abstractmetabuilder_p.h | 1 + .../doc/typesystem_specifying_types.rst | 32 ++++++++ .../shiboken2/ApiExtractor/tests/testtemplates.cpp | 21 ++++++ sources/shiboken2/ApiExtractor/typedatabase.cpp | 52 ++++++++++++- sources/shiboken2/ApiExtractor/typedatabase.h | 5 +- .../shiboken2/ApiExtractor/typedatabase_typedefs.h | 2 + sources/shiboken2/ApiExtractor/typesystem.cpp | 86 ++++++++++++++++++++-- sources/shiboken2/ApiExtractor/typesystem.h | 37 +++++++++- sources/shiboken2/ApiExtractor/typesystem_p.h | 4 + .../generator/shiboken2/headergenerator.cpp | 30 ++++++++ 11 files changed, 278 insertions(+), 10 deletions(-) diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp index 3f76739f3..a200a87d1 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp @@ -514,6 +514,8 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom) addAbstractMetaClass(cls); } + traverseTypesystemTypedefs(); + for (const ClassModelItem &item : typeValues) traverseClassMembers(item); @@ -1039,6 +1041,22 @@ AbstractMetaClass* AbstractMetaBuilderPrivate::traverseTypeDef(const FileModelIt return metaClass; } +// Add the typedef'ed classes +void AbstractMetaBuilderPrivate::traverseTypesystemTypedefs() +{ + const auto &entries = TypeDatabase::instance()->typedefEntries(); + for (auto it = entries.begin(), end = entries.end(); it != end; ++it) { + TypedefEntry *te = it.value(); + AbstractMetaClass *metaClass = new AbstractMetaClass; + metaClass->setTypeDef(true); + metaClass->setTypeEntry(te->target()); + metaClass->setBaseClassNames(QStringList(te->sourceType())); + *metaClass += AbstractMetaAttributes::Public; + fillAddedFunctions(metaClass); + addAbstractMetaClass(metaClass); + } +} + AbstractMetaClass *AbstractMetaBuilderPrivate::traverseClass(const FileModelItem &dom, const ClassModelItem &classItem) { diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h b/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h index 9fcad26ad..2193a7c78 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h @@ -65,6 +65,7 @@ public: void addAbstractMetaClass(AbstractMetaClass *cls); AbstractMetaClass *traverseTypeDef(const FileModelItem &dom, const TypeDefModelItem &typeDef); + void traverseTypesystemTypedefs(); AbstractMetaClass *traverseClass(const FileModelItem &dom, const ClassModelItem &item); AbstractMetaClass *currentTraversedClass(ScopeModelItem item); diff --git a/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst b/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst index c5f6463fd..c8a31a426 100644 --- a/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst +++ b/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst @@ -329,6 +329,38 @@ container-type The *optional* **since** value is used to specify the API version of this container. +typedef-type +^^^^^^^^^^^^ + + The typedef-type allows for specifying typedefs in the typesystem. They + are mostly equivalent to spelling out the typedef in the included header, which + is often complicated when trying to wrap libraries whose source code cannot be + easily extended. + + .. code-block:: xml + + + + + The **source** attribute is the source. Example: + + .. code-block:: xml + + + \n" + + + + is equivalent to + + .. code-block:: c++ + + typedef std::optional IntOptional; + + The *optional* **since** value is used to specify the API version of this type. .. _custom-type: diff --git a/sources/shiboken2/ApiExtractor/tests/testtemplates.cpp b/sources/shiboken2/ApiExtractor/tests/testtemplates.cpp index d32e24379..b1b171bae 100644 --- a/sources/shiboken2/ApiExtractor/tests/testtemplates.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testtemplates.cpp @@ -474,6 +474,7 @@ public: << "typedef Optional IntOptional;\n"; QString xml; QTextStream(&xml) << xmlPrefix << xmlOptionalDecl << xmlOptionalIntDecl + << "" << xmlPostFix; QTest::newRow("global-namespace") << cpp << xml; @@ -486,6 +487,7 @@ public: QTextStream(&xml) << xmlPrefix << "\n" << xmlOptionalDecl << "\n" << xmlOptionalIntDecl + << "" << xmlPostFix; QTest::newRow("namespace-Std") << cpp << xml; @@ -498,6 +500,7 @@ public: QTextStream(&xml) << xmlPrefix << "\n" << xmlOptionalDecl << "\n" << xmlOptionalIntDecl + << "" << xmlPostFix; QTest::newRow("nested-class") << cpp << xml; @@ -523,17 +526,35 @@ void TestTemplates::testTemplateTypeDefs() QVERIFY(optionalInt); QCOMPARE(optionalInt->templateBaseClass(), optional); + // Find the class typedef'ed in the typesystem XML + const AbstractMetaClass *xmlOptionalInt = + AbstractMetaClass::findClass(classes, QLatin1String("XmlIntOptional")); + QVERIFY(xmlOptionalInt); + QCOMPARE(xmlOptionalInt->templateBaseClass(), optional); + // Check whether the value() method now has an 'int' return const AbstractMetaFunction *valueMethod = optionalInt->findFunction(QLatin1String("value")); QVERIFY(valueMethod); QCOMPARE(valueMethod->type()->cppSignature(), QLatin1String("int")); + // ditto for typesystem XML + const AbstractMetaFunction *xmlValueMethod = + xmlOptionalInt->findFunction(QLatin1String("value")); + QVERIFY(xmlValueMethod); + QCOMPARE(xmlValueMethod->type()->cppSignature(), QLatin1String("int")); + // Check whether the m_value field is of type 'int' const AbstractMetaField *valueField = optionalInt->findField(QLatin1String("m_value")); QVERIFY(valueField); QCOMPARE(valueField->type()->cppSignature(), QLatin1String("int")); + + // ditto for typesystem XML + const AbstractMetaField *xmlValueField = + xmlOptionalInt->findField(QLatin1String("m_value")); + QVERIFY(xmlValueField); + QCOMPARE(xmlValueField->type()->cppSignature(), QLatin1String("int")); } QTEST_APPLESS_MAIN(TestTemplates) diff --git a/sources/shiboken2/ApiExtractor/typedatabase.cpp b/sources/shiboken2/ApiExtractor/typedatabase.cpp index 77b1ed23d..9639e4f67 100644 --- a/sources/shiboken2/ApiExtractor/typedatabase.cpp +++ b/sources/shiboken2/ApiExtractor/typedatabase.cpp @@ -307,9 +307,52 @@ bool TypeDatabase::isEnumRejected(const QString& className, const QString& enumN return findRejection(m_rejections, TypeRejection::Enum, className, enumName, reason); } -void TypeDatabase::addType(TypeEntry *e) +TypeEntry *TypeDatabase::resolveTypeDefEntry(TypedefEntry *typedefEntry, + QString *errorMessage) +{ + QString sourceName = typedefEntry->sourceType(); + const int lessThanPos = sourceName.indexOf(QLatin1Char('<')); + if (lessThanPos != -1) + sourceName.truncate(lessThanPos); + ComplexTypeEntry *source = nullptr; + for (TypeEntry *e : findTypes(sourceName)) { + switch (e->type()) { + case TypeEntry::BasicValueType: + case TypeEntry::ContainerType: + case TypeEntry::InterfaceType: + case TypeEntry::ObjectType: + case TypeEntry::SmartPointerType: + source = dynamic_cast(e); + Q_ASSERT(source); + break; + default: + break; + } + } + if (!source) { + if (errorMessage) + *errorMessage = QLatin1String("Unable to resolve typedef \"") + + typedefEntry->sourceType() + QLatin1Char('"'); + return nullptr; + } + + ComplexTypeEntry *result = static_cast(source->clone()); + result->useAsTypedef(typedefEntry); + typedefEntry->setSource(source); + typedefEntry->setTarget(result); + m_typedefEntries.insert(typedefEntry->qualifiedCppName(), typedefEntry); + return result; +} + +bool TypeDatabase::addType(TypeEntry *e, QString *errorMessage) { + if (e->type() == TypeEntry::TypedefType) { + e = resolveTypeDefEntry(static_cast(e), errorMessage); + if (Q_UNLIKELY(!e)) + return false; + } m_entries.insert(e->qualifiedCppName(), e); + return true; } bool TypeDatabase::isFunctionRejected(const QString& className, const QString& functionName, @@ -745,6 +788,13 @@ void ComplexTypeEntry::formatDebug(QDebug &d) const FORMAT_LIST_SIZE("fieldMods", m_fieldMods) } +void TypedefEntry::formatDebug(QDebug &d) const +{ + ComplexTypeEntry::formatDebug(d); + d << ", sourceType=\"" << m_sourceType << '"' + << ", source=" << m_source << ", target=" << m_target; +} + void EnumTypeEntry::formatDebug(QDebug &d) const { TypeEntry::formatDebug(d); diff --git a/sources/shiboken2/ApiExtractor/typedatabase.h b/sources/shiboken2/ApiExtractor/typedatabase.h index b37efedc8..42d41be46 100644 --- a/sources/shiboken2/ApiExtractor/typedatabase.h +++ b/sources/shiboken2/ApiExtractor/typedatabase.h @@ -95,6 +95,7 @@ public: TypeEntry* findType(const QString& name) const; const TypeEntryMultiMap &entries() const { return m_entries; } + const TypedefEntryMap &typedefEntries() const { return m_typedefEntries; } PrimitiveTypeEntryList primitiveTypes() const; @@ -113,7 +114,7 @@ public: bool isReturnTypeRejected(const QString& className, const QString& typeName, QString *reason = nullptr) const; - void addType(TypeEntry* e); + bool addType(TypeEntry* e, QString *errorMessage = nullptr); FlagsTypeEntry* findFlagsType(const QString& name) const; void addFlagsType(FlagsTypeEntry* fte); @@ -162,10 +163,12 @@ public: #endif private: TypeEntryMultiMapConstIteratorRange findTypes(const QString &name) const; + TypeEntry *resolveTypeDefEntry(TypedefEntry *typedefEntry, QString *errorMessage); bool m_suppressWarnings; TypeEntryMultiMap m_entries; TypeEntryMap m_flagsEntries; + TypedefEntryMap m_typedefEntries; TemplateEntryMap m_templates; QVector m_suppressedWarnings; diff --git a/sources/shiboken2/ApiExtractor/typedatabase_typedefs.h b/sources/shiboken2/ApiExtractor/typedatabase_typedefs.h index 8d3862d9d..fbbbabe43 100644 --- a/sources/shiboken2/ApiExtractor/typedatabase_typedefs.h +++ b/sources/shiboken2/ApiExtractor/typedatabase_typedefs.h @@ -37,6 +37,7 @@ class ContainerTypeEntry; class PrimitiveTypeEntry; class TemplateEntry; class TypeEntry; +class TypedefEntry; typedef QVector TypeEntryList; typedef QMap TemplateEntryMap; @@ -57,6 +58,7 @@ typedef QMultiMap TypeEntryMultiMap; typedef QMultiMapConstIteratorRange TypeEntryMultiMapConstIteratorRange; typedef QMap TypeEntryMap; +typedef QMap TypedefEntryMap; typedef QVector ContainerTypeEntryList; typedef QVector PrimitiveTypeEntryList; diff --git a/sources/shiboken2/ApiExtractor/typesystem.cpp b/sources/shiboken2/ApiExtractor/typesystem.cpp index 586a20cd9..3a7fbccd6 100644 --- a/sources/shiboken2/ApiExtractor/typesystem.cpp +++ b/sources/shiboken2/ApiExtractor/typesystem.cpp @@ -91,6 +91,7 @@ static inline QString toAttribute() { return QStringLiteral("to"); } static inline QString signatureAttribute() { return QStringLiteral("signature"); } static inline QString staticAttribute() { return QStringLiteral("static"); } static inline QString threadAttribute() { return QStringLiteral("thread"); } +static inline QString sourceAttribute() { return QStringLiteral("source"); } static inline QString streamAttribute() { return QStringLiteral("stream"); } static inline QString xPathAttribute() { return QStringLiteral("xpath"); } static inline QString virtualSlotAttribute() { return QStringLiteral("virtual-slot"); } @@ -348,6 +349,7 @@ ENUM_LOOKUP_BEGIN(StackElement::ElementType, Qt::CaseInsensitive, {QStringViewLiteral("suppress-warning"), StackElement::SuppressedWarning}, {QStringViewLiteral("target-to-native"), StackElement::TargetToNative}, {QStringViewLiteral("template"), StackElement::Template}, + {QStringViewLiteral("typedef-type"), StackElement::TypedefTypeEntry}, {QStringViewLiteral("typesystem"), StackElement::Root}, {QStringViewLiteral("value-type"), StackElement::ValueTypeEntry}, }; @@ -1211,6 +1213,27 @@ FunctionTypeEntry * return result; } +TypedefEntry * + Handler::parseTypedefEntry(const QXmlStreamReader &, const QString &name, + const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + if (m_current && m_current->type != StackElement::Root + && m_current->type != StackElement::NamespaceTypeEntry) { + m_error = QLatin1String("typedef entries must be nested in namespaces or type system."); + return nullptr; + } + const int sourceIndex = indexOfAttribute(*attributes, sourceAttribute()); + if (sourceIndex == -1) { + m_error = msgMissingAttribute(sourceAttribute()); + return nullptr; + } + const QString sourceType = attributes->takeAt(sourceIndex).value().toString(); + auto result = new TypedefEntry(name, sourceType, since); + applyCommonAttributes(result, attributes); + return result; +} + void Handler::applyComplexTypeAttributes(const QXmlStreamReader &reader, ComplexTypeEntry *ctype, QXmlStreamAttributes *attributes) const @@ -2342,13 +2365,18 @@ bool Handler::startElement(const QXmlStreamReader &reader) qPrintable(msgUnimplementedElementWarning(reader, tagName))); } - if (element->type == StackElement::Root - || element->type == StackElement::NamespaceTypeEntry - || element->type == StackElement::InterfaceTypeEntry - || element->type == StackElement::ObjectTypeEntry - || element->type == StackElement::ValueTypeEntry - || element->type == StackElement::PrimitiveTypeEntry) { + switch (element->type) { + case StackElement::Root: + case StackElement::NamespaceTypeEntry: + case StackElement::InterfaceTypeEntry: + case StackElement::ObjectTypeEntry: + case StackElement::ValueTypeEntry: + case StackElement::PrimitiveTypeEntry: + case StackElement::TypedefTypeEntry: m_contextStack.push(new StackElementContext()); + break; + default: + break; } if (element->type & StackElement::TypeEntryMask) { @@ -2485,12 +2513,21 @@ bool Handler::startElement(const QXmlStreamReader &reader) if (Q_UNLIKELY(!element->entry)) return false; break; + case StackElement::TypedefTypeEntry: + if (TypedefEntry *te = parseTypedefEntry(reader, name, since, &attributes)) { + applyComplexTypeAttributes(reader, te, &attributes); + element->entry = te; + } else { + return false; + } + break; default: Q_ASSERT(false); }; if (element->entry) { - m_database->addType(element->entry); + if (!m_database->addType(element->entry, &m_error)) + return false; } else { qCWarning(lcShiboken).noquote().nospace() << QStringLiteral("Type: %1 was rejected by typesystem").arg(name); @@ -2790,6 +2827,16 @@ TypeEntry *ComplexTypeEntry::clone() const return new ComplexTypeEntry(*this); } +// Take over parameters relevant for typedefs +void ComplexTypeEntry::useAsTypedef(const ComplexTypeEntry *source) +{ + TypeEntry::useAsTypedef(source); + m_qualifiedCppName = source->m_qualifiedCppName; + m_targetLangName = source->m_targetLangName; + m_lookupName = source->m_lookupName; + m_targetType = source->m_targetType; +} + ComplexTypeEntry::ComplexTypeEntry(const ComplexTypeEntry &) = default; QString ContainerTypeEntry::targetLangName() const @@ -3241,6 +3288,15 @@ TypeEntry *TypeEntry::clone() const return new TypeEntry(*this); } +// Take over parameters relevant for typedefs +void TypeEntry::useAsTypedef(const TypeEntry *source) +{ + m_name = source->m_name; + m_targetLangPackage = source->m_targetLangPackage; + m_codeGeneration = source->m_codeGeneration; + m_version = source->m_version; +} + TypeEntry::TypeEntry(const TypeEntry &) = default; TypeSystemTypeEntry::TypeSystemTypeEntry(const QString &name, const QVersionNumber &vr) : @@ -3352,6 +3408,22 @@ FlagsTypeEntry::FlagsTypeEntry(const QString &name, const QVersionNumber &vr) : { } +/* A typedef entry allows for specifying template specializations in the + * typesystem XML file. */ +TypedefEntry::TypedefEntry(const QString &name, const QString &sourceType, + const QVersionNumber &vr) : + ComplexTypeEntry(name, TypedefType, vr), + m_sourceType(sourceType) +{ +} + +TypeEntry *TypedefEntry::clone() const +{ + return new TypedefEntry(*this); +} + +TypedefEntry::TypedefEntry(const TypedefEntry &) = default; + ContainerTypeEntry::ContainerTypeEntry(const QString &name, Type type, const QVersionNumber &vr) : ComplexTypeEntry(name, ContainerType, vr), diff --git a/sources/shiboken2/ApiExtractor/typesystem.h b/sources/shiboken2/ApiExtractor/typesystem.h index 51a8ec937..26f94e3ee 100644 --- a/sources/shiboken2/ApiExtractor/typesystem.h +++ b/sources/shiboken2/ApiExtractor/typesystem.h @@ -545,7 +545,8 @@ public: CustomType, TargetLangType, FunctionType, - SmartPointerType + SmartPointerType, + TypedefType }; Q_ENUM(Type) @@ -873,6 +874,8 @@ public: virtual TypeEntry *clone() const; + void useAsTypedef(const TypeEntry *source); + #ifndef QT_NO_DEBUG_STREAM virtual void formatDebug(QDebug &d) const; #endif @@ -1366,6 +1369,8 @@ public: TypeEntry *clone() const override; + void useAsTypedef(const ComplexTypeEntry *source); + #ifndef QT_NO_DEBUG_STREAM void formatDebug(QDebug &d) const override; #endif @@ -1395,6 +1400,36 @@ private: const ComplexTypeEntry* m_baseContainerType = nullptr; }; +class TypedefEntry : public ComplexTypeEntry +{ +public: + explicit TypedefEntry(const QString &name, + const QString &sourceType, + const QVersionNumber &vr); + + QString sourceType() const { return m_sourceType; } + void setSourceType(const QString &s) { m_sourceType =s; } + + TypeEntry *clone() const override; + + ComplexTypeEntry *source() const { return m_source; } + void setSource(ComplexTypeEntry *source) { m_source = source; } + + ComplexTypeEntry *target() const { return m_target; } + void setTarget(ComplexTypeEntry *target) { m_target = target; } + +#ifndef QT_NO_DEBUG_STREAM + virtual void formatDebug(QDebug &d) const override; +#endif +protected: + TypedefEntry(const TypedefEntry &); + +private: + QString m_sourceType; + ComplexTypeEntry *m_source; + ComplexTypeEntry *m_target; +}; + class ContainerTypeEntry : public ComplexTypeEntry { Q_GADGET diff --git a/sources/shiboken2/ApiExtractor/typesystem_p.h b/sources/shiboken2/ApiExtractor/typesystem_p.h index d54c4cb65..4f778c6fa 100644 --- a/sources/shiboken2/ApiExtractor/typesystem_p.h +++ b/sources/shiboken2/ApiExtractor/typesystem_p.h @@ -55,6 +55,7 @@ class StackElement FunctionTypeEntry = 0xb, CustomTypeEntry = 0xc, SmartPointerTypeEntry = 0xd, + TypedefTypeEntry = 0xe, TypeEntryMask = 0xf, // Documentation tags @@ -177,6 +178,9 @@ private: FunctionTypeEntry * parseFunctionTypeEntry(const QXmlStreamReader &, const QString &name, const QVersionNumber &since, QXmlStreamAttributes *); + TypedefEntry * + parseTypedefEntry(const QXmlStreamReader &, const QString &name, + const QVersionNumber &since, QXmlStreamAttributes *); void applyComplexTypeAttributes(const QXmlStreamReader &, ComplexTypeEntry *ctype, QXmlStreamAttributes *) const; bool parseRenameFunction(const QXmlStreamReader &, QString *name, diff --git a/sources/shiboken2/generator/shiboken2/headergenerator.cpp b/sources/shiboken2/generator/shiboken2/headergenerator.cpp index d240cacbf..9bb5fafde 100644 --- a/sources/shiboken2/generator/shiboken2/headergenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/headergenerator.cpp @@ -334,6 +334,33 @@ void HeaderGenerator::writeTypeIndexValueLines(QTextStream& s, const AbstractMet } } +// Format the typedefs for the typedef entries to be generated +static void formatTypeDefEntries(QTextStream &s) +{ + QVector entries; + const auto typeDbEntries = TypeDatabase::instance()->typedefEntries(); + for (auto it = typeDbEntries.cbegin(), end = typeDbEntries.cend(); it != end; ++it) { + if (it.value()->generateCode() != 0) + entries.append(it.value()); + } + if (entries.isEmpty()) + return; + s << "\n// typedef entries\n"; + for (const auto e : entries) { + const QString name = e->qualifiedCppName(); + // Fixme: simplify by using nested namespaces in C++ 17. + const auto components = name.splitRef(QLatin1String("::")); + const int nameSpaceCount = components.size() - 1; + for (int n = 0; n < nameSpaceCount; ++n) + s << "namespace " << components.at(n) << " {\n"; + s << "using " << components.constLast() << " = " << e->sourceType() << ";\n"; + for (int n = 0; n < nameSpaceCount; ++n) + s << "}\n"; + } + s << '\n'; +} + + bool HeaderGenerator::finishGeneration() { // Generate the main header for this module. @@ -413,6 +440,9 @@ bool HeaderGenerator::finishGeneration() _writeTypeIndexValue(macrosStream, QStringLiteral("SBK_%1_CONVERTERS_IDX_COUNT") .arg(moduleName()), pCount); macrosStream << "\n};\n"; + + formatTypeDefEntries(macrosStream); + // TODO-CONVERTER ------------------------------------------------------------------------------ macrosStream << "// Macros for type check" << endl; -- cgit v1.2.3