From 363e1e3139585cf8da4970cd0d90938b8163e837 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 4 Jul 2019 09:59:56 +0200 Subject: shiboken: Move typesystem parser out of typesystem.cpp The file is too large to be maintainable. Task-number: PYSIDE-1024 Change-Id: I9e95ede1b7e7ce804059ef510dcec9dc9e773a48 Reviewed-by: Christian Tismer --- sources/shiboken2/ApiExtractor/CMakeLists.txt | 1 + sources/shiboken2/ApiExtractor/typedatabase.cpp | 6 +- sources/shiboken2/ApiExtractor/typesystem.cpp | 2942 +------------------ sources/shiboken2/ApiExtractor/typesystem_p.h | 273 -- .../shiboken2/ApiExtractor/typesystemparser.cpp | 2974 ++++++++++++++++++++ sources/shiboken2/ApiExtractor/typesystemparser.h | 274 ++ 6 files changed, 3253 insertions(+), 3217 deletions(-) delete mode 100644 sources/shiboken2/ApiExtractor/typesystem_p.h create mode 100644 sources/shiboken2/ApiExtractor/typesystemparser.cpp create mode 100644 sources/shiboken2/ApiExtractor/typesystemparser.h diff --git a/sources/shiboken2/ApiExtractor/CMakeLists.txt b/sources/shiboken2/ApiExtractor/CMakeLists.txt index 6cc8cd583..c8a5fb336 100644 --- a/sources/shiboken2/ApiExtractor/CMakeLists.txt +++ b/sources/shiboken2/ApiExtractor/CMakeLists.txt @@ -15,6 +15,7 @@ messages.cpp reporthandler.cpp typeparser.cpp typesystem.cpp +typesystemparser.cpp include.cpp typedatabase.cpp # Clang diff --git a/sources/shiboken2/ApiExtractor/typedatabase.cpp b/sources/shiboken2/ApiExtractor/typedatabase.cpp index 144795c6a..4f8887bd2 100644 --- a/sources/shiboken2/ApiExtractor/typedatabase.cpp +++ b/sources/shiboken2/ApiExtractor/typedatabase.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt for Python. @@ -28,7 +28,7 @@ #include "typedatabase.h" #include "typesystem.h" -#include "typesystem_p.h" +#include "typesystemparser.h" #include #include @@ -572,7 +572,7 @@ bool TypeDatabase::parseFile(const QString &filename, const QString ¤tPath bool TypeDatabase::parseFile(QIODevice* device, bool generate) { QXmlStreamReader reader(device); - Handler handler(this, generate); + TypeSystemParser handler(this, generate); const bool result = handler.parse(reader); if (!result) qCWarning(lcShiboken, "%s", qPrintable(handler.errorString())); diff --git a/sources/shiboken2/ApiExtractor/typesystem.cpp b/sources/shiboken2/ApiExtractor/typesystem.cpp index 65e3443da..fa141670c 100644 --- a/sources/shiboken2/ApiExtractor/typesystem.cpp +++ b/sources/shiboken2/ApiExtractor/typesystem.cpp @@ -27,2962 +27,22 @@ ****************************************************************************/ #include "typesystem.h" -#include "typesystem_p.h" #include "typedatabase.h" #include "messages.h" -#include "reporthandler.h" -#include -#include -#include +#include #include #include -#include -#include -#include -#include -#include #include -const char *TARGET_CONVERSION_RULE_FLAG = "0"; -const char *NATIVE_CONVERSION_RULE_FLAG = "1"; - static QString strings_Object = QLatin1String("Object"); static QString strings_String = QLatin1String("String"); static QString strings_char = QLatin1String("char"); static QString strings_jchar = QLatin1String("jchar"); static QString strings_jobject = QLatin1String("jobject"); -static inline QString allowThreadAttribute() { return QStringLiteral("allow-thread"); } -static inline QString colonColon() { return QStringLiteral("::"); } -static inline QString copyableAttribute() { return QStringLiteral("copyable"); } -static inline QString accessAttribute() { return QStringLiteral("access"); } -static inline QString actionAttribute() { return QStringLiteral("action"); } -static inline QString quoteAfterLineAttribute() { return QStringLiteral("quote-after-line"); } -static inline QString quoteBeforeLineAttribute() { return QStringLiteral("quote-before-line"); } -static inline QString textAttribute() { return QStringLiteral("text"); } -static inline QString nameAttribute() { return QStringLiteral("name"); } -static inline QString sinceAttribute() { return QStringLiteral("since"); } -static inline QString defaultSuperclassAttribute() { return QStringLiteral("default-superclass"); } -static inline QString deleteInMainThreadAttribute() { return QStringLiteral("delete-in-main-thread"); } -static inline QString deprecatedAttribute() { return QStringLiteral("deprecated"); } -static inline QString exceptionHandlingAttribute() { return QStringLiteral("exception-handling"); } -static inline QString extensibleAttribute() { return QStringLiteral("extensible"); } -static inline QString flagsAttribute() { return QStringLiteral("flags"); } -static inline QString forceAbstractAttribute() { return QStringLiteral("force-abstract"); } -static inline QString forceIntegerAttribute() { return QStringLiteral("force-integer"); } -static inline QString formatAttribute() { return QStringLiteral("format"); } -static inline QString classAttribute() { return QStringLiteral("class"); } -static inline QString generateAttribute() { return QStringLiteral("generate"); } -static inline QString genericClassAttribute() { return QStringLiteral("generic-class"); } -static inline QString indexAttribute() { return QStringLiteral("index"); } -static inline QString invalidateAfterUseAttribute() { return QStringLiteral("invalidate-after-use"); } -static inline QString locationAttribute() { return QStringLiteral("location"); } -static inline QString modifiedTypeAttribute() { return QStringLiteral("modified-type"); } -static inline QString modifierAttribute() { return QStringLiteral("modifier"); } -static inline QString ownershipAttribute() { return QStringLiteral("owner"); } -static inline QString packageAttribute() { return QStringLiteral("package"); } -static inline QString positionAttribute() { return QStringLiteral("position"); } -static inline QString preferredConversionAttribute() { return QStringLiteral("preferred-conversion"); } -static inline QString preferredTargetLangTypeAttribute() { return QStringLiteral("preferred-target-lang-type"); } -static inline QString removeAttribute() { return QStringLiteral("remove"); } -static inline QString renameAttribute() { return QStringLiteral("rename"); } -static inline QString readAttribute() { return QStringLiteral("read"); } -static inline QString writeAttribute() { return QStringLiteral("write"); } -static inline QString replaceAttribute() { return QStringLiteral("replace"); } -static inline QString toAttribute() { return QStringLiteral("to"); } -static inline QString signatureAttribute() { return QStringLiteral("signature"); } -static inline QString snippetAttribute() { return QStringLiteral("snippet"); } -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"); } -static inline QString enumIdentifiedByValueAttribute() { return QStringLiteral("identified-by-value"); } - -static inline QString noAttributeValue() { return QStringLiteral("no"); } -static inline QString yesAttributeValue() { return QStringLiteral("yes"); } -static inline QString trueAttributeValue() { return QStringLiteral("true"); } -static inline QString falseAttributeValue() { return QStringLiteral("false"); } - static inline QString callOperator() { return QStringLiteral("operator()"); } -static QVector customConversionsForReview; - -// Set a regular expression for rejection from text. By legacy, those are fixed -// strings, except for '*' meaning 'match all'. Enclosing in "^..$" -// indicates regular expression. -static bool setRejectionRegularExpression(const QString &patternIn, - QRegularExpression *re, - QString *errorMessage) -{ - QString pattern; - if (patternIn.startsWith(QLatin1Char('^')) && patternIn.endsWith(QLatin1Char('$'))) - pattern = patternIn; - else if (patternIn == QLatin1String("*")) - pattern = QStringLiteral("^.*$"); - else - pattern = QLatin1Char('^') + QRegularExpression::escape(patternIn) + QLatin1Char('$'); - re->setPattern(pattern); - if (!re->isValid()) { - *errorMessage = msgInvalidRegularExpression(patternIn, re->errorString()); - return false; - } - return true; -} - -// Extract a snippet from a file within annotation "// @snippet label". -static QString extractSnippet(const QString &code, const QString &snippetLabel) -{ - if (snippetLabel.isEmpty()) - return code; - const QString pattern = QStringLiteral(R"(^\s*//\s*@snippet\s+)") - + QRegularExpression::escape(snippetLabel) - + QStringLiteral(R"(\s*$)"); - const QRegularExpression snippetRe(pattern); - Q_ASSERT(snippetRe.isValid()); - - bool useLine = false; - QString result; - const auto lines = code.splitRef(QLatin1Char('\n')); - for (const QStringRef &line : lines) { - if (snippetRe.match(line).hasMatch()) { - useLine = !useLine; - if (!useLine) - break; // End of snippet reached - } else if (useLine) - result += line.toString() + QLatin1Char('\n'); - } - return result; -} - -template -struct EnumLookup -{ - QStringView name; - EnumType value; -}; - -template -bool operator==(const EnumLookup &e1, const EnumLookup &e2) -{ - return e1.name.compare(e2.name, cs) == 0; -} - -template -bool operator<(const EnumLookup &e1, const EnumLookup &e2) -{ - return e1.name.compare(e2.name, cs) < 0; -} - -// Helper macros to define lookup functions that take a QStringView needle -// and an optional default return value. -#define ENUM_LOOKUP_BEGIN(EnumType, caseSensitivity, functionName, defaultReturnValue) \ -static EnumType functionName(QStringView needle, EnumType defaultValue = defaultReturnValue) \ -{ \ - typedef EnumLookup HaystackEntry; \ - static const HaystackEntry haystack[] = - -#define ENUM_LOOKUP_LINEAR_SEARCH() \ - const auto end = haystack + sizeof(haystack) / sizeof(haystack[0]); \ - const auto it = std::find(haystack, end, HaystackEntry{needle, defaultValue}); \ - return it != end ? it->value : defaultValue; \ -} - -#define ENUM_LOOKUP_BINARY_SEARCH() \ - const auto end = haystack + sizeof(haystack) / sizeof(haystack[0]); \ - const HaystackEntry needleEntry{needle, defaultValue}; \ - const auto lb = std::lower_bound(haystack, end, needleEntry); \ - return lb != end && *lb == needleEntry ? lb->value : defaultValue; \ -} - -ENUM_LOOKUP_BEGIN(TypeSystem::AllowThread, Qt::CaseInsensitive, - allowThreadFromAttribute, TypeSystem::AllowThread::Unspecified) - { - {QStringViewLiteral("yes"), TypeSystem::AllowThread::Allow}, - {QStringViewLiteral("true"), TypeSystem::AllowThread::Allow}, - {QStringViewLiteral("auto"), TypeSystem::AllowThread::Auto}, - {QStringViewLiteral("no"), TypeSystem::AllowThread::Disallow}, - {QStringViewLiteral("false"), TypeSystem::AllowThread::Disallow}, - }; -ENUM_LOOKUP_LINEAR_SEARCH() - -ENUM_LOOKUP_BEGIN(TypeSystem::Language, Qt::CaseInsensitive, - languageFromAttribute, TypeSystem::NoLanguage) - { - {QStringViewLiteral("all"), TypeSystem::All}, // sorted! - {QStringViewLiteral("constructors"), TypeSystem::Constructors}, - {QStringViewLiteral("destructor-function"), TypeSystem::DestructorFunction}, - {QStringViewLiteral("interface"), TypeSystem::Interface}, - {QStringViewLiteral("library-initializer"), TypeSystem::PackageInitializer}, - {QStringViewLiteral("native"), TypeSystem::NativeCode}, // em algum lugar do cpp - {QStringViewLiteral("shell"), TypeSystem::ShellCode}, // coloca no header, mas antes da declaracao da classe - {QStringViewLiteral("shell-declaration"), TypeSystem::ShellDeclaration}, - {QStringViewLiteral("target"), TypeSystem::TargetLangCode} // em algum lugar do cpp - }; -ENUM_LOOKUP_BINARY_SEARCH() - -ENUM_LOOKUP_BEGIN(TypeSystem::Ownership, Qt::CaseInsensitive, - ownershipFromFromAttribute, TypeSystem::InvalidOwnership) - { - {QStringViewLiteral("target"), TypeSystem::TargetLangOwnership}, - {QStringViewLiteral("c++"), TypeSystem::CppOwnership}, - {QStringViewLiteral("default"), TypeSystem::DefaultOwnership} - }; -ENUM_LOOKUP_LINEAR_SEARCH() - -ENUM_LOOKUP_BEGIN(AddedFunction::Access, Qt::CaseInsensitive, - addedFunctionAccessFromAttribute, AddedFunction::InvalidAccess) - { - {QStringViewLiteral("public"), AddedFunction::Public}, - {QStringViewLiteral("protected"), AddedFunction::Protected}, - }; -ENUM_LOOKUP_LINEAR_SEARCH() - -ENUM_LOOKUP_BEGIN(Modification::Modifiers, Qt::CaseSensitive, - modifierFromAttribute, Modification::InvalidModifier) - { - {QStringViewLiteral("private"), Modification::Private}, - {QStringViewLiteral("public"), Modification::Public}, - {QStringViewLiteral("protected"), Modification::Protected}, - {QStringViewLiteral("friendly"), Modification::Friendly}, - {QStringViewLiteral("rename"), Modification::Rename}, - {QStringViewLiteral("final"), Modification::Final}, - {QStringViewLiteral("non-final"), Modification::NonFinal} - }; -ENUM_LOOKUP_LINEAR_SEARCH() - -ENUM_LOOKUP_BEGIN(ReferenceCount::Action, Qt::CaseInsensitive, - referenceCountFromAttribute, ReferenceCount::Invalid) - { - {QStringViewLiteral("add"), ReferenceCount::Add}, - {QStringViewLiteral("add-all"), ReferenceCount::AddAll}, - {QStringViewLiteral("remove"), ReferenceCount::Remove}, - {QStringViewLiteral("set"), ReferenceCount::Set}, - {QStringViewLiteral("ignore"), ReferenceCount::Ignore} - }; -ENUM_LOOKUP_LINEAR_SEARCH() - -ENUM_LOOKUP_BEGIN(ArgumentOwner::Action, Qt::CaseInsensitive, - argumentOwnerActionFromAttribute, ArgumentOwner::Invalid) - { - {QStringViewLiteral("add"), ArgumentOwner::Add}, - {QStringViewLiteral("remove"), ArgumentOwner::Remove} - }; -ENUM_LOOKUP_LINEAR_SEARCH() - -ENUM_LOOKUP_BEGIN(TypeSystem::CodeSnipPosition, Qt::CaseInsensitive, - codeSnipPositionFromAttribute, TypeSystem::CodeSnipPositionInvalid) - { - {QStringViewLiteral("beginning"), TypeSystem::CodeSnipPositionBeginning}, - {QStringViewLiteral("end"), TypeSystem::CodeSnipPositionEnd}, - {QStringViewLiteral("declaration"), TypeSystem::CodeSnipPositionDeclaration}, - {QStringViewLiteral("prototype-initialization"), TypeSystem::CodeSnipPositionPrototypeInitialization}, - {QStringViewLiteral("constructor-initialization"), TypeSystem::CodeSnipPositionConstructorInitialization}, - {QStringViewLiteral("constructor"), TypeSystem::CodeSnipPositionConstructor} - }; -ENUM_LOOKUP_LINEAR_SEARCH() - -ENUM_LOOKUP_BEGIN(Include::IncludeType, Qt::CaseInsensitive, - locationFromAttribute, Include::InvalidInclude) - { - {QStringViewLiteral("global"), Include::IncludePath}, - {QStringViewLiteral("local"), Include::LocalPath}, - {QStringViewLiteral("target"), Include::TargetLangImport} - }; -ENUM_LOOKUP_LINEAR_SEARCH() - -ENUM_LOOKUP_BEGIN(TypeSystem::DocModificationMode, Qt::CaseInsensitive, - docModificationFromAttribute, TypeSystem::DocModificationInvalid) - { - {QStringViewLiteral("append"), TypeSystem::DocModificationAppend}, - {QStringViewLiteral("prepend"), TypeSystem::DocModificationPrepend}, - {QStringViewLiteral("replace"), TypeSystem::DocModificationReplace} - }; -ENUM_LOOKUP_LINEAR_SEARCH() - -ENUM_LOOKUP_BEGIN(ContainerTypeEntry::Type, Qt::CaseSensitive, - containerTypeFromAttribute, ContainerTypeEntry::NoContainer) - { - {QStringViewLiteral("list"), ContainerTypeEntry::ListContainer}, - {QStringViewLiteral("string-list"), ContainerTypeEntry::StringListContainer}, - {QStringViewLiteral("linked-list"), ContainerTypeEntry::LinkedListContainer}, - {QStringViewLiteral("vector"), ContainerTypeEntry::VectorContainer}, - {QStringViewLiteral("stack"), ContainerTypeEntry::StackContainer}, - {QStringViewLiteral("queue"), ContainerTypeEntry::QueueContainer}, - {QStringViewLiteral("set"), ContainerTypeEntry::SetContainer}, - {QStringViewLiteral("map"), ContainerTypeEntry::MapContainer}, - {QStringViewLiteral("multi-map"), ContainerTypeEntry::MultiMapContainer}, - {QStringViewLiteral("hash"), ContainerTypeEntry::HashContainer}, - {QStringViewLiteral("multi-hash"), ContainerTypeEntry::MultiHashContainer}, - {QStringViewLiteral("pair"), ContainerTypeEntry::PairContainer} - }; -ENUM_LOOKUP_LINEAR_SEARCH() - -ENUM_LOOKUP_BEGIN(TypeRejection::MatchType, Qt::CaseSensitive, - typeRejectionFromAttribute, TypeRejection::Invalid) - { - {QStringViewLiteral("class"), TypeRejection::ExcludeClass}, - {QStringViewLiteral("function-name"), TypeRejection::Function}, - {QStringViewLiteral("field-name"), TypeRejection::Field}, - {QStringViewLiteral("enum-name"), TypeRejection::Enum }, - {QStringViewLiteral("argument-type"), TypeRejection::ArgumentType}, - {QStringViewLiteral("return-type"), TypeRejection::ReturnType} - }; -ENUM_LOOKUP_LINEAR_SEARCH() - -ENUM_LOOKUP_BEGIN(TypeSystem::ExceptionHandling, Qt::CaseSensitive, - exceptionHandlingFromAttribute, TypeSystem::ExceptionHandling::Unspecified) -{ - {QStringViewLiteral("no"), TypeSystem::ExceptionHandling::Off}, - {QStringViewLiteral("false"), TypeSystem::ExceptionHandling::Off}, - {QStringViewLiteral("auto-off"), TypeSystem::ExceptionHandling::AutoDefaultToOff}, - {QStringViewLiteral("auto-on"), TypeSystem::ExceptionHandling::AutoDefaultToOn}, - {QStringViewLiteral("yes"), TypeSystem::ExceptionHandling::On}, - {QStringViewLiteral("true"), TypeSystem::ExceptionHandling::On}, -}; -ENUM_LOOKUP_LINEAR_SEARCH() - -ENUM_LOOKUP_BEGIN(StackElement::ElementType, Qt::CaseInsensitive, - elementFromTag, StackElement::None) - { - {QStringViewLiteral("access"), StackElement::Access}, // sorted! - {QStringViewLiteral("add-conversion"), StackElement::AddConversion}, - {QStringViewLiteral("add-function"), StackElement::AddFunction}, - {QStringViewLiteral("argument-map"), StackElement::ArgumentMap}, - {QStringViewLiteral("array"), StackElement::Array}, - {QStringViewLiteral("container-type"), StackElement::ContainerTypeEntry}, - {QStringViewLiteral("conversion-rule"), StackElement::ConversionRule}, - {QStringViewLiteral("custom-constructor"), StackElement::CustomMetaConstructor}, - {QStringViewLiteral("custom-destructor"), StackElement::CustomMetaDestructor}, - {QStringViewLiteral("custom-type"), StackElement::CustomTypeEntry}, - {QStringViewLiteral("define-ownership"), StackElement::DefineOwnership}, - {QStringViewLiteral("enum-type"), StackElement::EnumTypeEntry}, - {QStringViewLiteral("extra-includes"), StackElement::ExtraIncludes}, - {QStringViewLiteral("function"), StackElement::FunctionTypeEntry}, - {QStringViewLiteral("include"), StackElement::Include}, - {QStringViewLiteral("inject-code"), StackElement::InjectCode}, - {QStringViewLiteral("inject-documentation"), StackElement::InjectDocumentation}, - {QStringViewLiteral("insert-template"), StackElement::TemplateInstanceEnum}, - {QStringViewLiteral("interface-type"), StackElement::InterfaceTypeEntry}, - {QStringViewLiteral("load-typesystem"), StackElement::LoadTypesystem}, - {QStringViewLiteral("modify-argument"), StackElement::ModifyArgument}, - {QStringViewLiteral("modify-documentation"), StackElement::ModifyDocumentation}, - {QStringViewLiteral("modify-field"), StackElement::ModifyField}, - {QStringViewLiteral("modify-function"), StackElement::ModifyFunction}, - {QStringViewLiteral("namespace-type"), StackElement::NamespaceTypeEntry}, - {QStringViewLiteral("native-to-target"), StackElement::NativeToTarget}, - {QStringViewLiteral("no-null-pointer"), StackElement::NoNullPointers}, - {QStringViewLiteral("object-type"), StackElement::ObjectTypeEntry}, - {QStringViewLiteral("parent"), StackElement::ParentOwner}, - {QStringViewLiteral("primitive-type"), StackElement::PrimitiveTypeEntry}, - {QStringViewLiteral("reference-count"), StackElement::ReferenceCount}, - {QStringViewLiteral("reject-enum-value"), StackElement::RejectEnumValue}, - {QStringViewLiteral("rejection"), StackElement::Rejection}, - {QStringViewLiteral("remove"), StackElement::Removal}, - {QStringViewLiteral("remove-argument"), StackElement::RemoveArgument}, - {QStringViewLiteral("remove-default-expression"), StackElement::RemoveDefaultExpression}, - {QStringViewLiteral("rename"), StackElement::Rename}, - {QStringViewLiteral("replace"), StackElement::Replace}, - {QStringViewLiteral("replace-default-expression"), StackElement::ReplaceDefaultExpression}, - {QStringViewLiteral("replace-type"), StackElement::ReplaceType}, - {QStringViewLiteral("smart-pointer-type"), StackElement::SmartPointerTypeEntry}, - {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}, - }; -ENUM_LOOKUP_BINARY_SEARCH() - -static int indexOfAttribute(const QXmlStreamAttributes &atts, - QStringView name) -{ - for (int i = 0, size = atts.size(); i < size; ++i) { - if (atts.at(i).qualifiedName() == name) - return i; - } - return -1; -} - -static QString msgMissingAttribute(const QString &a) -{ - return QLatin1String("Required attribute '") + a - + QLatin1String("' missing."); -} - -QTextStream &operator<<(QTextStream &str, const QXmlStreamAttribute &attribute) -{ - str << attribute.qualifiedName() << "=\"" << attribute.value() << '"'; - return str; -} - -static QString msgInvalidAttributeValue(const QXmlStreamAttribute &attribute) -{ - QString result; - QTextStream(&result) << "Invalid attribute value:" << attribute; - return result; -} - -static QString msgUnusedAttributes(const QStringRef &tag, const QXmlStreamAttributes &attributes) -{ - QString result; - QTextStream str(&result); - str << attributes.size() << " attributes(s) unused on <" << tag << ">: "; - for (int i = 0, size = attributes.size(); i < size; ++i) { - if (i) - str << ", "; - str << attributes.at(i); - } - return result; -} - -// QXmlStreamEntityResolver::resolveEntity(publicId, systemId) is not -// implemented; resolve via undeclared entities instead. -class TypeSystemEntityResolver : public QXmlStreamEntityResolver -{ -public: - explicit TypeSystemEntityResolver(const QString ¤tPath) : - m_currentPath(currentPath) {} - - QString resolveUndeclaredEntity(const QString &name) override; - -private: - QString readFile(const QString &entityName, QString *errorMessage) const; - - const QString m_currentPath; - QHash m_cache; -}; - -QString TypeSystemEntityResolver::readFile(const QString &entityName, QString *errorMessage) const -{ - QString fileName = entityName; - if (!fileName.contains(QLatin1Char('.'))) - fileName += QLatin1String(".xml"); - QString path = TypeDatabase::instance()->modifiedTypesystemFilepath(fileName, m_currentPath); - if (!QFileInfo::exists(path)) // PySide2-specific hack - fileName.prepend(QLatin1String("typesystem_")); - path = TypeDatabase::instance()->modifiedTypesystemFilepath(fileName, m_currentPath); - if (!QFileInfo::exists(path)) { - *errorMessage = QLatin1String("Unable to resolve: ") + entityName; - return QString(); - } - QFile file(path); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - *errorMessage = msgCannotOpenForReading(file); - return QString(); - } - QString result = QString::fromUtf8(file.readAll()).trimmed(); - // Remove license header comments on which QXmlStreamReader chokes - if (result.startsWith(QLatin1String("")); - if (commentEnd != -1) { - result.remove(0, commentEnd + 3); - result = result.trimmed(); - } - } - return result; -} - -QString TypeSystemEntityResolver::resolveUndeclaredEntity(const QString &name) -{ - auto it = m_cache.find(name); - if (it == m_cache.end()) { - QString errorMessage; - it = m_cache.insert(name, readFile(name, &errorMessage)); - if (it.value().isEmpty()) { // The parser will fail and display the line number. - qCWarning(lcShiboken, "%s", - qPrintable(msgCannotResolveEntity(name, errorMessage))); - } - } - return it.value(); -} - -Handler::Handler(TypeDatabase *database, bool generate) : - m_database(database), - m_generate(generate ? TypeEntry::GenerateAll : TypeEntry::GenerateForSubclass) -{ -} - -Handler::~Handler() = default; - -static QString readerFileName(const QXmlStreamReader &reader) -{ - const auto *file = qobject_cast(reader.device()); - return file != nullptr ? file->fileName() : QString(); -} - -static QString msgReaderMessage(const QXmlStreamReader &reader, - const char *type, - const QString &what) -{ - QString message; - QTextStream str(&message); - str << type << ": "; - const QString fileName = readerFileName(reader); - if (fileName.isEmpty()) - str << ":"; - else - str << QDir::toNativeSeparators(fileName) << ':'; - str << reader.lineNumber() << ':' << reader.columnNumber() - << ": " << what; - return message; -} - -static QString msgReaderWarning(const QXmlStreamReader &reader, const QString &what) -{ - return msgReaderMessage(reader, "Warning", what); -} - -static QString msgReaderError(const QXmlStreamReader &reader, const QString &what) -{ - return msgReaderMessage(reader, "Error", what); -} - -static QString msgUnimplementedElementWarning(const QXmlStreamReader &reader, - const QStringRef &name) -{ - const QString message = QLatin1String("The element \"") + - name + QLatin1String("\" is not implemented."); - return msgReaderMessage(reader, "Warning", message); -} - -static QString msgUnimplementedAttributeWarning(const QXmlStreamReader &reader, - const QStringRef &name) -{ - const QString message = QLatin1String("The attribute \"") + - name + QLatin1String("\" is not implemented."); - return msgReaderMessage(reader, "Warning", message); -} - -static inline QString msgUnimplementedAttributeWarning(const QXmlStreamReader &reader, - const QXmlStreamAttribute &attribute) -{ - return msgUnimplementedAttributeWarning(reader, attribute.qualifiedName()); -} - -static QString - msgUnimplementedAttributeValueWarning(const QXmlStreamReader &reader, - QStringView name, QStringView value) -{ - QString message; - QTextStream(&message) << "The value \"" << value - << "\" of the attribute \"" << name << "\" is not implemented."; - return msgReaderMessage(reader, "Warning", message); -} - -static inline - QString msgUnimplementedAttributeValueWarning(const QXmlStreamReader &reader, - const QXmlStreamAttribute &attribute) -{ - return msgUnimplementedAttributeValueWarning(reader, - attribute.qualifiedName(), - attribute.value()); -} - -static QString msgInvalidVersion(const QStringRef &version, const QString &package = QString()) -{ - QString result; - QTextStream str(&result); - str << "Invalid version \"" << version << '"'; - if (!package.isEmpty()) - str << "\" specified for package " << package; - str << '.'; - return result; -} - -static bool addRejection(TypeDatabase *database, QXmlStreamAttributes *attributes, - QString *errorMessage) -{ - const int classIndex = indexOfAttribute(*attributes, classAttribute()); - if (classIndex == -1) { - *errorMessage = msgMissingAttribute(classAttribute()); - return false; - } - - TypeRejection rejection; - const QString className = attributes->takeAt(classIndex).value().toString(); - if (!setRejectionRegularExpression(className, &rejection.className, errorMessage)) - return false; - - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - const TypeRejection::MatchType type = typeRejectionFromAttribute(name); - switch (type) { - case TypeRejection::Function: - case TypeRejection::Field: - case TypeRejection::Enum: - case TypeRejection::ArgumentType: - case TypeRejection::ReturnType: { - const QString pattern = attributes->takeAt(i).value().toString(); - if (!setRejectionRegularExpression(pattern, &rejection.pattern, errorMessage)) - return false; - rejection.matchType = type; - database->addRejection(rejection); - return true; - } - break; - default: - break; - } - } - - // Special case: When all fields except class are empty, completely exclude class - if (className == QLatin1String("*")) { - *errorMessage = QLatin1String("bad reject entry, neither 'class', 'function-name'" - " nor 'field' specified"); - return false; - } - rejection.matchType = TypeRejection::ExcludeClass; - database->addRejection(rejection); - return true; -} - -bool Handler::parse(QXmlStreamReader &reader) -{ - m_error.clear(); - m_currentPath.clear(); - const QString fileName = readerFileName(reader); - if (!fileName.isEmpty()) - m_currentPath = QFileInfo(fileName).absolutePath(); - m_entityResolver.reset(new TypeSystemEntityResolver(m_currentPath)); - reader.setEntityResolver(m_entityResolver.data()); - - while (!reader.atEnd()) { - switch (reader.readNext()) { - case QXmlStreamReader::NoToken: - case QXmlStreamReader::Invalid: - m_error = msgReaderError(reader, reader.errorString()); - return false; - case QXmlStreamReader::StartElement: - if (!startElement(reader)) { - m_error = msgReaderError(reader, m_error); - return false; - } - - break; - case QXmlStreamReader::EndElement: - if (!endElement(reader.name())) { - m_error = msgReaderError(reader, m_error); - return false; - } - break; - case QXmlStreamReader::Characters: - if (!characters(reader.text())) { - m_error = msgReaderError(reader, m_error); - return false; - } - break; - case QXmlStreamReader::StartDocument: - case QXmlStreamReader::EndDocument: - case QXmlStreamReader::Comment: - case QXmlStreamReader::DTD: - case QXmlStreamReader::EntityReference: - case QXmlStreamReader::ProcessingInstruction: - break; - } - } - return true; -} - -bool Handler::endElement(const QStringRef &localName) -{ - if (m_ignoreDepth) { - --m_ignoreDepth; - return true; - } - - if (m_currentDroppedEntry) { - if (m_currentDroppedEntryDepth == 1) { - m_current = m_currentDroppedEntry->parent; - delete m_currentDroppedEntry; - m_currentDroppedEntry = nullptr; - m_currentDroppedEntryDepth = 0; - } else { - --m_currentDroppedEntryDepth; - } - return true; - } - - if (!localName.compare(QLatin1String("import-file"), Qt::CaseInsensitive)) - return true; - - if (!m_current) - return true; - - switch (m_current->type) { - case StackElement::Root: - if (m_generate == TypeEntry::GenerateAll) { - TypeDatabase::instance()->addGlobalUserFunctions(m_contextStack.top()->addedFunctions); - TypeDatabase::instance()->addGlobalUserFunctionModifications(m_contextStack.top()->functionMods); - for (CustomConversion *customConversion : qAsConst(customConversionsForReview)) { - const CustomConversion::TargetToNativeConversions &toNatives = customConversion->targetToNativeConversions(); - for (CustomConversion::TargetToNativeConversion *toNative : toNatives) - toNative->setSourceType(m_database->findType(toNative->sourceTypeName())); - } - } - break; - case StackElement::ObjectTypeEntry: - case StackElement::ValueTypeEntry: - case StackElement::InterfaceTypeEntry: - case StackElement::NamespaceTypeEntry: { - auto *centry = static_cast(m_current->entry); - centry->setAddedFunctions(m_contextStack.top()->addedFunctions); - centry->setFunctionModifications(m_contextStack.top()->functionMods); - centry->setFieldModifications(m_contextStack.top()->fieldMods); - centry->setCodeSnips(m_contextStack.top()->codeSnips); - centry->setDocModification(m_contextStack.top()->docModifications); - - if (centry->designatedInterface()) { - centry->designatedInterface()->setCodeSnips(m_contextStack.top()->codeSnips); - centry->designatedInterface()->setFunctionModifications(m_contextStack.top()->functionMods); - } - } - break; - case StackElement::AddFunction: { - // Leaving add-function: Assign all modifications to the added function - StackElementContext *top = m_contextStack.top(); - const int modIndex = top->addedFunctionModificationIndex; - top->addedFunctionModificationIndex = -1; - Q_ASSERT(modIndex >= 0); - Q_ASSERT(!top->addedFunctions.isEmpty()); - while (modIndex < top->functionMods.size()) - top->addedFunctions.last()->modifications.append(top->functionMods.takeAt(modIndex)); - } - break; - case StackElement::NativeToTarget: - case StackElement::AddConversion: { - CustomConversion* customConversion = static_cast(m_current->entry)->customConversion(); - if (!customConversion) { - m_error = QLatin1String("CustomConversion object is missing."); - return false; - } - - QString code = m_contextStack.top()->codeSnips.takeLast().code(); - if (m_current->type == StackElement::AddConversion) { - if (customConversion->targetToNativeConversions().isEmpty()) { - m_error = QLatin1String("CustomConversion's target to native conversions missing."); - return false; - } - customConversion->targetToNativeConversions().last()->setConversion(code); - } else { - customConversion->setNativeToTargetConversion(code); - } - } - break; - case StackElement::CustomMetaConstructor: { - m_current->entry->setCustomConstructor(*m_current->value.customFunction); - delete m_current->value.customFunction; - } - break; - case StackElement::CustomMetaDestructor: { - m_current->entry->setCustomDestructor(*m_current->value.customFunction); - delete m_current->value.customFunction; - } - break; - case StackElement::EnumTypeEntry: - m_current->entry->setDocModification(m_contextStack.top()->docModifications); - m_contextStack.top()->docModifications = DocModificationList(); - m_currentEnum = nullptr; - break; - case StackElement::Template: - m_database->addTemplate(m_current->value.templateEntry); - break; - case StackElement::TemplateInstanceEnum: - switch (m_current->parent->type) { - case StackElement::InjectCode: - if (m_current->parent->parent->type == StackElement::Root) { - CodeSnipList snips = m_current->parent->entry->codeSnips(); - CodeSnip snip = snips.takeLast(); - snip.addTemplateInstance(m_current->value.templateInstance); - snips.append(snip); - m_current->parent->entry->setCodeSnips(snips); - break; - } - Q_FALLTHROUGH(); - case StackElement::NativeToTarget: - case StackElement::AddConversion: - m_contextStack.top()->codeSnips.last().addTemplateInstance(m_current->value.templateInstance); - break; - case StackElement::Template: - m_current->parent->value.templateEntry->addTemplateInstance(m_current->value.templateInstance); - break; - case StackElement::CustomMetaConstructor: - case StackElement::CustomMetaDestructor: - m_current->parent->value.customFunction->addTemplateInstance(m_current->value.templateInstance); - break; - case StackElement::ConversionRule: - m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.last().addTemplateInstance(m_current->value.templateInstance); - break; - case StackElement::InjectCodeInFunction: - m_contextStack.top()->functionMods.last().snips.last().addTemplateInstance(m_current->value.templateInstance); - break; - default: - break; // nada - }; - break; - default: - break; - } - - switch (m_current->type) { - case StackElement::Root: - case StackElement::NamespaceTypeEntry: - case StackElement::InterfaceTypeEntry: - case StackElement::ObjectTypeEntry: - case StackElement::ValueTypeEntry: - case StackElement::PrimitiveTypeEntry: - case StackElement::TypedefTypeEntry: - delete m_contextStack.pop(); - break; - default: - break; - } - - StackElement *child = m_current; - m_current = m_current->parent; - delete(child); - - return true; -} - -template // QString/QStringRef -bool Handler::characters(const String &ch) -{ - if (m_currentDroppedEntry || m_ignoreDepth) - return true; - - if (m_current->type == StackElement::Template) { - m_current->value.templateEntry->addCode(ch); - return true; - } - - if (m_current->type == StackElement::CustomMetaConstructor || m_current->type == StackElement::CustomMetaDestructor) { - m_current->value.customFunction->addCode(ch); - return true; - } - - if (m_current->type == StackElement::ConversionRule - && m_current->parent->type == StackElement::ModifyArgument) { - m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.last().addCode(ch); - return true; - } - - if (m_current->type == StackElement::NativeToTarget || m_current->type == StackElement::AddConversion) { - m_contextStack.top()->codeSnips.last().addCode(ch); - return true; - } - - if (m_current->parent) { - if ((m_current->type & StackElement::CodeSnipMask)) { - CodeSnipList snips; - switch (m_current->parent->type) { - case StackElement::Root: - snips = m_current->parent->entry->codeSnips(); - snips.last().addCode(ch); - m_current->parent->entry->setCodeSnips(snips); - break; - case StackElement::ModifyFunction: - case StackElement::AddFunction: - m_contextStack.top()->functionMods.last().snips.last().addCode(ch); - m_contextStack.top()->functionMods.last().modifiers |= FunctionModification::CodeInjection; - break; - case StackElement::NamespaceTypeEntry: - case StackElement::ObjectTypeEntry: - case StackElement::ValueTypeEntry: - case StackElement::InterfaceTypeEntry: - m_contextStack.top()->codeSnips.last().addCode(ch); - break; - default: - Q_ASSERT(false); - }; - return true; - } - } - - if (m_current->type & StackElement::DocumentationMask) - m_contextStack.top()->docModifications.last().setCode(ch); - - return true; -} - -bool Handler::importFileElement(const QXmlStreamAttributes &atts) -{ - const QString fileName = atts.value(nameAttribute()).toString(); - if (fileName.isEmpty()) { - m_error = QLatin1String("Required attribute 'name' missing for include-file tag."); - return false; - } - - QFile file(fileName); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - file.setFileName(QLatin1String(":/trolltech/generator/") + fileName); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - m_error = QString::fromLatin1("Could not open file: '%1'").arg(QDir::toNativeSeparators(fileName)); - return false; - } - } - - const QStringRef quoteFrom = atts.value(quoteAfterLineAttribute()); - bool foundFromOk = quoteFrom.isEmpty(); - bool from = quoteFrom.isEmpty(); - - const QStringRef quoteTo = atts.value(quoteBeforeLineAttribute()); - bool foundToOk = quoteTo.isEmpty(); - bool to = true; - - QTextStream in(&file); - while (!in.atEnd()) { - QString line = in.readLine(); - if (from && to && line.contains(quoteTo)) { - to = false; - foundToOk = true; - break; - } - if (from && to) - characters(line + QLatin1Char('\n')); - if (!from && line.contains(quoteFrom)) { - from = true; - foundFromOk = true; - } - } - if (!foundFromOk || !foundToOk) { - QString fromError = QStringLiteral("Could not find quote-after-line='%1' in file '%2'.") - .arg(quoteFrom.toString(), fileName); - QString toError = QStringLiteral("Could not find quote-before-line='%1' in file '%2'.") - .arg(quoteTo.toString(), fileName); - - if (!foundToOk) - m_error = toError; - if (!foundFromOk) - m_error = fromError; - if (!foundFromOk && !foundToOk) - m_error = fromError + QLatin1Char(' ') + toError; - return false; - } - - return true; -} - -static bool convertBoolean(QStringView value, const QString &attributeName, bool defaultValue) -{ -#ifdef QTBUG_69389_FIXED - if (value.compare(trueAttributeValue(), Qt::CaseInsensitive) == 0 - || value.compare(yesAttributeValue(), Qt::CaseInsensitive) == 0) { - return true; - } - if (value.compare(falseAttributeValue(), Qt::CaseInsensitive) == 0 - || value.compare(noAttributeValue(), Qt::CaseInsensitive) == 0) { - return false; - } -#else - if (QtPrivate::compareStrings(value, trueAttributeValue(), Qt::CaseInsensitive) == 0 - || QtPrivate::compareStrings(value, yesAttributeValue(), Qt::CaseInsensitive) == 0) { - return true; - } - if (QtPrivate::compareStrings(value, falseAttributeValue(), Qt::CaseInsensitive) == 0 - || QtPrivate::compareStrings(value, noAttributeValue(), Qt::CaseInsensitive) == 0) { - return false; - } -#endif - const QString warn = QStringLiteral("Boolean value '%1' not supported in attribute '%2'. Use 'yes' or 'no'. Defaulting to '%3'.") - .arg(value) - .arg(attributeName, - defaultValue ? yesAttributeValue() : noAttributeValue()); - - qCWarning(lcShiboken).noquote().nospace() << warn; - return defaultValue; -} - -static bool convertRemovalAttribute(QStringView remove, Modification& mod, QString& errorMsg) -{ - if (remove.isEmpty()) - return true; -#ifdef QTBUG_69389_FIXED - if (remove.compare(QStringViewLiteral("all"), Qt::CaseInsensitive) == 0) { -#else - if (QtPrivate::compareStrings(remove, QStringViewLiteral("all"), Qt::CaseInsensitive) == 0) { -#endif - mod.removal = TypeSystem::All; - return true; - } -#ifdef QTBUG_69389_FIXED - if (remove.compare(QStringViewLiteral("target"), Qt::CaseInsensitive) == 0) { -#else - if (QtPrivate::compareStrings(remove, QStringViewLiteral("target"), Qt::CaseInsensitive) == 0) { -#endif - mod.removal = TypeSystem::TargetLangAndNativeCode; - return true; - } - errorMsg = QString::fromLatin1("Bad removal type '%1'").arg(remove); - return false; -} - -static void getNamePrefixRecursive(StackElement* element, QStringList& names) -{ - if (!element->parent || !element->parent->entry) - return; - getNamePrefixRecursive(element->parent, names); - names << element->parent->entry->name(); -} - -static QString getNamePrefix(StackElement* element) -{ - QStringList names; - getNamePrefixRecursive(element, names); - return names.join(QLatin1Char('.')); -} - -// Returns empty string if there's no error. -static QString checkSignatureError(const QString& signature, const QString& tag) -{ - QString funcName = signature.left(signature.indexOf(QLatin1Char('('))).trimmed(); - static const QRegularExpression whiteSpace(QStringLiteral("\\s")); - Q_ASSERT(whiteSpace.isValid()); - if (!funcName.startsWith(QLatin1String("operator ")) && funcName.contains(whiteSpace)) { - return QString::fromLatin1("Error in <%1> tag signature attribute '%2'.\n" - "White spaces aren't allowed in function names, " - "and return types should not be part of the signature.") - .arg(tag, signature); - } - return QString(); -} - -void Handler::applyCommonAttributes(TypeEntry *type, QXmlStreamAttributes *attributes) const -{ - type->setCodeGeneration(m_generate); - const int revisionIndex = - indexOfAttribute(*attributes, QStringViewLiteral("revision")); - if (revisionIndex != -1) - type->setRevision(attributes->takeAt(revisionIndex).value().toInt()); -} - -FlagsTypeEntry * - Handler::parseFlagsEntry(const QXmlStreamReader &, - EnumTypeEntry *enumEntry, - const QString &name, QString flagName, - const QVersionNumber &since, - QXmlStreamAttributes *attributes) - -{ - FlagsTypeEntry *ftype = new FlagsTypeEntry(QLatin1String("QFlags<") + name + QLatin1Char('>'), since); - ftype->setOriginator(enumEntry); - ftype->setTargetLangPackage(enumEntry->targetLangPackage()); - // Try to get the guess the qualified flag name - const int lastSepPos = name.lastIndexOf(colonColon()); - if (lastSepPos >= 0 && !flagName.contains(colonColon())) - flagName.prepend(name.left(lastSepPos + 2)); - - ftype->setOriginalName(flagName); - applyCommonAttributes(ftype, attributes); - QString n = ftype->originalName(); - - QStringList lst = n.split(colonColon()); - const QString &targetLangQualifier = enumEntry->targetLangQualifier(); - if (QStringList(lst.mid(0, lst.size() - 1)).join(colonColon()) != targetLangQualifier) { - qCWarning(lcShiboken).noquote().nospace() - << QStringLiteral("enum %1 and flags %2 differ in qualifiers") - .arg(targetLangQualifier, lst.constFirst()); - } - - ftype->setFlagsName(lst.constLast()); - enumEntry->setFlags(ftype); - - m_database->addFlagsType(ftype); - m_database->addType(ftype); - - const int revisionIndex = - indexOfAttribute(*attributes, QStringViewLiteral("flags-revision")); - ftype->setRevision(revisionIndex != -1 - ? attributes->takeAt(revisionIndex).value().toInt() - : enumEntry->revision()); - return ftype; -} - -SmartPointerTypeEntry * - Handler::parseSmartPointerEntry(const QXmlStreamReader &, - const QString &name, const QVersionNumber &since, - QXmlStreamAttributes *attributes) -{ - QString smartPointerType; - QString getter; - QString refCountMethodName; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == QLatin1String("type")) { - smartPointerType = attributes->takeAt(i).value().toString(); - } else if (name == QLatin1String("getter")) { - getter = attributes->takeAt(i).value().toString(); - } else if (name == QLatin1String("ref-count-method")) { - refCountMethodName = attributes->takeAt(i).value().toString(); - } - } - - if (smartPointerType.isEmpty()) { - m_error = QLatin1String("No type specified for the smart pointer. Currently supported types: 'shared',"); - return nullptr; - } - if (smartPointerType != QLatin1String("shared")) { - m_error = QLatin1String("Currently only the 'shared' type is supported."); - return nullptr; - } - - if (getter.isEmpty()) { - m_error = QLatin1String("No function getter name specified for getting the raw pointer held by the smart pointer."); - return nullptr; - } - - QString signature = getter + QLatin1String("()"); - signature = TypeDatabase::normalizedSignature(signature); - if (signature.isEmpty()) { - m_error = QLatin1String("No signature for the smart pointer getter found."); - return nullptr; - } - - QString errorString = checkSignatureError(signature, - QLatin1String("smart-pointer-type")); - if (!errorString.isEmpty()) { - m_error = errorString; - return nullptr; - } - - auto *type = new SmartPointerTypeEntry(name, getter, smartPointerType, refCountMethodName, since); - applyCommonAttributes(type, attributes); - return type; -} - -PrimitiveTypeEntry * - Handler::parsePrimitiveTypeEntry(const QXmlStreamReader &reader, - const QString &name, const QVersionNumber &since, - QXmlStreamAttributes *attributes) -{ - auto *type = new PrimitiveTypeEntry(name, since); - applyCommonAttributes(type, attributes); - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == QLatin1String("target-lang-name")) { - type->setTargetLangName(attributes->takeAt(i).value().toString()); - } else if (name == QLatin1String("target-lang-api-name")) { - type->setTargetLangApiName(attributes->takeAt(i).value().toString()); - } else if (name == preferredConversionAttribute()) { - qCWarning(lcShiboken, "%s", - qPrintable(msgUnimplementedAttributeWarning(reader, name))); - } else if (name == preferredTargetLangTypeAttribute()) { - const bool v = convertBoolean(attributes->takeAt(i).value(), - preferredTargetLangTypeAttribute(), true); - type->setPreferredTargetLangType(v); - } else if (name == QLatin1String("default-constructor")) { - type->setDefaultConstructor(attributes->takeAt(i).value().toString()); - } - } - - if (type->targetLangName().isEmpty()) - type->setTargetLangName(type->name()); - if (type->targetLangApiName().isEmpty()) - type->setTargetLangApiName(type->name()); - type->setTargetLangPackage(m_defaultPackage); - return type; -} - -ContainerTypeEntry * - Handler::parseContainerTypeEntry(const QXmlStreamReader &, - const QString &name, const QVersionNumber &since, - QXmlStreamAttributes *attributes) -{ - const int typeIndex = indexOfAttribute(*attributes, QStringViewLiteral("type")); - if (typeIndex == -1) { - m_error = QLatin1String("no 'type' attribute specified"); - return nullptr; - } - const QStringRef typeName = attributes->takeAt(typeIndex).value(); - ContainerTypeEntry::Type containerType = containerTypeFromAttribute(typeName); - if (containerType == ContainerTypeEntry::NoContainer) { - m_error = QLatin1String("there is no container of type ") + typeName.toString(); - return nullptr; - } - auto *type = new ContainerTypeEntry(name, containerType, since); - applyCommonAttributes(type, attributes); - return type; -} - -EnumTypeEntry * - Handler::parseEnumTypeEntry(const QXmlStreamReader &reader, - const QString &fullName, const QVersionNumber &since, - QXmlStreamAttributes *attributes) -{ - QString scope; - QString name = fullName; - const int sep = fullName.lastIndexOf(colonColon()); - if (sep != -1) { - scope = fullName.left(sep); - name = fullName.right(fullName.size() - sep - 2); - } - auto *entry = new EnumTypeEntry(scope, name, since); - applyCommonAttributes(entry, attributes); - entry->setTargetLangPackage(m_defaultPackage); - - QString flagNames; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == QLatin1String("upper-bound")) { - qCWarning(lcShiboken, "%s", - qPrintable(msgUnimplementedAttributeWarning(reader, name))); - } else if (name == QLatin1String("lower-bound")) { - qCWarning(lcShiboken, "%s", - qPrintable(msgUnimplementedAttributeWarning(reader, name))); - } else if (name == forceIntegerAttribute()) { - qCWarning(lcShiboken, "%s", - qPrintable(msgUnimplementedAttributeWarning(reader, name))); - } else if (name == extensibleAttribute()) { - qCWarning(lcShiboken, "%s", - qPrintable(msgUnimplementedAttributeWarning(reader, name))); - } else if (name == flagsAttribute()) { - flagNames = attributes->takeAt(i).value().toString(); - } - } - - // put in the flags parallel... - if (!flagNames.isEmpty()) { - const QStringList &flagNameList = flagNames.split(QLatin1Char(',')); - for (const QString &flagName : flagNameList) - parseFlagsEntry(reader, entry, fullName, flagName.trimmed(), since, attributes); - } - return entry; -} - -ObjectTypeEntry * - Handler::parseInterfaceTypeEntry(const QXmlStreamReader &, - const QString &name, const QVersionNumber &since, - QXmlStreamAttributes *attributes) -{ - auto *otype = new ObjectTypeEntry(name, since); - applyCommonAttributes(otype, attributes); - QString targetLangName = name; - bool generate = true; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == QLatin1String("target-lang-name")) { - targetLangName = attributes->takeAt(i).value().toString(); - } else if (name == generateAttribute()) { - generate = convertBoolean(attributes->takeAt(i).value(), - generateAttribute(), true); - } - } - - InterfaceTypeEntry *itype = - new InterfaceTypeEntry(InterfaceTypeEntry::interfaceName(targetLangName), since); - - if (generate) - itype->setCodeGeneration(m_generate); - else - itype->setCodeGeneration(TypeEntry::GenerateForSubclass); - - otype->setDesignatedInterface(itype); - itype->setOrigin(otype); - return otype; -} - -NamespaceTypeEntry * - Handler::parseNamespaceTypeEntry(const QXmlStreamReader &reader, - const QString &name, const QVersionNumber &since, - QXmlStreamAttributes *attributes) -{ - QScopedPointer result(new NamespaceTypeEntry(name, since)); - applyCommonAttributes(result.data(), attributes); - applyComplexTypeAttributes(reader, result.data(), attributes); - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef attributeName = attributes->at(i).qualifiedName(); - if (attributeName == QLatin1String("files")) { - const QString pattern = attributes->takeAt(i).value().toString(); - QRegularExpression re(pattern); - if (!re.isValid()) { - m_error = msgInvalidRegularExpression(pattern, re.errorString()); - return nullptr; - } - result->setFilePattern(re); - } else if (attributeName == QLatin1String("extends")) { - const auto extendsPackageName = attributes->takeAt(i).value(); - auto allEntries = TypeDatabase::instance()->findNamespaceTypes(name); - auto extendsIt = std::find_if(allEntries.cbegin(), allEntries.cend(), - [extendsPackageName] (const NamespaceTypeEntry *e) { - return e->targetLangPackage() == extendsPackageName; - }); - if (extendsIt == allEntries.cend()) { - m_error = msgCannotFindNamespaceToExtend(name, extendsPackageName); - return nullptr; - } - result->setExtends(*extendsIt); - } - } - - if (result->extends() && !result->hasPattern()) { - m_error = msgExtendingNamespaceRequiresPattern(name); - return nullptr; - } - - return result.take(); -} - -ValueTypeEntry * - Handler::parseValueTypeEntry(const QXmlStreamReader &, - const QString &name, const QVersionNumber &since, - QXmlStreamAttributes *attributes) -{ - auto *typeEntry = new ValueTypeEntry(name, since); - applyCommonAttributes(typeEntry, attributes); - const int defaultCtIndex = - indexOfAttribute(*attributes, QStringViewLiteral("default-constructor")); - if (defaultCtIndex != -1) - typeEntry->setDefaultConstructor(attributes->takeAt(defaultCtIndex).value().toString()); - return typeEntry; -} - -FunctionTypeEntry * - Handler::parseFunctionTypeEntry(const QXmlStreamReader &, - const QString &name, const QVersionNumber &since, - QXmlStreamAttributes *attributes) -{ - const int signatureIndex = indexOfAttribute(*attributes, signatureAttribute()); - if (signatureIndex == -1) { - m_error = msgMissingAttribute(signatureAttribute()); - return nullptr; - } - const QString signature = - TypeDatabase::normalizedSignature(attributes->takeAt(signatureIndex).value().toString()); - - TypeEntry *existingType = m_database->findType(name); - - if (!existingType) { - auto *result = new FunctionTypeEntry(name, signature, since); - applyCommonAttributes(result, attributes); - return result; - } - - if (existingType->type() != TypeEntry::FunctionType) { - m_error = QStringLiteral("%1 expected to be a function, but isn't! Maybe it was already declared as a class or something else.") - .arg(name); - return nullptr; - } - - auto *result = reinterpret_cast(existingType); - result->addSignature(signature); - 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 -{ - bool generate = true; - ctype->setCopyable(ComplexTypeEntry::Unknown); - auto exceptionHandling = m_exceptionHandling; - auto allowThread = m_allowThread; - - QString package = m_defaultPackage; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == streamAttribute()) { - ctype->setStream(convertBoolean(attributes->takeAt(i).value(), streamAttribute(), false)); - } else if (name == generateAttribute()) { - generate = convertBoolean(attributes->takeAt(i).value(), generateAttribute(), true); - } else if (name ==packageAttribute()) { - package = attributes->takeAt(i).value().toString(); - } else if (name == defaultSuperclassAttribute()) { - ctype->setDefaultSuperclass(attributes->takeAt(i).value().toString()); - } else if (name == genericClassAttribute()) { - qCWarning(lcShiboken, "%s", - qPrintable(msgUnimplementedAttributeWarning(reader, name))); - const bool v = convertBoolean(attributes->takeAt(i).value(), genericClassAttribute(), false); - ctype->setGenericClass(v); - } else if (name == QLatin1String("target-lang-name")) { - ctype->setTargetLangName(attributes->takeAt(i).value().toString()); - } else if (name == QLatin1String("polymorphic-base")) { - ctype->setPolymorphicIdValue(attributes->takeAt(i).value().toString()); - } else if (name == QLatin1String("polymorphic-id-expression")) { - ctype->setPolymorphicIdValue(attributes->takeAt(i).value().toString()); - } else if (name == copyableAttribute()) { - const bool v = convertBoolean(attributes->takeAt(i).value(), copyableAttribute(), false); - ctype->setCopyable(v ? ComplexTypeEntry::CopyableSet : ComplexTypeEntry::NonCopyableSet); - } else if (name == exceptionHandlingAttribute()) { - const auto attribute = attributes->takeAt(i); - const auto v = exceptionHandlingFromAttribute(attribute.value()); - if (v != TypeSystem::ExceptionHandling::Unspecified) { - exceptionHandling = v; - } else { - qCWarning(lcShiboken, "%s", - qPrintable(msgInvalidAttributeValue(attribute))); - } - } else if (name == allowThreadAttribute()) { - const auto attribute = attributes->takeAt(i); - const auto v = allowThreadFromAttribute(attribute.value()); - if (v != TypeSystem::AllowThread::Unspecified) { - allowThread = v; - } else { - qCWarning(lcShiboken, "%s", - qPrintable(msgInvalidAttributeValue(attribute))); - } - } else if (name == QLatin1String("held-type")) { - qCWarning(lcShiboken, "%s", - qPrintable(msgUnimplementedAttributeWarning(reader, name))); - } else if (name == QLatin1String("hash-function")) { - ctype->setHashFunction(attributes->takeAt(i).value().toString()); - } else if (name == forceAbstractAttribute()) { - qCWarning(lcShiboken, "%s", - qPrintable(msgUnimplementedAttributeWarning(reader, name))); - } else if (name == deprecatedAttribute()) { - if (convertBoolean(attributes->takeAt(i).value(), deprecatedAttribute(), false)) - ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::Deprecated); - } else if (name == deleteInMainThreadAttribute()) { - if (convertBoolean(attributes->takeAt(i).value(), deleteInMainThreadAttribute(), false)) - ctype->setDeleteInMainThread(true); - } else if (name == QLatin1String("target-type")) { - ctype->setTargetType(attributes->takeAt(i).value().toString()); - } - } - - if (exceptionHandling != TypeSystem::ExceptionHandling::Unspecified) - ctype->setExceptionHandling(exceptionHandling); - if (allowThread != TypeSystem::AllowThread::Unspecified) - ctype->setAllowThread(allowThread); - - // The generator code relies on container's package being empty. - if (ctype->type() != TypeEntry::ContainerType) - ctype->setTargetLangPackage(package); - - if (InterfaceTypeEntry *di = ctype->designatedInterface()) - di->setTargetLangPackage(package); - - if (generate) - ctype->setCodeGeneration(m_generate); - else - ctype->setCodeGeneration(TypeEntry::GenerateForSubclass); -} - -bool Handler::parseRenameFunction(const QXmlStreamReader &, - QString *name, QXmlStreamAttributes *attributes) -{ - QString signature; - QString rename; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == signatureAttribute()) { - // Do not remove as it is needed for the type entry later on - signature = attributes->at(i).value().toString(); - } else if (name == renameAttribute()) { - rename = attributes->takeAt(i).value().toString(); - } - } - - if (signature.isEmpty()) { - m_error = msgMissingAttribute(signatureAttribute()); - return false; - } - - *name = signature.left(signature.indexOf(QLatin1Char('('))).trimmed(); - - QString errorString = checkSignatureError(signature, QLatin1String("function")); - if (!errorString.isEmpty()) { - m_error = errorString; - return false; - } - - if (!rename.isEmpty()) { - static const QRegularExpression functionNameRegExp(QLatin1String("^[a-zA-Z_][a-zA-Z0-9_]*$")); - Q_ASSERT(functionNameRegExp.isValid()); - if (!functionNameRegExp.match(rename).hasMatch()) { - m_error = QLatin1String("can not rename '") + signature + QLatin1String("', '") - + rename + QLatin1String("' is not a valid function name"); - return false; - } - FunctionModification mod; - if (!mod.setSignature(signature, &m_error)) - return false; - mod.renamedToName = rename; - mod.modifiers |= Modification::Rename; - m_contextStack.top()->functionMods << mod; - } - return true; -} - -bool Handler::parseInjectDocumentation(const QXmlStreamReader &, - QXmlStreamAttributes *attributes) -{ - const int validParent = StackElement::TypeEntryMask - | StackElement::ModifyFunction - | StackElement::ModifyField; - if (!m_current->parent || (m_current->parent->type & validParent) == 0) { - m_error = QLatin1String("inject-documentation must be inside modify-function, " - "modify-field or other tags that creates a type"); - return false; - } - - TypeSystem::DocModificationMode mode = TypeSystem::DocModificationReplace; - TypeSystem::Language lang = TypeSystem::NativeCode; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == QLatin1String("mode")) { - const QStringRef modeName = attributes->takeAt(i).value(); - mode = docModificationFromAttribute(modeName); - if (mode == TypeSystem::DocModificationInvalid) { - m_error = QLatin1String("Unknown documentation injection mode: ") + modeName; - return false; - } - } else if (name == formatAttribute()) { - const QStringRef format = attributes->takeAt(i).value(); - lang = languageFromAttribute(format); - if (lang != TypeSystem::TargetLangCode && lang != TypeSystem::NativeCode) { - m_error = QStringLiteral("unsupported class attribute: '%1'").arg(format); - return false; - } - } - } - - QString signature = m_current->type & StackElement::TypeEntryMask - ? QString() : m_currentSignature; - DocModification mod(mode, signature); - mod.setFormat(lang); - m_contextStack.top()->docModifications << mod; - return true; -} - -bool Handler::parseModifyDocumentation(const QXmlStreamReader &, - QXmlStreamAttributes *attributes) -{ - const int validParent = StackElement::TypeEntryMask - | StackElement::ModifyFunction - | StackElement::ModifyField; - if (!m_current->parent || (m_current->parent->type & validParent) == 0) { - m_error = QLatin1String("modify-documentation must be inside modify-function, " - "modify-field or other tags that creates a type"); - return false; - } - - const int xpathIndex = indexOfAttribute(*attributes, xPathAttribute()); - if (xpathIndex == -1) { - m_error = msgMissingAttribute(xPathAttribute()); - return false; - } - - const QString xpath = attributes->takeAt(xpathIndex).value().toString(); - QString signature = (m_current->type & StackElement::TypeEntryMask) ? QString() : m_currentSignature; - m_contextStack.top()->docModifications - << DocModification(xpath, signature); - return true; -} - -// m_exceptionHandling -TypeSystemTypeEntry *Handler::parseRootElement(const QXmlStreamReader &, - const QVersionNumber &since, - QXmlStreamAttributes *attributes) -{ - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == packageAttribute()) { - m_defaultPackage = attributes->takeAt(i).value().toString(); - } else if (name == defaultSuperclassAttribute()) { - m_defaultSuperclass = attributes->takeAt(i).value().toString(); - } else if (name == exceptionHandlingAttribute()) { - const auto attribute = attributes->takeAt(i); - const auto v = exceptionHandlingFromAttribute(attribute.value()); - if (v != TypeSystem::ExceptionHandling::Unspecified) { - m_exceptionHandling = v; - } else { - qCWarning(lcShiboken, "%s", - qPrintable(msgInvalidAttributeValue(attribute))); - } - } else if (name == allowThreadAttribute()) { - const auto attribute = attributes->takeAt(i); - const auto v = allowThreadFromAttribute(attribute.value()); - if (v != TypeSystem::AllowThread::Unspecified) { - m_allowThread = v; - } else { - qCWarning(lcShiboken, "%s", - qPrintable(msgInvalidAttributeValue(attribute))); - } - } - } - - auto *moduleEntry = - const_cast(m_database->findTypeSystemType(m_defaultPackage)); - const bool add = moduleEntry == nullptr; - if (add) - moduleEntry = new TypeSystemTypeEntry(m_defaultPackage, since); - moduleEntry->setCodeGeneration(m_generate); - - if ((m_generate == TypeEntry::GenerateForSubclass || - m_generate == TypeEntry::GenerateNothing) && !m_defaultPackage.isEmpty()) - TypeDatabase::instance()->addRequiredTargetImport(m_defaultPackage); - - if (add) - m_database->addTypeSystemType(moduleEntry); - return moduleEntry; -} - -bool Handler::loadTypesystem(const QXmlStreamReader &, - QXmlStreamAttributes *attributes) -{ - QString typeSystemName; - bool generateChild = true; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == nameAttribute()) - typeSystemName = attributes->takeAt(i).value().toString(); - else if (name == generateAttribute()) - generateChild = convertBoolean(attributes->takeAt(i).value(), generateAttribute(), true); - } - if (typeSystemName.isEmpty()) { - m_error = QLatin1String("No typesystem name specified"); - return false; - } - const bool result = - m_database->parseFile(typeSystemName, m_currentPath, generateChild - && m_generate == TypeEntry::GenerateAll); - if (!result) - m_error = QStringLiteral("Failed to parse: '%1'").arg(typeSystemName); - return result; -} - -bool Handler::parseRejectEnumValue(const QXmlStreamReader &, - QXmlStreamAttributes *attributes) -{ - if (!m_currentEnum) { - m_error = QLatin1String(" node must be used inside a node"); - return false; - } - const int nameIndex = indexOfAttribute(*attributes, nameAttribute()); - if (nameIndex == -1) { - m_error = msgMissingAttribute(nameAttribute()); - return false; - } - m_currentEnum->addEnumValueRejection(attributes->takeAt(nameIndex).value().toString()); - return true; -} - -bool Handler::parseReplaceArgumentType(const QXmlStreamReader &, - const StackElement &topElement, - QXmlStreamAttributes *attributes) -{ - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("Type replacement can only be specified for argument modifications"); - return false; - } - const int modifiedTypeIndex = indexOfAttribute(*attributes, modifiedTypeAttribute()); - if (modifiedTypeIndex == -1) { - m_error = QLatin1String("Type replacement requires 'modified-type' attribute"); - return false; - } - m_contextStack.top()->functionMods.last().argument_mods.last().modified_type = - attributes->takeAt(modifiedTypeIndex).value().toString(); - return true; -} - -bool Handler::parseCustomConversion(const QXmlStreamReader &, - const StackElement &topElement, - QXmlStreamAttributes *attributes) -{ - if (topElement.type != StackElement::ModifyArgument - && topElement.type != StackElement::ValueTypeEntry - && topElement.type != StackElement::PrimitiveTypeEntry - && topElement.type != StackElement::ContainerTypeEntry) { - m_error = QLatin1String("Conversion rules can only be specified for argument modification, " - "value-type, primitive-type or container-type conversion."); - return false; - } - - QString sourceFile; - QString snippetLabel; - TypeSystem::Language lang = TypeSystem::NativeCode; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == classAttribute()) { - const QStringRef languageAttribute = attributes->takeAt(i).value(); - lang = languageFromAttribute(languageAttribute); - if (lang != TypeSystem::TargetLangCode && lang != TypeSystem::NativeCode) { - m_error = QStringLiteral("unsupported class attribute: '%1'").arg(languageAttribute); - return false; - } - } else if (name == QLatin1String("file")) { - sourceFile = attributes->takeAt(i).value().toString(); - } else if (name == snippetAttribute()) { - snippetLabel = attributes->takeAt(i).value().toString(); - } - } - - if (topElement.type == StackElement::ModifyArgument) { - CodeSnip snip; - snip.language = lang; - m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.append(snip); - return true; - } - - if (topElement.entry->hasConversionRule() || topElement.entry->hasCustomConversion()) { - m_error = QLatin1String("Types can have only one conversion rule"); - return false; - } - - // The old conversion rule tag that uses a file containing the conversion - // will be kept temporarily for compatibility reasons. - if (!sourceFile.isEmpty()) { - if (m_generate != TypeEntry::GenerateForSubclass - && m_generate != TypeEntry::GenerateNothing) { - - const char* conversionFlag = NATIVE_CONVERSION_RULE_FLAG; - if (lang == TypeSystem::TargetLangCode) - conversionFlag = TARGET_CONVERSION_RULE_FLAG; - - QFile conversionSource(sourceFile); - if (conversionSource.open(QIODevice::ReadOnly | QIODevice::Text)) { - const QString conversionRule = - extractSnippet(QString::fromUtf8(conversionSource.readAll()), snippetLabel); - topElement.entry->setConversionRule(QLatin1String(conversionFlag) + conversionRule); - } else { - qCWarning(lcShiboken).noquote().nospace() - << "File containing conversion code for " - << topElement.entry->name() << " type does not exist or is not readable: " - << sourceFile; - } - } - } - - auto *customConversion = new CustomConversion(m_current->entry); - customConversionsForReview.append(customConversion); - return true; -} - -bool Handler::parseNativeToTarget(const QXmlStreamReader &, - const StackElement &topElement, - QXmlStreamAttributes *attributes) -{ - if (topElement.type != StackElement::ConversionRule) { - m_error = QLatin1String("Native to Target conversion code can only be specified for custom conversion rules."); - return false; - } - CodeSnip snip; - if (!readFileSnippet(attributes, &snip)) - return false; - m_contextStack.top()->codeSnips.append(snip); - return true; -} - -bool Handler::parseAddConversion(const QXmlStreamReader &, - const StackElement &topElement, - QXmlStreamAttributes *attributes) -{ - if (topElement.type != StackElement::TargetToNative) { - m_error = QLatin1String("Target to Native conversions can only be added inside 'target-to-native' tags."); - return false; - } - QString sourceTypeName; - QString typeCheck; - CodeSnip snip; - if (!readFileSnippet(attributes, &snip)) - return false; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == QLatin1String("type")) - sourceTypeName = attributes->takeAt(i).value().toString(); - else if (name == QLatin1String("check")) - typeCheck = attributes->takeAt(i).value().toString(); - } - if (sourceTypeName.isEmpty()) { - m_error = QLatin1String("Target to Native conversions must specify the input type with the 'type' attribute."); - return false; - } - m_current->entry->customConversion()->addTargetToNativeConversion(sourceTypeName, typeCheck); - m_contextStack.top()->codeSnips.append(snip); - return true; -} - -static bool parseIndex(const QString &index, int *result, QString *errorMessage) -{ - bool ok = false; - *result = index.toInt(&ok); - if (!ok) - *errorMessage = QStringLiteral("Cannot convert '%1' to integer").arg(index); - return ok; -} - -static bool parseArgumentIndex(const QString &index, int *result, QString *errorMessage) -{ - if (index == QLatin1String("return")) { - *result = 0; - return true; - } - if (index == QLatin1String("this")) { - *result = -1; - return true; - } - return parseIndex(index, result, errorMessage); -} - -bool Handler::parseModifyArgument(const QXmlStreamReader &, - const StackElement &topElement, QXmlStreamAttributes *attributes) -{ - if (topElement.type != StackElement::ModifyFunction - && topElement.type != StackElement::AddFunction) { - m_error = QString::fromLatin1("argument modification requires function" - " modification as parent, was %1") - .arg(topElement.type, 0, 16); - return false; - } - - QString index; - QString replaceValue; - bool resetAfterUse = false; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == indexAttribute()) { - index = attributes->takeAt(i).value().toString(); - } else if (name == QLatin1String("replace-value")) { - replaceValue = attributes->takeAt(i).value().toString(); - } else if (name == invalidateAfterUseAttribute()) { - resetAfterUse = convertBoolean(attributes->takeAt(i).value(), - invalidateAfterUseAttribute(), false); - } - } - - if (index.isEmpty()) { - m_error = msgMissingAttribute(indexAttribute()); - return false; - } - - int idx; - if (!parseArgumentIndex(index, &idx, &m_error)) - return false; - - if (!replaceValue.isEmpty() && idx) { - m_error = QLatin1String("replace-value is only supported for return values (index=0)."); - return false; - } - - ArgumentModification argumentModification = ArgumentModification(idx); - argumentModification.replace_value = replaceValue; - argumentModification.resetAfterUse = resetAfterUse; - m_contextStack.top()->functionMods.last().argument_mods.append(argumentModification); - return true; -} - -bool Handler::parseNoNullPointer(const QXmlStreamReader &reader, - const StackElement &topElement, QXmlStreamAttributes *attributes) -{ - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("no-null-pointer requires argument modification as parent"); - return false; - } - - ArgumentModification &lastArgMod = m_contextStack.top()->functionMods.last().argument_mods.last(); - lastArgMod.noNullPointers = true; - - const int defaultValueIndex = - indexOfAttribute(*attributes, QStringViewLiteral("default-value")); - if (defaultValueIndex != -1) { - const QXmlStreamAttribute attribute = attributes->takeAt(defaultValueIndex); - qCWarning(lcShiboken, "%s", - qPrintable(msgUnimplementedAttributeWarning(reader, attribute))); - } - return true; -} - -bool Handler::parseDefineOwnership(const QXmlStreamReader &, - const StackElement &topElement, - QXmlStreamAttributes *attributes) -{ - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("define-ownership requires argument modification as parent"); - return false; - } - - TypeSystem::Language lang = TypeSystem::TargetLangCode; - QString ownership; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == classAttribute()) { - const QStringRef className = attributes->takeAt(i).value(); - lang = languageFromAttribute(className); - if (lang != TypeSystem::TargetLangCode && lang != TypeSystem::NativeCode) { - m_error = QStringLiteral("unsupported class attribute: '%1'").arg(className); - return false; - } - } else if (name == ownershipAttribute()) { - ownership = attributes->takeAt(i).value().toString(); - } - } - const TypeSystem::Ownership owner = ownershipFromFromAttribute(ownership); - if (owner == TypeSystem::InvalidOwnership) { - m_error = QStringLiteral("unsupported owner attribute: '%1'").arg(ownership); - return false; - } - m_contextStack.top()->functionMods.last().argument_mods.last().ownerships[lang] = owner; - return true; -} - -bool Handler::parseArgumentMap(const QXmlStreamReader &, - const StackElement &topElement, - QXmlStreamAttributes *attributes) -{ - if (!(topElement.type & StackElement::CodeSnipMask)) { - m_error = QLatin1String("Argument maps requires code injection as parent"); - return false; - } - - int pos = 1; - QString metaName; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == indexAttribute()) { - if (!parseIndex(attributes->takeAt(i).value().toString(), &pos, &m_error)) - return false; - if (pos <= 0) { - m_error = QStringLiteral("Argument position %1 must be a positive number").arg(pos); - return false; - } - } else if (name == QLatin1String("meta-name")) { - metaName = attributes->takeAt(i).value().toString(); - } - } - - if (metaName.isEmpty()) - qCWarning(lcShiboken) << "Empty meta name in argument map"; - - if (topElement.type == StackElement::InjectCodeInFunction) { - m_contextStack.top()->functionMods.last().snips.last().argumentMap[pos] = metaName; - } else { - qCWarning(lcShiboken) << "Argument maps are only useful for injection of code " - "into functions."; - } - return true; -} - -bool Handler::parseRemoval(const QXmlStreamReader &, - const StackElement &topElement, - QXmlStreamAttributes *attributes) -{ - if (topElement.type != StackElement::ModifyFunction) { - m_error = QLatin1String("Function modification parent required"); - return false; - } - - TypeSystem::Language lang = TypeSystem::All; - const int classIndex = indexOfAttribute(*attributes, classAttribute()); - if (classIndex != -1) { - const QStringRef value = attributes->takeAt(classIndex).value(); - lang = languageFromAttribute(value); - if (lang == TypeSystem::TargetLangCode) // "target" means TargetLangAndNativeCode here - lang = TypeSystem::TargetLangAndNativeCode; - if (lang != TypeSystem::TargetLangAndNativeCode && lang != TypeSystem::All) { - m_error = QStringLiteral("unsupported class attribute: '%1'").arg(value); - return false; - } - } - m_contextStack.top()->functionMods.last().removal = lang; - return true; -} - -bool Handler::parseRename(const QXmlStreamReader &reader, - StackElement::ElementType type, - const StackElement &topElement, - QXmlStreamAttributes *attributes) -{ - if (topElement.type != StackElement::ModifyField - && topElement.type != StackElement::ModifyFunction - && topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("Function, field or argument modification parent required"); - return false; - } - - Modification *mod = nullptr; - if (topElement.type == StackElement::ModifyFunction) - mod = &m_contextStack.top()->functionMods.last(); - else if (topElement.type == StackElement::ModifyField) - mod = &m_contextStack.top()->fieldMods.last(); - - Modification::Modifiers modifierFlag = Modification::Rename; - if (type == StackElement::Rename) { - const int toIndex = indexOfAttribute(*attributes, toAttribute()); - if (toIndex == -1) { - m_error = msgMissingAttribute(toAttribute()); - return false; - } - const QString renamed_to = attributes->takeAt(toIndex).value().toString(); - if (topElement.type == StackElement::ModifyFunction) - mod->setRenamedTo(renamed_to); - else if (topElement.type == StackElement::ModifyField) - mod->setRenamedTo(renamed_to); - else - m_contextStack.top()->functionMods.last().argument_mods.last().renamed_to = renamed_to; - } else { - const int modifierIndex = indexOfAttribute(*attributes, modifierAttribute()); - if (modifierIndex == -1) { - m_error = msgMissingAttribute(modifierAttribute()); - return false; - } - const QStringRef modifier = attributes->takeAt(modifierIndex).value(); - modifierFlag = modifierFromAttribute(modifier); - if (modifierFlag == Modification::InvalidModifier) { - m_error = QStringLiteral("Unknown access modifier: '%1'").arg(modifier); - return false; - } - if (modifierFlag == Modification::Friendly) { - qCWarning(lcShiboken, "%s", - qPrintable(msgUnimplementedAttributeValueWarning(reader, modifierAttribute(), modifier))); - } - } - - if (mod) - mod->modifiers |= modifierFlag; - return true; -} - -bool Handler::parseModifyField(const QXmlStreamReader &reader, - QXmlStreamAttributes *attributes) -{ - FieldModification fm; - fm.modifiers = FieldModification::Readable | FieldModification::Writable; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == nameAttribute()) { - fm.name = attributes->takeAt(i).value().toString(); - } else if (name == removeAttribute()) { - if (!convertRemovalAttribute(attributes->takeAt(i).value(), fm, m_error)) - return false; - } else if (name == readAttribute()) { - qCWarning(lcShiboken, "%s", - qPrintable(msgUnimplementedAttributeWarning(reader, name))); - if (!convertBoolean(attributes->takeAt(i).value(), readAttribute(), true)) - fm.modifiers &= ~FieldModification::Readable; - } else if (name == writeAttribute()) { - qCWarning(lcShiboken, "%s", - qPrintable(msgUnimplementedAttributeWarning(reader, name))); - if (!convertBoolean(attributes->takeAt(i).value(), writeAttribute(), true)) - fm.modifiers &= ~FieldModification::Writable; - } - } - if (fm.name.isEmpty()) { - m_error = msgMissingAttribute(nameAttribute()); - return false; - } - m_contextStack.top()->fieldMods << fm; - return true; -} - -bool Handler::parseAddFunction(const QXmlStreamReader &, - const StackElement &topElement, - QXmlStreamAttributes *attributes) -{ - if (!(topElement.type & (StackElement::ComplexTypeEntryMask | StackElement::Root))) { - m_error = QString::fromLatin1("Add function requires a complex type or a root tag as parent" - ", was=%1").arg(topElement.type, 0, 16); - return false; - } - QString originalSignature; - QString returnType = QLatin1String("void"); - bool staticFunction = false; - QString access; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == QLatin1String("signature")) { - originalSignature = attributes->takeAt(i).value().toString(); - } else if (name == QLatin1String("return-type")) { - returnType = attributes->takeAt(i).value().toString(); - } else if (name == staticAttribute()) { - staticFunction = convertBoolean(attributes->takeAt(i).value(), - staticAttribute(), false); - } else if (name == accessAttribute()) { - access = attributes->takeAt(i).value().toString(); - } - } - - QString signature = TypeDatabase::normalizedSignature(originalSignature); - if (signature.isEmpty()) { - m_error = QLatin1String("No signature for the added function"); - return false; - } - - QString errorString = checkSignatureError(signature, QLatin1String("add-function")); - if (!errorString.isEmpty()) { - m_error = errorString; - return false; - } - - AddedFunctionPtr func(new AddedFunction(signature, returnType)); - func->setStatic(staticFunction); - if (!signature.contains(QLatin1Char('('))) - signature += QLatin1String("()"); - m_currentSignature = signature; - - if (!access.isEmpty()) { - const AddedFunction::Access a = addedFunctionAccessFromAttribute(access); - if (a == AddedFunction::InvalidAccess) { - m_error = QString::fromLatin1("Bad access type '%1'").arg(access); - return false; - } - func->setAccess(a); - } - - m_contextStack.top()->addedFunctions << func; - m_contextStack.top()->addedFunctionModificationIndex = - m_contextStack.top()->functionMods.size(); - - FunctionModification mod; - if (!mod.setSignature(m_currentSignature, &m_error)) - return false; - mod.setOriginalSignature(originalSignature); - m_contextStack.top()->functionMods << mod; - return true; -} - -bool Handler::parseModifyFunction(const QXmlStreamReader &reader, - const StackElement &topElement, - QXmlStreamAttributes *attributes) -{ - if (!(topElement.type & StackElement::ComplexTypeEntryMask)) { - m_error = QString::fromLatin1("Modify function requires complex type as parent" - ", was=%1").arg(topElement.type, 0, 16); - return false; - } - - QString originalSignature; - QString access; - QString removal; - QString rename; - QString association; - bool deprecated = false; - bool isThread = false; - TypeSystem::ExceptionHandling exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; - TypeSystem::AllowThread allowThread = TypeSystem::AllowThread::Unspecified; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == QLatin1String("signature")) { - originalSignature = attributes->takeAt(i).value().toString(); - } else if (name == accessAttribute()) { - access = attributes->takeAt(i).value().toString(); - } else if (name == renameAttribute()) { - rename = attributes->takeAt(i).value().toString(); - } else if (name == QLatin1String("associated-to")) { - association = attributes->takeAt(i).value().toString(); - qCWarning(lcShiboken, "%s", - qPrintable(msgUnimplementedAttributeWarning(reader, name))); - } else if (name == removeAttribute()) { - removal = attributes->takeAt(i).value().toString(); - } else if (name == deprecatedAttribute()) { - deprecated = convertBoolean(attributes->takeAt(i).value(), - deprecatedAttribute(), false); - } else if (name == threadAttribute()) { - isThread = convertBoolean(attributes->takeAt(i).value(), - threadAttribute(), false); - } else if (name == allowThreadAttribute()) { - const QXmlStreamAttribute attribute = attributes->takeAt(i); - allowThread = allowThreadFromAttribute(attribute.value()); - if (allowThread == TypeSystem::AllowThread::Unspecified) { - m_error = msgInvalidAttributeValue(attribute); - return false; - } - } else if (name == exceptionHandlingAttribute()) { - const auto attribute = attributes->takeAt(i); - exceptionHandling = exceptionHandlingFromAttribute(attribute.value()); - if (exceptionHandling == TypeSystem::ExceptionHandling::Unspecified) { - qCWarning(lcShiboken, "%s", - qPrintable(msgInvalidAttributeValue(attribute))); - } - } else if (name == virtualSlotAttribute()) { - qCWarning(lcShiboken, "%s", - qPrintable(msgUnimplementedAttributeWarning(reader, name))); - } - } - - const QString signature = TypeDatabase::normalizedSignature(originalSignature); - if (signature.isEmpty()) { - m_error = QLatin1String("No signature for modified function"); - return false; - } - - QString errorString = checkSignatureError(signature, QLatin1String("modify-function")); - if (!errorString.isEmpty()) { - m_error = errorString; - return false; - } - - FunctionModification mod; - if (!mod.setSignature(signature, &m_error)) - return false; - mod.setOriginalSignature(originalSignature); - mod.setExceptionHandling(exceptionHandling); - m_currentSignature = signature; - - if (!access.isEmpty()) { - const Modification::Modifiers m = modifierFromAttribute(access); - if ((m & (Modification::AccessModifierMask | Modification::FinalMask)) == 0) { - m_error = QString::fromLatin1("Bad access type '%1'").arg(access); - return false; - } - if (m == Modification::Final || m == Modification::NonFinal) { - qCWarning(lcShiboken, "%s", - qPrintable(msgUnimplementedAttributeValueWarning(reader, - accessAttribute(), access))); - } - mod.modifiers |= m; - } - - if (deprecated) - mod.modifiers |= Modification::Deprecated; - - if (!removal.isEmpty() && !convertRemovalAttribute(removal, mod, m_error)) - return false; - - if (!rename.isEmpty()) { - mod.renamedToName = rename; - mod.modifiers |= Modification::Rename; - } - - if (!association.isEmpty()) - mod.association = association; - - mod.setIsThread(isThread); - if (allowThread != TypeSystem::AllowThread::Unspecified) - mod.setAllowThread(allowThread); - - m_contextStack.top()->functionMods << mod; - return true; -} - -bool Handler::parseReplaceDefaultExpression(const QXmlStreamReader &, - const StackElement &topElement, - QXmlStreamAttributes *attributes) -{ - if (!(topElement.type & StackElement::ModifyArgument)) { - m_error = QLatin1String("Replace default expression only allowed as child of argument modification"); - return false; - } - const int withIndex = indexOfAttribute(*attributes, QStringViewLiteral("with")); - if (withIndex == -1 || attributes->at(withIndex).value().isEmpty()) { - m_error = QLatin1String("Default expression replaced with empty string. Use remove-default-expression instead."); - return false; - } - - m_contextStack.top()->functionMods.last().argument_mods.last().replacedDefaultExpression = - attributes->takeAt(withIndex).value().toString(); - return true; -} - -CustomFunction * - Handler::parseCustomMetaConstructor(const QXmlStreamReader &, - StackElement::ElementType type, - const StackElement &topElement, - QXmlStreamAttributes *attributes) -{ - QString functionName = topElement.entry->name().toLower() - + (type == StackElement::CustomMetaConstructor - ? QLatin1String("_create") : QLatin1String("_delete")); - QString paramName = QLatin1String("copy"); - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == nameAttribute()) - functionName = attributes->takeAt(i).value().toString(); - else if (name == QLatin1String("param-name")) - paramName = attributes->takeAt(i).value().toString(); - } - auto *func = new CustomFunction(functionName); - func->paramName = paramName; - return func; -} - -bool Handler::parseReferenceCount(const QXmlStreamReader &reader, - const StackElement &topElement, - QXmlStreamAttributes *attributes) -{ - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("reference-count must be child of modify-argument"); - return false; - } - - ReferenceCount rc; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == actionAttribute()) { - const QXmlStreamAttribute attribute = attributes->takeAt(i); - rc.action = referenceCountFromAttribute(attribute.value()); - switch (rc.action) { - case ReferenceCount::Invalid: - m_error = QLatin1String("unrecognized value '") + attribute.value() - + QLatin1String("' for action attribute."); - return false; - case ReferenceCount::AddAll: - case ReferenceCount::Ignore: - qCWarning(lcShiboken, "%s", - qPrintable(msgUnimplementedAttributeValueWarning(reader, attribute))); - break; - default: - break; - } - } else if (name == QLatin1String("variable-name")) { - rc.varName = attributes->takeAt(i).value().toString(); - } - } - - m_contextStack.top()->functionMods.last().argument_mods.last().referenceCounts.append(rc); - return true; -} - -bool Handler::parseParentOwner(const QXmlStreamReader &, - const StackElement &topElement, - QXmlStreamAttributes *attributes) -{ - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("parent-policy must be child of modify-argument"); - return false; - } - ArgumentOwner ao; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == indexAttribute()) { - const QString index = attributes->takeAt(i).value().toString(); - if (!parseArgumentIndex(index, &ao.index, &m_error)) - return false; - } else if (name == actionAttribute()) { - const QStringRef action = attributes->takeAt(i).value(); - ao.action = argumentOwnerActionFromAttribute(action); - if (ao.action == ArgumentOwner::Invalid) { - m_error = QLatin1String("Invalid parent actionr '") + action + QLatin1String("'."); - return false; - } - } - } - m_contextStack.top()->functionMods.last().argument_mods.last().owner = ao; - return true; -} - -bool Handler::readFileSnippet(QXmlStreamAttributes *attributes, CodeSnip *snip) -{ - QString fileName; - QString snippetLabel; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == QLatin1String("file")) { - fileName = attributes->takeAt(i).value().toString(); - } else if (name == snippetAttribute()) { - snippetLabel = attributes->takeAt(i).value().toString(); - } - } - if (fileName.isEmpty()) - return true; - const QString resolved = m_database->modifiedTypesystemFilepath(fileName, m_currentPath); - if (!QFile::exists(resolved)) { - m_error = QLatin1String("File for inject code not exist: ") - + QDir::toNativeSeparators(fileName); - return false; - } - QFile codeFile(resolved); - if (!codeFile.open(QIODevice::Text | QIODevice::ReadOnly)) { - m_error = msgCannotOpenForReading(codeFile); - return false; - } - QString source = fileName; - if (!snippetLabel.isEmpty()) - source += QLatin1String(" (") + snippetLabel + QLatin1Char(')'); - QString content; - QTextStream str(&content); - str << "// ========================================================================\n" - "// START of custom code block [file: " - << source << "]\n" - << extractSnippet(QString::fromUtf8(codeFile.readAll()), snippetLabel) - << "\n// END of custom code block [file: " << source - << "]\n// ========================================================================\n"; - snip->addCode(content); - return true; -} - -bool Handler::parseInjectCode(const QXmlStreamReader &, - const StackElement &topElement, - StackElement* element, QXmlStreamAttributes *attributes) -{ - if (!(topElement.type & StackElement::ComplexTypeEntryMask) - && (topElement.type != StackElement::AddFunction) - && (topElement.type != StackElement::ModifyFunction) - && (topElement.type != StackElement::Root)) { - m_error = QLatin1String("wrong parent type for code injection"); - return false; - } - - TypeSystem::CodeSnipPosition position = TypeSystem::CodeSnipPositionBeginning; - TypeSystem::Language lang = TypeSystem::TargetLangCode; - CodeSnip snip; - if (!readFileSnippet(attributes, &snip)) - return false; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == classAttribute()) { - const QStringRef className = attributes->takeAt(i).value(); - lang = languageFromAttribute(className); - if (lang == TypeSystem::NoLanguage) { - m_error = QStringLiteral("Invalid class specifier: '%1'").arg(className); - return false; - } - } else if (name == positionAttribute()) { - const QStringRef value = attributes->takeAt(i).value(); - position = codeSnipPositionFromAttribute(value); - if (position == TypeSystem::CodeSnipPositionInvalid) { - m_error = QStringLiteral("Invalid position: '%1'").arg(value); - return false; - } - } - } - - snip.position = position; - snip.language = lang; - - if (snip.language == TypeSystem::Interface - && topElement.type != StackElement::InterfaceTypeEntry) { - m_error = QLatin1String("Interface code injections must be direct child of an interface type entry"); - return false; - } - - if (topElement.type == StackElement::ModifyFunction - || topElement.type == StackElement::AddFunction) { - if (snip.language == TypeSystem::ShellDeclaration) { - m_error = QLatin1String("no function implementation in shell declaration in which to inject code"); - return false; - } - - FunctionModification &mod = m_contextStack.top()->functionMods.last(); - mod.snips << snip; - if (!snip.code().isEmpty()) - mod.modifiers |= FunctionModification::CodeInjection; - element->type = StackElement::InjectCodeInFunction; - } else if (topElement.type == StackElement::Root) { - element->entry->addCodeSnip(snip); - } else if (topElement.type != StackElement::Root) { - m_contextStack.top()->codeSnips << snip; - } - return true; -} - -bool Handler::parseInclude(const QXmlStreamReader &, - const StackElement &topElement, - TypeEntry *entry, QXmlStreamAttributes *attributes) -{ - QString fileName; - QString location; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == QLatin1String("file-name")) - fileName = attributes->takeAt(i).value().toString(); - else if (name == locationAttribute()) - location = attributes->takeAt(i).value().toString(); - } - const Include::IncludeType loc = locationFromAttribute(location); - if (loc == Include::InvalidInclude) { - m_error = QStringLiteral("Location not recognized: '%1'").arg(location); - return false; - } - - Include inc(loc, fileName); - if (topElement.type - & (StackElement::ComplexTypeEntryMask | StackElement::PrimitiveTypeEntry)) { - entry->setInclude(inc); - } else if (topElement.type == StackElement::ExtraIncludes) { - entry->addExtraInclude(inc); - } else { - m_error = QLatin1String("Only supported parent tags are primitive-type, complex types or extra-includes"); - return false; - } - if (InterfaceTypeEntry *di = entry->designatedInterface()) { - di->setInclude(entry->include()); - di->setExtraIncludes(entry->extraIncludes()); - } - return true; -} - -TemplateInstance * - Handler::parseTemplateInstanceEnum(const QXmlStreamReader &, - const StackElement &topElement, - QXmlStreamAttributes *attributes) -{ - if (!(topElement.type & StackElement::CodeSnipMask) && - (topElement.type != StackElement::Template) && - (topElement.type != StackElement::CustomMetaConstructor) && - (topElement.type != StackElement::CustomMetaDestructor) && - (topElement.type != StackElement::NativeToTarget) && - (topElement.type != StackElement::AddConversion) && - (topElement.type != StackElement::ConversionRule)) { - m_error = QLatin1String("Can only insert templates into code snippets, templates, custom-constructors, "\ - "custom-destructors, conversion-rule, native-to-target or add-conversion tags."); - return nullptr; - } - const int nameIndex = indexOfAttribute(*attributes, nameAttribute()); - if (nameIndex == -1) { - m_error = msgMissingAttribute(nameAttribute()); - return nullptr; - } - return new TemplateInstance(attributes->takeAt(nameIndex).value().toString()); -} - -bool Handler::parseReplace(const QXmlStreamReader &, - const StackElement &topElement, - StackElement *element, QXmlStreamAttributes *attributes) -{ - if (topElement.type != StackElement::TemplateInstanceEnum) { - m_error = QLatin1String("Can only insert replace rules into insert-template."); - return false; - } - QString from; - QString to; - for (int i = attributes->size() - 1; i >= 0; --i) { - const QStringRef name = attributes->at(i).qualifiedName(); - if (name == QLatin1String("from")) - from = attributes->takeAt(i).value().toString(); - else if (name == toAttribute()) - to = attributes->takeAt(i).value().toString(); - } - element->parent->value.templateInstance->addReplaceRule(from, to); - return true; -} - -bool Handler::startElement(const QXmlStreamReader &reader) -{ - if (m_ignoreDepth) { - ++m_ignoreDepth; - return true; - } - - const QStringRef tagName = reader.name(); - QXmlStreamAttributes attributes = reader.attributes(); - - QVersionNumber since(0, 0); - int index = indexOfAttribute(attributes, sinceAttribute()); - if (index != -1) { - const QStringRef sinceSpec = attributes.takeAt(index).value(); - since = QVersionNumber::fromString(sinceSpec.toString()); - if (since.isNull()) { - m_error = msgInvalidVersion(sinceSpec, m_defaultPackage); - return false; - } - } - - if (!m_defaultPackage.isEmpty() && since > QVersionNumber(0, 0)) { - TypeDatabase* td = TypeDatabase::instance(); - if (!td->checkApiVersion(m_defaultPackage, since)) { - ++m_ignoreDepth; - return true; - } - } - - if (tagName.compare(QLatin1String("import-file"), Qt::CaseInsensitive) == 0) - return importFileElement(attributes); - - const StackElement::ElementType elementType = elementFromTag(tagName); - if (elementType == StackElement::None) { - m_error = QStringLiteral("Unknown tag name: '%1'").arg(tagName); - return false; - } - - if (m_currentDroppedEntry) { - ++m_currentDroppedEntryDepth; - return true; - } - - auto *element = new StackElement(m_current); - element->type = elementType; - - if (element->type == StackElement::Root && m_generate == TypeEntry::GenerateAll) - customConversionsForReview.clear(); - - if (element->type == StackElement::CustomMetaConstructor - || element->type == StackElement::CustomMetaDestructor) { - qCWarning(lcShiboken, "%s", - qPrintable(msgUnimplementedElementWarning(reader, tagName))); - } - - 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) { - QString name; - if (element->type != StackElement::FunctionTypeEntry) { - const int nameIndex = indexOfAttribute(attributes, nameAttribute()); - if (nameIndex != -1) { - name = attributes.takeAt(nameIndex).value().toString(); - } else if (element->type != StackElement::EnumTypeEntry) { // anonymous enum? - m_error = msgMissingAttribute(nameAttribute()); - return false; - } - } - - if (m_database->hasDroppedTypeEntries()) { - QString identifier = getNamePrefix(element) + QLatin1Char('.'); - identifier += element->type == StackElement::FunctionTypeEntry - ? attributes.value(signatureAttribute()).toString() - : name; - if (m_database->shouldDropTypeEntry(identifier)) { - m_currentDroppedEntry = element; - m_currentDroppedEntryDepth = 1; - if (ReportHandler::isDebug(ReportHandler::SparseDebug)) { - qCDebug(lcShiboken) - << QStringLiteral("Type system entry '%1' was intentionally dropped from generation.").arg(identifier); - } - return true; - } - } - - // The top level tag 'function' has only the 'signature' tag - // and we should extract the 'name' value from it. - if (element->type == StackElement::FunctionTypeEntry - && !parseRenameFunction(reader, &name, &attributes)) { - return false; - } - - // We need to be able to have duplicate primitive type entries, - // or it's not possible to cover all primitive target language - // types (which we need to do in order to support fake meta objects) - if (element->type != StackElement::PrimitiveTypeEntry - && element->type != StackElement::FunctionTypeEntry) { - TypeEntry *tmp = m_database->findType(name); - if (tmp && !tmp->isNamespace()) - qCWarning(lcShiboken).noquote().nospace() - << QStringLiteral("Duplicate type entry: '%1'").arg(name); - } - - if (element->type == StackElement::EnumTypeEntry) { - const int enumIdentifiedByIndex = indexOfAttribute(attributes, enumIdentifiedByValueAttribute()); - const QString identifiedByValue = enumIdentifiedByIndex != -1 - ? attributes.takeAt(enumIdentifiedByIndex).value().toString() : QString(); - if (name.isEmpty()) { - name = identifiedByValue; - } else if (!identifiedByValue.isEmpty()) { - m_error = QLatin1String("can't specify both 'name' and 'identified-by-value' attributes"); - return false; - } - } - - // Fix type entry name using nesting information. - if (element->type & StackElement::TypeEntryMask - && element->parent && element->parent->type != StackElement::Root) { - name = element->parent->entry->name() + colonColon() + name; - } - - - if (name.isEmpty()) { - m_error = QLatin1String("no 'name' attribute specified"); - return false; - } - - switch (element->type) { - case StackElement::CustomTypeEntry: - element->entry = new TypeEntry(name, TypeEntry::CustomType, since); - break; - case StackElement::PrimitiveTypeEntry: - element->entry = parsePrimitiveTypeEntry(reader, name, since, &attributes); - if (Q_UNLIKELY(!element->entry)) - return false; - break; - case StackElement::ContainerTypeEntry: - if (ContainerTypeEntry *ce = parseContainerTypeEntry(reader, name, since, &attributes)) { - applyComplexTypeAttributes(reader, ce, &attributes); - element->entry = ce; - } else { - return false; - } - break; - - case StackElement::SmartPointerTypeEntry: - if (SmartPointerTypeEntry *se = parseSmartPointerEntry(reader, name, since, &attributes)) { - applyComplexTypeAttributes(reader, se, &attributes); - element->entry = se; - } else { - return false; - } - break; - case StackElement::EnumTypeEntry: - m_currentEnum = parseEnumTypeEntry(reader, name, since, &attributes); - if (Q_UNLIKELY(!m_currentEnum)) - return false; - element->entry = m_currentEnum; - break; - - case StackElement::InterfaceTypeEntry: - if (ObjectTypeEntry *oe = parseInterfaceTypeEntry(reader, name, since, &attributes)) { - applyComplexTypeAttributes(reader, oe, &attributes); - element->entry = oe; - } else { - return false; - } - break; - case StackElement::ValueTypeEntry: - if (ValueTypeEntry *ve = parseValueTypeEntry(reader, name, since, &attributes)) { - applyComplexTypeAttributes(reader, ve, &attributes); - element->entry = ve; - } else { - return false; - } - break; - case StackElement::NamespaceTypeEntry: - if (auto entry = parseNamespaceTypeEntry(reader, name, since, &attributes)) - element->entry = entry; - else - return false; - break; - case StackElement::ObjectTypeEntry: - element->entry = new ObjectTypeEntry(name, since); - applyCommonAttributes(element->entry, &attributes); - applyComplexTypeAttributes(reader, static_cast(element->entry), &attributes); - break; - case StackElement::FunctionTypeEntry: - element->entry = parseFunctionTypeEntry(reader, name, since, &attributes); - 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) { - if (!m_database->addType(element->entry, &m_error)) - return false; - } else { - qCWarning(lcShiboken).noquote().nospace() - << QStringLiteral("Type: %1 was rejected by typesystem").arg(name); - } - - } else if (element->type == StackElement::InjectDocumentation) { - if (!parseInjectDocumentation(reader, &attributes)) - return false; - } else if (element->type == StackElement::ModifyDocumentation) { - if (!parseModifyDocumentation(reader, &attributes)) - return false; - } else if (element->type != StackElement::None) { - bool topLevel = element->type == StackElement::Root - || element->type == StackElement::SuppressedWarning - || element->type == StackElement::Rejection - || element->type == StackElement::LoadTypesystem - || element->type == StackElement::InjectCode - || element->type == StackElement::ExtraIncludes - || element->type == StackElement::ConversionRule - || element->type == StackElement::AddFunction - || element->type == StackElement::Template; - - if (!topLevel && m_current->type == StackElement::Root) { - m_error = QStringLiteral("Tag requires parent: '%1'").arg(tagName); - return false; - } - - StackElement topElement = !m_current ? StackElement(nullptr) : *m_current; - element->entry = topElement.entry; - - switch (element->type) { - case StackElement::Root: - element->entry = parseRootElement(reader, since, &attributes); - element->type = StackElement::Root; - break; - case StackElement::LoadTypesystem: - if (!loadTypesystem(reader, &attributes)) - return false; - break; - case StackElement::RejectEnumValue: - if (!parseRejectEnumValue(reader, &attributes)) - return false; - break; - case StackElement::ReplaceType: - if (!parseReplaceArgumentType(reader, topElement, &attributes)) - return false; - break; - case StackElement::ConversionRule: - if (!Handler::parseCustomConversion(reader, topElement, &attributes)) - return false; - break; - case StackElement::NativeToTarget: - if (!parseNativeToTarget(reader, topElement, &attributes)) - return false; - break; - case StackElement::TargetToNative: { - if (topElement.type != StackElement::ConversionRule) { - m_error = QLatin1String("Target to Native conversions can only be specified for custom conversion rules."); - return false; - } - const int replaceIndex = indexOfAttribute(attributes, replaceAttribute()); - const bool replace = replaceIndex == -1 - || convertBoolean(attributes.takeAt(replaceIndex).value(), - replaceAttribute(), true); - m_current->entry->customConversion()->setReplaceOriginalTargetToNativeConversions(replace); - } - break; - case StackElement::AddConversion: - if (!parseAddConversion(reader, topElement, &attributes)) - return false; - break; - case StackElement::ModifyArgument: - if (!parseModifyArgument(reader, topElement, &attributes)) - return false; - break; - case StackElement::NoNullPointers: - if (!parseNoNullPointer(reader, topElement, &attributes)) - return false; - break; - case StackElement::DefineOwnership: - if (!parseDefineOwnership(reader, topElement, &attributes)) - return false; - break; - case StackElement::SuppressedWarning: { - const int textIndex = indexOfAttribute(attributes, textAttribute()); - if (textIndex == -1) { - qCWarning(lcShiboken) << "Suppressed warning with no text specified"; - } else { - const QString suppressedWarning = - attributes.takeAt(textIndex).value().toString(); - if (!m_database->addSuppressedWarning(suppressedWarning, &m_error)) - return false; - } - } - break; - case StackElement::ArgumentMap: - qCWarning(lcShiboken, "%s", - qPrintable(msgUnimplementedElementWarning(reader, tagName))); - if (!parseArgumentMap(reader, topElement, &attributes)) - return false; - break; - case StackElement::Removal: - if (!parseRemoval(reader, topElement, &attributes)) - return false; - break; - case StackElement::Rename: - case StackElement::Access: - if (!parseRename(reader, element->type, topElement, &attributes)) - return false; - break; - case StackElement::RemoveArgument: - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("Removing argument requires argument modification as parent"); - return false; - } - - m_contextStack.top()->functionMods.last().argument_mods.last().removed = true; - break; - - case StackElement::ModifyField: - if (!parseModifyField(reader, &attributes)) - return false; - break; - case StackElement::AddFunction: - if (!parseAddFunction(reader, topElement, &attributes)) - return false; - break; - case StackElement::ModifyFunction: - if (!parseModifyFunction(reader, topElement, &attributes)) - return false; - break; - case StackElement::ReplaceDefaultExpression: - if (!parseReplaceDefaultExpression(reader, topElement, &attributes)) - return false; - break; - case StackElement::RemoveDefaultExpression: - m_contextStack.top()->functionMods.last().argument_mods.last().removedDefaultExpression = true; - break; - case StackElement::CustomMetaConstructor: - case StackElement::CustomMetaDestructor: - element->value.customFunction = - parseCustomMetaConstructor(reader, element->type, topElement, &attributes); - break; - case StackElement::ReferenceCount: - if (!parseReferenceCount(reader, topElement, &attributes)) - return false; - break; - case StackElement::ParentOwner: - if (!parseParentOwner(reader, topElement, &attributes)) - return false; - break; - case StackElement::Array: - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("array must be child of modify-argument"); - return false; - } - m_contextStack.top()->functionMods.last().argument_mods.last().array = true; - break; - case StackElement::InjectCode: - if (!parseInjectCode(reader, topElement, element, &attributes)) - return false; - break; - case StackElement::Include: - if (!parseInclude(reader, topElement, element->entry, &attributes)) - return false; - break; - case StackElement::Rejection: - if (!addRejection(m_database, &attributes, &m_error)) - return false; - break; - case StackElement::Template: { - const int nameIndex = indexOfAttribute(attributes, nameAttribute()); - if (nameIndex == -1) { - m_error = msgMissingAttribute(nameAttribute()); - return false; - } - element->value.templateEntry = - new TemplateEntry(attributes.takeAt(nameIndex).value().toString()); - } - break; - case StackElement::TemplateInstanceEnum: - element->value.templateInstance = - parseTemplateInstanceEnum(reader, topElement, &attributes); - if (!element->value.templateInstance) - return false; - break; - case StackElement::Replace: - if (!parseReplace(reader, topElement, element, &attributes)) - return false; - break; - default: - break; // nada - }; - } - - if (!attributes.isEmpty()) { - const QString message = msgUnusedAttributes(tagName, attributes); - qCWarning(lcShiboken, "%s", qPrintable(msgReaderWarning(reader, message))); - } - - m_current = element; - return true; -} - PrimitiveTypeEntry::PrimitiveTypeEntry(const QString &name, const QVersionNumber &vr) : TypeEntry(name, PrimitiveType, vr), m_preferredTargetLangType(true) diff --git a/sources/shiboken2/ApiExtractor/typesystem_p.h b/sources/shiboken2/ApiExtractor/typesystem_p.h deleted file mode 100644 index 5b8b93cee..000000000 --- a/sources/shiboken2/ApiExtractor/typesystem_p.h +++ /dev/null @@ -1,273 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef TYPESYSTEM_P_H -#define TYPESYSTEM_P_H - -#include -#include -#include "typesystem.h" - -QT_FORWARD_DECLARE_CLASS(QXmlStreamAttributes) -QT_FORWARD_DECLARE_CLASS(QXmlStreamReader) - -class TypeSystemEntityResolver; -class TypeDatabase; -class StackElement -{ - public: - enum ElementType { - None = 0x0, - - // Type tags (0x1, ... , 0xff) - ObjectTypeEntry = 0x1, - ValueTypeEntry = 0x2, - InterfaceTypeEntry = 0x3, - NamespaceTypeEntry = 0x4, - ComplexTypeEntryMask = 0x7, - - // Non-complex type tags (0x8, 0x9, ... , 0xf) - PrimitiveTypeEntry = 0x8, - EnumTypeEntry = 0x9, - ContainerTypeEntry = 0xa, - FunctionTypeEntry = 0xb, - CustomTypeEntry = 0xc, - SmartPointerTypeEntry = 0xd, - TypedefTypeEntry = 0xe, - TypeEntryMask = 0xf, - - // Documentation tags - InjectDocumentation = 0x10, - ModifyDocumentation = 0x20, - DocumentationMask = 0xf0, - - // Simple tags (0x100, 0x200, ... , 0xf00) - ExtraIncludes = 0x0100, - Include = 0x0200, - ModifyFunction = 0x0300, - ModifyField = 0x0400, - Root = 0x0500, - CustomMetaConstructor = 0x0600, - CustomMetaDestructor = 0x0700, - ArgumentMap = 0x0800, - SuppressedWarning = 0x0900, - Rejection = 0x0a00, - LoadTypesystem = 0x0b00, - RejectEnumValue = 0x0c00, - Template = 0x0d00, - TemplateInstanceEnum = 0x0e00, - Replace = 0x0f00, - AddFunction = 0x1000, - NativeToTarget = 0x1100, - TargetToNative = 0x1200, - AddConversion = 0x1300, - SimpleMask = 0x3f00, - - // Code snip tags (0x1000, 0x2000, ... , 0xf000) - InjectCode = 0x4000, - InjectCodeInFunction = 0x8000, - CodeSnipMask = 0xc000, - - // Function modifier tags (0x010000, 0x020000, ... , 0xf00000) - Access = 0x010000, - Removal = 0x020000, - Rename = 0x040000, - ModifyArgument = 0x080000, - Thread = 0x100000, - FunctionModifiers = 0xff0000, - - // Argument modifier tags (0x01000000 ... 0xf0000000) - ConversionRule = 0x01000000, - ReplaceType = 0x02000000, - ReplaceDefaultExpression = 0x04000000, - RemoveArgument = 0x08000000, - DefineOwnership = 0x10000000, - RemoveDefaultExpression = 0x20000000, - NoNullPointers = 0x40000000, - ReferenceCount = 0x80000000, - ParentOwner = 0x90000000, - Array = 0xA0000000, - ArgumentModifiers = 0xff000000 - }; - - StackElement(StackElement *p) : entry(nullptr), type(None), parent(p) { } - - TypeEntry* entry; - ElementType type; - StackElement *parent; - - union { - TemplateInstance* templateInstance; - TemplateEntry* templateEntry; - CustomFunction* customFunction; - } value; -}; - -struct StackElementContext -{ - CodeSnipList codeSnips; - AddedFunctionList addedFunctions; - FunctionModificationList functionMods; - FieldModificationList fieldMods; - DocModificationList docModifications; - int addedFunctionModificationIndex = -1; -}; - -class Handler -{ -public: - Q_DISABLE_COPY(Handler) - - Handler(TypeDatabase* database, bool generate); - ~Handler(); - - bool parse(QXmlStreamReader &reader); - - QString errorString() const { return m_error; } - -private: - bool startElement(const QXmlStreamReader &reader); - SmartPointerTypeEntry *parseSmartPointerEntry(const QXmlStreamReader &, - const QString &name, - const QVersionNumber &since, - QXmlStreamAttributes *attributes); - bool endElement(const QStringRef& localName); - template // QString/QStringRef - bool characters(const String &ch); - - bool importFileElement(const QXmlStreamAttributes &atts); - - void applyCommonAttributes(TypeEntry *type, QXmlStreamAttributes *attributes) const; - PrimitiveTypeEntry * - parsePrimitiveTypeEntry(const QXmlStreamReader &, const QString &name, - const QVersionNumber &since, QXmlStreamAttributes *); - ContainerTypeEntry * - parseContainerTypeEntry(const QXmlStreamReader &, const QString &name, - const QVersionNumber &since, QXmlStreamAttributes *); - EnumTypeEntry * - parseEnumTypeEntry(const QXmlStreamReader &, const QString &name, - const QVersionNumber &since, QXmlStreamAttributes *); - FlagsTypeEntry * - parseFlagsEntry(const QXmlStreamReader &, EnumTypeEntry *enumEntry, - const QString &name, QString flagName, - const QVersionNumber &since, QXmlStreamAttributes *); - - NamespaceTypeEntry * - parseNamespaceTypeEntry(const QXmlStreamReader &, - const QString &name, const QVersionNumber &since, - QXmlStreamAttributes *attributes); - - ObjectTypeEntry * - parseInterfaceTypeEntry(const QXmlStreamReader &, const QString &name, - const QVersionNumber &since, QXmlStreamAttributes *); - ValueTypeEntry * - parseValueTypeEntry(const QXmlStreamReader &, const QString &name, - const QVersionNumber &since, QXmlStreamAttributes *); - 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, - QXmlStreamAttributes *); - bool parseInjectDocumentation(const QXmlStreamReader &, QXmlStreamAttributes *); - bool parseModifyDocumentation(const QXmlStreamReader &, QXmlStreamAttributes *); - TypeSystemTypeEntry * - parseRootElement(const QXmlStreamReader &, const QVersionNumber &since, - QXmlStreamAttributes *); - bool loadTypesystem(const QXmlStreamReader &, QXmlStreamAttributes *); - bool parseRejectEnumValue(const QXmlStreamReader &, QXmlStreamAttributes *); - bool parseReplaceArgumentType(const QXmlStreamReader &, const StackElement &topElement, - QXmlStreamAttributes *); - bool parseCustomConversion(const QXmlStreamReader &, const StackElement &topElement, - QXmlStreamAttributes *); - bool parseAddConversion(const QXmlStreamReader &, const StackElement &topElement, - QXmlStreamAttributes *); - bool parseNativeToTarget(const QXmlStreamReader &, const StackElement &topElement, - QXmlStreamAttributes *attributes); - bool parseModifyArgument(const QXmlStreamReader &, const StackElement &topElement, - QXmlStreamAttributes *attributes); - bool parseNoNullPointer(const QXmlStreamReader &, const StackElement &topElement, - QXmlStreamAttributes *attributes); - bool parseDefineOwnership(const QXmlStreamReader &, const StackElement &topElement, - QXmlStreamAttributes *); - bool parseArgumentMap(const QXmlStreamReader &, const StackElement &topElement, - QXmlStreamAttributes *); - bool parseRemoval(const QXmlStreamReader &, const StackElement &topElement, - QXmlStreamAttributes *); - bool parseRename(const QXmlStreamReader &, StackElement::ElementType type, - const StackElement &topElement, QXmlStreamAttributes *); - bool parseModifyField(const QXmlStreamReader &, QXmlStreamAttributes *); - bool parseAddFunction(const QXmlStreamReader &, const StackElement &topElement, - QXmlStreamAttributes *); - bool parseModifyFunction(const QXmlStreamReader &, const StackElement &topElement, - QXmlStreamAttributes *); - bool parseReplaceDefaultExpression(const QXmlStreamReader &, - const StackElement &topElement, QXmlStreamAttributes *); - CustomFunction * - parseCustomMetaConstructor(const QXmlStreamReader &, - StackElement::ElementType type, - const StackElement &topElement, QXmlStreamAttributes *); - bool parseReferenceCount(const QXmlStreamReader &, const StackElement &topElement, - QXmlStreamAttributes *); - bool parseParentOwner(const QXmlStreamReader &, const StackElement &topElement, - QXmlStreamAttributes *); - bool readFileSnippet(QXmlStreamAttributes *attributes, CodeSnip *snip); - bool parseInjectCode(const QXmlStreamReader &, const StackElement &topElement, - StackElement* element, QXmlStreamAttributes *); - bool parseInclude(const QXmlStreamReader &, const StackElement &topElement, - TypeEntry *entry, QXmlStreamAttributes *); - TemplateInstance - *parseTemplateInstanceEnum(const QXmlStreamReader &, const StackElement &topElement, - QXmlStreamAttributes *); - bool parseReplace(const QXmlStreamReader &, const StackElement &topElement, - StackElement *element, QXmlStreamAttributes *); - - TypeDatabase* m_database; - StackElement* m_current = nullptr; - StackElement* m_currentDroppedEntry = nullptr; - int m_currentDroppedEntryDepth = 0; - int m_ignoreDepth = 0; - QString m_defaultPackage; - QString m_defaultSuperclass; - TypeSystem::ExceptionHandling m_exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; - TypeSystem::AllowThread m_allowThread = TypeSystem::AllowThread::Unspecified; - QString m_error; - const TypeEntry::CodeGeneration m_generate; - - EnumTypeEntry* m_currentEnum = nullptr; - QStack m_contextStack; - - QString m_currentSignature; - QString m_currentPath; - QScopedPointer m_entityResolver; -}; - -#endif diff --git a/sources/shiboken2/ApiExtractor/typesystemparser.cpp b/sources/shiboken2/ApiExtractor/typesystemparser.cpp new file mode 100644 index 000000000..57ceb357f --- /dev/null +++ b/sources/shiboken2/ApiExtractor/typesystemparser.cpp @@ -0,0 +1,2974 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 "typesystemparser.h" +#include "typedatabase.h" +#include "messages.h" +#include "reporthandler.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +const char *TARGET_CONVERSION_RULE_FLAG = "0"; +const char *NATIVE_CONVERSION_RULE_FLAG = "1"; + +static inline QString allowThreadAttribute() { return QStringLiteral("allow-thread"); } +static inline QString colonColon() { return QStringLiteral("::"); } +static inline QString copyableAttribute() { return QStringLiteral("copyable"); } +static inline QString accessAttribute() { return QStringLiteral("access"); } +static inline QString actionAttribute() { return QStringLiteral("action"); } +static inline QString quoteAfterLineAttribute() { return QStringLiteral("quote-after-line"); } +static inline QString quoteBeforeLineAttribute() { return QStringLiteral("quote-before-line"); } +static inline QString textAttribute() { return QStringLiteral("text"); } +static inline QString nameAttribute() { return QStringLiteral("name"); } +static inline QString sinceAttribute() { return QStringLiteral("since"); } +static inline QString defaultSuperclassAttribute() { return QStringLiteral("default-superclass"); } +static inline QString deleteInMainThreadAttribute() { return QStringLiteral("delete-in-main-thread"); } +static inline QString deprecatedAttribute() { return QStringLiteral("deprecated"); } +static inline QString exceptionHandlingAttribute() { return QStringLiteral("exception-handling"); } +static inline QString extensibleAttribute() { return QStringLiteral("extensible"); } +static inline QString flagsAttribute() { return QStringLiteral("flags"); } +static inline QString forceAbstractAttribute() { return QStringLiteral("force-abstract"); } +static inline QString forceIntegerAttribute() { return QStringLiteral("force-integer"); } +static inline QString formatAttribute() { return QStringLiteral("format"); } +static inline QString classAttribute() { return QStringLiteral("class"); } +static inline QString generateAttribute() { return QStringLiteral("generate"); } +static inline QString genericClassAttribute() { return QStringLiteral("generic-class"); } +static inline QString indexAttribute() { return QStringLiteral("index"); } +static inline QString invalidateAfterUseAttribute() { return QStringLiteral("invalidate-after-use"); } +static inline QString locationAttribute() { return QStringLiteral("location"); } +static inline QString modifiedTypeAttribute() { return QStringLiteral("modified-type"); } +static inline QString modifierAttribute() { return QStringLiteral("modifier"); } +static inline QString ownershipAttribute() { return QStringLiteral("owner"); } +static inline QString packageAttribute() { return QStringLiteral("package"); } +static inline QString positionAttribute() { return QStringLiteral("position"); } +static inline QString preferredConversionAttribute() { return QStringLiteral("preferred-conversion"); } +static inline QString preferredTargetLangTypeAttribute() { return QStringLiteral("preferred-target-lang-type"); } +static inline QString removeAttribute() { return QStringLiteral("remove"); } +static inline QString renameAttribute() { return QStringLiteral("rename"); } +static inline QString readAttribute() { return QStringLiteral("read"); } +static inline QString writeAttribute() { return QStringLiteral("write"); } +static inline QString replaceAttribute() { return QStringLiteral("replace"); } +static inline QString toAttribute() { return QStringLiteral("to"); } +static inline QString signatureAttribute() { return QStringLiteral("signature"); } +static inline QString snippetAttribute() { return QStringLiteral("snippet"); } +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"); } +static inline QString enumIdentifiedByValueAttribute() { return QStringLiteral("identified-by-value"); } + +static inline QString noAttributeValue() { return QStringLiteral("no"); } +static inline QString yesAttributeValue() { return QStringLiteral("yes"); } +static inline QString trueAttributeValue() { return QStringLiteral("true"); } +static inline QString falseAttributeValue() { return QStringLiteral("false"); } + +static QVector customConversionsForReview; + +// Set a regular expression for rejection from text. By legacy, those are fixed +// strings, except for '*' meaning 'match all'. Enclosing in "^..$" +// indicates regular expression. +static bool setRejectionRegularExpression(const QString &patternIn, + QRegularExpression *re, + QString *errorMessage) +{ + QString pattern; + if (patternIn.startsWith(QLatin1Char('^')) && patternIn.endsWith(QLatin1Char('$'))) + pattern = patternIn; + else if (patternIn == QLatin1String("*")) + pattern = QStringLiteral("^.*$"); + else + pattern = QLatin1Char('^') + QRegularExpression::escape(patternIn) + QLatin1Char('$'); + re->setPattern(pattern); + if (!re->isValid()) { + *errorMessage = msgInvalidRegularExpression(patternIn, re->errorString()); + return false; + } + return true; +} + +// Extract a snippet from a file within annotation "// @snippet label". +static QString extractSnippet(const QString &code, const QString &snippetLabel) +{ + if (snippetLabel.isEmpty()) + return code; + const QString pattern = QStringLiteral(R"(^\s*//\s*@snippet\s+)") + + QRegularExpression::escape(snippetLabel) + + QStringLiteral(R"(\s*$)"); + const QRegularExpression snippetRe(pattern); + Q_ASSERT(snippetRe.isValid()); + + bool useLine = false; + QString result; + const auto lines = code.splitRef(QLatin1Char('\n')); + for (const QStringRef &line : lines) { + if (snippetRe.match(line).hasMatch()) { + useLine = !useLine; + if (!useLine) + break; // End of snippet reached + } else if (useLine) + result += line.toString() + QLatin1Char('\n'); + } + return result; +} + +template +struct EnumLookup +{ + QStringView name; + EnumType value; +}; + +template +bool operator==(const EnumLookup &e1, const EnumLookup &e2) +{ + return e1.name.compare(e2.name, cs) == 0; +} + +template +bool operator<(const EnumLookup &e1, const EnumLookup &e2) +{ + return e1.name.compare(e2.name, cs) < 0; +} + +// Helper macros to define lookup functions that take a QStringView needle +// and an optional default return value. +#define ENUM_LOOKUP_BEGIN(EnumType, caseSensitivity, functionName, defaultReturnValue) \ +static EnumType functionName(QStringView needle, EnumType defaultValue = defaultReturnValue) \ +{ \ + typedef EnumLookup HaystackEntry; \ + static const HaystackEntry haystack[] = + +#define ENUM_LOOKUP_LINEAR_SEARCH() \ + const auto end = haystack + sizeof(haystack) / sizeof(haystack[0]); \ + const auto it = std::find(haystack, end, HaystackEntry{needle, defaultValue}); \ + return it != end ? it->value : defaultValue; \ +} + +#define ENUM_LOOKUP_BINARY_SEARCH() \ + const auto end = haystack + sizeof(haystack) / sizeof(haystack[0]); \ + const HaystackEntry needleEntry{needle, defaultValue}; \ + const auto lb = std::lower_bound(haystack, end, needleEntry); \ + return lb != end && *lb == needleEntry ? lb->value : defaultValue; \ +} + +ENUM_LOOKUP_BEGIN(TypeSystem::AllowThread, Qt::CaseInsensitive, + allowThreadFromAttribute, TypeSystem::AllowThread::Unspecified) + { + {QStringViewLiteral("yes"), TypeSystem::AllowThread::Allow}, + {QStringViewLiteral("true"), TypeSystem::AllowThread::Allow}, + {QStringViewLiteral("auto"), TypeSystem::AllowThread::Auto}, + {QStringViewLiteral("no"), TypeSystem::AllowThread::Disallow}, + {QStringViewLiteral("false"), TypeSystem::AllowThread::Disallow}, + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(TypeSystem::Language, Qt::CaseInsensitive, + languageFromAttribute, TypeSystem::NoLanguage) + { + {QStringViewLiteral("all"), TypeSystem::All}, // sorted! + {QStringViewLiteral("constructors"), TypeSystem::Constructors}, + {QStringViewLiteral("destructor-function"), TypeSystem::DestructorFunction}, + {QStringViewLiteral("interface"), TypeSystem::Interface}, + {QStringViewLiteral("library-initializer"), TypeSystem::PackageInitializer}, + {QStringViewLiteral("native"), TypeSystem::NativeCode}, // em algum lugar do cpp + {QStringViewLiteral("shell"), TypeSystem::ShellCode}, // coloca no header, mas antes da declaracao da classe + {QStringViewLiteral("shell-declaration"), TypeSystem::ShellDeclaration}, + {QStringViewLiteral("target"), TypeSystem::TargetLangCode} // em algum lugar do cpp + }; +ENUM_LOOKUP_BINARY_SEARCH() + +ENUM_LOOKUP_BEGIN(TypeSystem::Ownership, Qt::CaseInsensitive, + ownershipFromFromAttribute, TypeSystem::InvalidOwnership) + { + {QStringViewLiteral("target"), TypeSystem::TargetLangOwnership}, + {QStringViewLiteral("c++"), TypeSystem::CppOwnership}, + {QStringViewLiteral("default"), TypeSystem::DefaultOwnership} + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(AddedFunction::Access, Qt::CaseInsensitive, + addedFunctionAccessFromAttribute, AddedFunction::InvalidAccess) + { + {QStringViewLiteral("public"), AddedFunction::Public}, + {QStringViewLiteral("protected"), AddedFunction::Protected}, + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(Modification::Modifiers, Qt::CaseSensitive, + modifierFromAttribute, Modification::InvalidModifier) + { + {QStringViewLiteral("private"), Modification::Private}, + {QStringViewLiteral("public"), Modification::Public}, + {QStringViewLiteral("protected"), Modification::Protected}, + {QStringViewLiteral("friendly"), Modification::Friendly}, + {QStringViewLiteral("rename"), Modification::Rename}, + {QStringViewLiteral("final"), Modification::Final}, + {QStringViewLiteral("non-final"), Modification::NonFinal} + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(ReferenceCount::Action, Qt::CaseInsensitive, + referenceCountFromAttribute, ReferenceCount::Invalid) + { + {QStringViewLiteral("add"), ReferenceCount::Add}, + {QStringViewLiteral("add-all"), ReferenceCount::AddAll}, + {QStringViewLiteral("remove"), ReferenceCount::Remove}, + {QStringViewLiteral("set"), ReferenceCount::Set}, + {QStringViewLiteral("ignore"), ReferenceCount::Ignore} + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(ArgumentOwner::Action, Qt::CaseInsensitive, + argumentOwnerActionFromAttribute, ArgumentOwner::Invalid) + { + {QStringViewLiteral("add"), ArgumentOwner::Add}, + {QStringViewLiteral("remove"), ArgumentOwner::Remove} + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(TypeSystem::CodeSnipPosition, Qt::CaseInsensitive, + codeSnipPositionFromAttribute, TypeSystem::CodeSnipPositionInvalid) + { + {QStringViewLiteral("beginning"), TypeSystem::CodeSnipPositionBeginning}, + {QStringViewLiteral("end"), TypeSystem::CodeSnipPositionEnd}, + {QStringViewLiteral("declaration"), TypeSystem::CodeSnipPositionDeclaration}, + {QStringViewLiteral("prototype-initialization"), TypeSystem::CodeSnipPositionPrototypeInitialization}, + {QStringViewLiteral("constructor-initialization"), TypeSystem::CodeSnipPositionConstructorInitialization}, + {QStringViewLiteral("constructor"), TypeSystem::CodeSnipPositionConstructor} + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(Include::IncludeType, Qt::CaseInsensitive, + locationFromAttribute, Include::InvalidInclude) + { + {QStringViewLiteral("global"), Include::IncludePath}, + {QStringViewLiteral("local"), Include::LocalPath}, + {QStringViewLiteral("target"), Include::TargetLangImport} + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(TypeSystem::DocModificationMode, Qt::CaseInsensitive, + docModificationFromAttribute, TypeSystem::DocModificationInvalid) + { + {QStringViewLiteral("append"), TypeSystem::DocModificationAppend}, + {QStringViewLiteral("prepend"), TypeSystem::DocModificationPrepend}, + {QStringViewLiteral("replace"), TypeSystem::DocModificationReplace} + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(ContainerTypeEntry::Type, Qt::CaseSensitive, + containerTypeFromAttribute, ContainerTypeEntry::NoContainer) + { + {QStringViewLiteral("list"), ContainerTypeEntry::ListContainer}, + {QStringViewLiteral("string-list"), ContainerTypeEntry::StringListContainer}, + {QStringViewLiteral("linked-list"), ContainerTypeEntry::LinkedListContainer}, + {QStringViewLiteral("vector"), ContainerTypeEntry::VectorContainer}, + {QStringViewLiteral("stack"), ContainerTypeEntry::StackContainer}, + {QStringViewLiteral("queue"), ContainerTypeEntry::QueueContainer}, + {QStringViewLiteral("set"), ContainerTypeEntry::SetContainer}, + {QStringViewLiteral("map"), ContainerTypeEntry::MapContainer}, + {QStringViewLiteral("multi-map"), ContainerTypeEntry::MultiMapContainer}, + {QStringViewLiteral("hash"), ContainerTypeEntry::HashContainer}, + {QStringViewLiteral("multi-hash"), ContainerTypeEntry::MultiHashContainer}, + {QStringViewLiteral("pair"), ContainerTypeEntry::PairContainer} + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(TypeRejection::MatchType, Qt::CaseSensitive, + typeRejectionFromAttribute, TypeRejection::Invalid) + { + {QStringViewLiteral("class"), TypeRejection::ExcludeClass}, + {QStringViewLiteral("function-name"), TypeRejection::Function}, + {QStringViewLiteral("field-name"), TypeRejection::Field}, + {QStringViewLiteral("enum-name"), TypeRejection::Enum }, + {QStringViewLiteral("argument-type"), TypeRejection::ArgumentType}, + {QStringViewLiteral("return-type"), TypeRejection::ReturnType} + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(TypeSystem::ExceptionHandling, Qt::CaseSensitive, + exceptionHandlingFromAttribute, TypeSystem::ExceptionHandling::Unspecified) +{ + {QStringViewLiteral("no"), TypeSystem::ExceptionHandling::Off}, + {QStringViewLiteral("false"), TypeSystem::ExceptionHandling::Off}, + {QStringViewLiteral("auto-off"), TypeSystem::ExceptionHandling::AutoDefaultToOff}, + {QStringViewLiteral("auto-on"), TypeSystem::ExceptionHandling::AutoDefaultToOn}, + {QStringViewLiteral("yes"), TypeSystem::ExceptionHandling::On}, + {QStringViewLiteral("true"), TypeSystem::ExceptionHandling::On}, +}; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(StackElement::ElementType, Qt::CaseInsensitive, + elementFromTag, StackElement::None) + { + {QStringViewLiteral("access"), StackElement::Access}, // sorted! + {QStringViewLiteral("add-conversion"), StackElement::AddConversion}, + {QStringViewLiteral("add-function"), StackElement::AddFunction}, + {QStringViewLiteral("argument-map"), StackElement::ArgumentMap}, + {QStringViewLiteral("array"), StackElement::Array}, + {QStringViewLiteral("container-type"), StackElement::ContainerTypeEntry}, + {QStringViewLiteral("conversion-rule"), StackElement::ConversionRule}, + {QStringViewLiteral("custom-constructor"), StackElement::CustomMetaConstructor}, + {QStringViewLiteral("custom-destructor"), StackElement::CustomMetaDestructor}, + {QStringViewLiteral("custom-type"), StackElement::CustomTypeEntry}, + {QStringViewLiteral("define-ownership"), StackElement::DefineOwnership}, + {QStringViewLiteral("enum-type"), StackElement::EnumTypeEntry}, + {QStringViewLiteral("extra-includes"), StackElement::ExtraIncludes}, + {QStringViewLiteral("function"), StackElement::FunctionTypeEntry}, + {QStringViewLiteral("include"), StackElement::Include}, + {QStringViewLiteral("inject-code"), StackElement::InjectCode}, + {QStringViewLiteral("inject-documentation"), StackElement::InjectDocumentation}, + {QStringViewLiteral("insert-template"), StackElement::TemplateInstanceEnum}, + {QStringViewLiteral("interface-type"), StackElement::InterfaceTypeEntry}, + {QStringViewLiteral("load-typesystem"), StackElement::LoadTypesystem}, + {QStringViewLiteral("modify-argument"), StackElement::ModifyArgument}, + {QStringViewLiteral("modify-documentation"), StackElement::ModifyDocumentation}, + {QStringViewLiteral("modify-field"), StackElement::ModifyField}, + {QStringViewLiteral("modify-function"), StackElement::ModifyFunction}, + {QStringViewLiteral("namespace-type"), StackElement::NamespaceTypeEntry}, + {QStringViewLiteral("native-to-target"), StackElement::NativeToTarget}, + {QStringViewLiteral("no-null-pointer"), StackElement::NoNullPointers}, + {QStringViewLiteral("object-type"), StackElement::ObjectTypeEntry}, + {QStringViewLiteral("parent"), StackElement::ParentOwner}, + {QStringViewLiteral("primitive-type"), StackElement::PrimitiveTypeEntry}, + {QStringViewLiteral("reference-count"), StackElement::ReferenceCount}, + {QStringViewLiteral("reject-enum-value"), StackElement::RejectEnumValue}, + {QStringViewLiteral("rejection"), StackElement::Rejection}, + {QStringViewLiteral("remove"), StackElement::Removal}, + {QStringViewLiteral("remove-argument"), StackElement::RemoveArgument}, + {QStringViewLiteral("remove-default-expression"), StackElement::RemoveDefaultExpression}, + {QStringViewLiteral("rename"), StackElement::Rename}, + {QStringViewLiteral("replace"), StackElement::Replace}, + {QStringViewLiteral("replace-default-expression"), StackElement::ReplaceDefaultExpression}, + {QStringViewLiteral("replace-type"), StackElement::ReplaceType}, + {QStringViewLiteral("smart-pointer-type"), StackElement::SmartPointerTypeEntry}, + {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}, + }; +ENUM_LOOKUP_BINARY_SEARCH() + +static int indexOfAttribute(const QXmlStreamAttributes &atts, + QStringView name) +{ + for (int i = 0, size = atts.size(); i < size; ++i) { + if (atts.at(i).qualifiedName() == name) + return i; + } + return -1; +} + +static QString msgMissingAttribute(const QString &a) +{ + return QLatin1String("Required attribute '") + a + + QLatin1String("' missing."); +} + +QTextStream &operator<<(QTextStream &str, const QXmlStreamAttribute &attribute) +{ + str << attribute.qualifiedName() << "=\"" << attribute.value() << '"'; + return str; +} + +static QString msgInvalidAttributeValue(const QXmlStreamAttribute &attribute) +{ + QString result; + QTextStream(&result) << "Invalid attribute value:" << attribute; + return result; +} + +static QString msgUnusedAttributes(const QStringRef &tag, const QXmlStreamAttributes &attributes) +{ + QString result; + QTextStream str(&result); + str << attributes.size() << " attributes(s) unused on <" << tag << ">: "; + for (int i = 0, size = attributes.size(); i < size; ++i) { + if (i) + str << ", "; + str << attributes.at(i); + } + return result; +} + +// QXmlStreamEntityResolver::resolveEntity(publicId, systemId) is not +// implemented; resolve via undeclared entities instead. +class TypeSystemEntityResolver : public QXmlStreamEntityResolver +{ +public: + explicit TypeSystemEntityResolver(const QString ¤tPath) : + m_currentPath(currentPath) {} + + QString resolveUndeclaredEntity(const QString &name) override; + +private: + QString readFile(const QString &entityName, QString *errorMessage) const; + + const QString m_currentPath; + QHash m_cache; +}; + +QString TypeSystemEntityResolver::readFile(const QString &entityName, QString *errorMessage) const +{ + QString fileName = entityName; + if (!fileName.contains(QLatin1Char('.'))) + fileName += QLatin1String(".xml"); + QString path = TypeDatabase::instance()->modifiedTypesystemFilepath(fileName, m_currentPath); + if (!QFileInfo::exists(path)) // PySide2-specific hack + fileName.prepend(QLatin1String("typesystem_")); + path = TypeDatabase::instance()->modifiedTypesystemFilepath(fileName, m_currentPath); + if (!QFileInfo::exists(path)) { + *errorMessage = QLatin1String("Unable to resolve: ") + entityName; + return QString(); + } + QFile file(path); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + *errorMessage = msgCannotOpenForReading(file); + return QString(); + } + QString result = QString::fromUtf8(file.readAll()).trimmed(); + // Remove license header comments on which QXmlStreamReader chokes + if (result.startsWith(QLatin1String("")); + if (commentEnd != -1) { + result.remove(0, commentEnd + 3); + result = result.trimmed(); + } + } + return result; +} + +QString TypeSystemEntityResolver::resolveUndeclaredEntity(const QString &name) +{ + auto it = m_cache.find(name); + if (it == m_cache.end()) { + QString errorMessage; + it = m_cache.insert(name, readFile(name, &errorMessage)); + if (it.value().isEmpty()) { // The parser will fail and display the line number. + qCWarning(lcShiboken, "%s", + qPrintable(msgCannotResolveEntity(name, errorMessage))); + } + } + return it.value(); +} + +TypeSystemParser::TypeSystemParser(TypeDatabase *database, bool generate) : + m_database(database), + m_generate(generate ? TypeEntry::GenerateAll : TypeEntry::GenerateForSubclass) +{ +} + +TypeSystemParser::~TypeSystemParser() = default; + +static QString readerFileName(const QXmlStreamReader &reader) +{ + const auto *file = qobject_cast(reader.device()); + return file != nullptr ? file->fileName() : QString(); +} + +static QString msgReaderMessage(const QXmlStreamReader &reader, + const char *type, + const QString &what) +{ + QString message; + QTextStream str(&message); + str << type << ": "; + const QString fileName = readerFileName(reader); + if (fileName.isEmpty()) + str << ":"; + else + str << QDir::toNativeSeparators(fileName) << ':'; + str << reader.lineNumber() << ':' << reader.columnNumber() + << ": " << what; + return message; +} + +static QString msgReaderWarning(const QXmlStreamReader &reader, const QString &what) +{ + return msgReaderMessage(reader, "Warning", what); +} + +static QString msgReaderError(const QXmlStreamReader &reader, const QString &what) +{ + return msgReaderMessage(reader, "Error", what); +} + +static QString msgUnimplementedElementWarning(const QXmlStreamReader &reader, + const QStringRef &name) +{ + const QString message = QLatin1String("The element \"") + + name + QLatin1String("\" is not implemented."); + return msgReaderMessage(reader, "Warning", message); +} + +static QString msgUnimplementedAttributeWarning(const QXmlStreamReader &reader, + const QStringRef &name) +{ + const QString message = QLatin1String("The attribute \"") + + name + QLatin1String("\" is not implemented."); + return msgReaderMessage(reader, "Warning", message); +} + +static inline QString msgUnimplementedAttributeWarning(const QXmlStreamReader &reader, + const QXmlStreamAttribute &attribute) +{ + return msgUnimplementedAttributeWarning(reader, attribute.qualifiedName()); +} + +static QString + msgUnimplementedAttributeValueWarning(const QXmlStreamReader &reader, + QStringView name, QStringView value) +{ + QString message; + QTextStream(&message) << "The value \"" << value + << "\" of the attribute \"" << name << "\" is not implemented."; + return msgReaderMessage(reader, "Warning", message); +} + +static inline + QString msgUnimplementedAttributeValueWarning(const QXmlStreamReader &reader, + const QXmlStreamAttribute &attribute) +{ + return msgUnimplementedAttributeValueWarning(reader, + attribute.qualifiedName(), + attribute.value()); +} + +static QString msgInvalidVersion(const QStringRef &version, const QString &package = QString()) +{ + QString result; + QTextStream str(&result); + str << "Invalid version \"" << version << '"'; + if (!package.isEmpty()) + str << "\" specified for package " << package; + str << '.'; + return result; +} + +static bool addRejection(TypeDatabase *database, QXmlStreamAttributes *attributes, + QString *errorMessage) +{ + const int classIndex = indexOfAttribute(*attributes, classAttribute()); + if (classIndex == -1) { + *errorMessage = msgMissingAttribute(classAttribute()); + return false; + } + + TypeRejection rejection; + const QString className = attributes->takeAt(classIndex).value().toString(); + if (!setRejectionRegularExpression(className, &rejection.className, errorMessage)) + return false; + + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + const TypeRejection::MatchType type = typeRejectionFromAttribute(name); + switch (type) { + case TypeRejection::Function: + case TypeRejection::Field: + case TypeRejection::Enum: + case TypeRejection::ArgumentType: + case TypeRejection::ReturnType: { + const QString pattern = attributes->takeAt(i).value().toString(); + if (!setRejectionRegularExpression(pattern, &rejection.pattern, errorMessage)) + return false; + rejection.matchType = type; + database->addRejection(rejection); + return true; + } + default: + break; + } + } + + // Special case: When all fields except class are empty, completely exclude class + if (className == QLatin1String("*")) { + *errorMessage = QLatin1String("bad reject entry, neither 'class', 'function-name'" + " nor 'field' specified"); + return false; + } + rejection.matchType = TypeRejection::ExcludeClass; + database->addRejection(rejection); + return true; +} + +bool TypeSystemParser::parse(QXmlStreamReader &reader) +{ + m_error.clear(); + m_currentPath.clear(); + const QString fileName = readerFileName(reader); + if (!fileName.isEmpty()) + m_currentPath = QFileInfo(fileName).absolutePath(); + m_entityResolver.reset(new TypeSystemEntityResolver(m_currentPath)); + reader.setEntityResolver(m_entityResolver.data()); + + while (!reader.atEnd()) { + switch (reader.readNext()) { + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + m_error = msgReaderError(reader, reader.errorString()); + return false; + case QXmlStreamReader::StartElement: + if (!startElement(reader)) { + m_error = msgReaderError(reader, m_error); + return false; + } + + break; + case QXmlStreamReader::EndElement: + if (!endElement(reader.name())) { + m_error = msgReaderError(reader, m_error); + return false; + } + break; + case QXmlStreamReader::Characters: + if (!characters(reader.text())) { + m_error = msgReaderError(reader, m_error); + return false; + } + break; + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } + return true; +} + +bool TypeSystemParser::endElement(const QStringRef &localName) +{ + if (m_ignoreDepth) { + --m_ignoreDepth; + return true; + } + + if (m_currentDroppedEntry) { + if (m_currentDroppedEntryDepth == 1) { + m_current = m_currentDroppedEntry->parent; + delete m_currentDroppedEntry; + m_currentDroppedEntry = nullptr; + m_currentDroppedEntryDepth = 0; + } else { + --m_currentDroppedEntryDepth; + } + return true; + } + + if (!localName.compare(QLatin1String("import-file"), Qt::CaseInsensitive)) + return true; + + if (!m_current) + return true; + + switch (m_current->type) { + case StackElement::Root: + if (m_generate == TypeEntry::GenerateAll) { + TypeDatabase::instance()->addGlobalUserFunctions(m_contextStack.top()->addedFunctions); + TypeDatabase::instance()->addGlobalUserFunctionModifications(m_contextStack.top()->functionMods); + for (CustomConversion *customConversion : qAsConst(customConversionsForReview)) { + const CustomConversion::TargetToNativeConversions &toNatives = customConversion->targetToNativeConversions(); + for (CustomConversion::TargetToNativeConversion *toNative : toNatives) + toNative->setSourceType(m_database->findType(toNative->sourceTypeName())); + } + } + break; + case StackElement::ObjectTypeEntry: + case StackElement::ValueTypeEntry: + case StackElement::InterfaceTypeEntry: + case StackElement::NamespaceTypeEntry: { + auto *centry = static_cast(m_current->entry); + centry->setAddedFunctions(m_contextStack.top()->addedFunctions); + centry->setFunctionModifications(m_contextStack.top()->functionMods); + centry->setFieldModifications(m_contextStack.top()->fieldMods); + centry->setCodeSnips(m_contextStack.top()->codeSnips); + centry->setDocModification(m_contextStack.top()->docModifications); + + if (centry->designatedInterface()) { + centry->designatedInterface()->setCodeSnips(m_contextStack.top()->codeSnips); + centry->designatedInterface()->setFunctionModifications(m_contextStack.top()->functionMods); + } + } + break; + case StackElement::AddFunction: { + // Leaving add-function: Assign all modifications to the added function + StackElementContext *top = m_contextStack.top(); + const int modIndex = top->addedFunctionModificationIndex; + top->addedFunctionModificationIndex = -1; + Q_ASSERT(modIndex >= 0); + Q_ASSERT(!top->addedFunctions.isEmpty()); + while (modIndex < top->functionMods.size()) + top->addedFunctions.last()->modifications.append(top->functionMods.takeAt(modIndex)); + } + break; + case StackElement::NativeToTarget: + case StackElement::AddConversion: { + CustomConversion* customConversion = static_cast(m_current->entry)->customConversion(); + if (!customConversion) { + m_error = QLatin1String("CustomConversion object is missing."); + return false; + } + + QString code = m_contextStack.top()->codeSnips.takeLast().code(); + if (m_current->type == StackElement::AddConversion) { + if (customConversion->targetToNativeConversions().isEmpty()) { + m_error = QLatin1String("CustomConversion's target to native conversions missing."); + return false; + } + customConversion->targetToNativeConversions().last()->setConversion(code); + } else { + customConversion->setNativeToTargetConversion(code); + } + } + break; + case StackElement::CustomMetaConstructor: { + m_current->entry->setCustomConstructor(*m_current->value.customFunction); + delete m_current->value.customFunction; + } + break; + case StackElement::CustomMetaDestructor: { + m_current->entry->setCustomDestructor(*m_current->value.customFunction); + delete m_current->value.customFunction; + } + break; + case StackElement::EnumTypeEntry: + m_current->entry->setDocModification(m_contextStack.top()->docModifications); + m_contextStack.top()->docModifications = DocModificationList(); + m_currentEnum = nullptr; + break; + case StackElement::Template: + m_database->addTemplate(m_current->value.templateEntry); + break; + case StackElement::TemplateInstanceEnum: + switch (m_current->parent->type) { + case StackElement::InjectCode: + if (m_current->parent->parent->type == StackElement::Root) { + CodeSnipList snips = m_current->parent->entry->codeSnips(); + CodeSnip snip = snips.takeLast(); + snip.addTemplateInstance(m_current->value.templateInstance); + snips.append(snip); + m_current->parent->entry->setCodeSnips(snips); + break; + } + Q_FALLTHROUGH(); + case StackElement::NativeToTarget: + case StackElement::AddConversion: + m_contextStack.top()->codeSnips.last().addTemplateInstance(m_current->value.templateInstance); + break; + case StackElement::Template: + m_current->parent->value.templateEntry->addTemplateInstance(m_current->value.templateInstance); + break; + case StackElement::CustomMetaConstructor: + case StackElement::CustomMetaDestructor: + m_current->parent->value.customFunction->addTemplateInstance(m_current->value.templateInstance); + break; + case StackElement::ConversionRule: + m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.last().addTemplateInstance(m_current->value.templateInstance); + break; + case StackElement::InjectCodeInFunction: + m_contextStack.top()->functionMods.last().snips.last().addTemplateInstance(m_current->value.templateInstance); + break; + default: + break; // nada + }; + break; + default: + break; + } + + switch (m_current->type) { + case StackElement::Root: + case StackElement::NamespaceTypeEntry: + case StackElement::InterfaceTypeEntry: + case StackElement::ObjectTypeEntry: + case StackElement::ValueTypeEntry: + case StackElement::PrimitiveTypeEntry: + case StackElement::TypedefTypeEntry: + delete m_contextStack.pop(); + break; + default: + break; + } + + StackElement *child = m_current; + m_current = m_current->parent; + delete(child); + + return true; +} + +template // QString/QStringRef +bool TypeSystemParser::characters(const String &ch) +{ + if (m_currentDroppedEntry || m_ignoreDepth) + return true; + + if (m_current->type == StackElement::Template) { + m_current->value.templateEntry->addCode(ch); + return true; + } + + if (m_current->type == StackElement::CustomMetaConstructor || m_current->type == StackElement::CustomMetaDestructor) { + m_current->value.customFunction->addCode(ch); + return true; + } + + if (m_current->type == StackElement::ConversionRule + && m_current->parent->type == StackElement::ModifyArgument) { + m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.last().addCode(ch); + return true; + } + + if (m_current->type == StackElement::NativeToTarget || m_current->type == StackElement::AddConversion) { + m_contextStack.top()->codeSnips.last().addCode(ch); + return true; + } + + if (m_current->parent) { + if ((m_current->type & StackElement::CodeSnipMask)) { + CodeSnipList snips; + switch (m_current->parent->type) { + case StackElement::Root: + snips = m_current->parent->entry->codeSnips(); + snips.last().addCode(ch); + m_current->parent->entry->setCodeSnips(snips); + break; + case StackElement::ModifyFunction: + case StackElement::AddFunction: + m_contextStack.top()->functionMods.last().snips.last().addCode(ch); + m_contextStack.top()->functionMods.last().modifiers |= FunctionModification::CodeInjection; + break; + case StackElement::NamespaceTypeEntry: + case StackElement::ObjectTypeEntry: + case StackElement::ValueTypeEntry: + case StackElement::InterfaceTypeEntry: + m_contextStack.top()->codeSnips.last().addCode(ch); + break; + default: + Q_ASSERT(false); + }; + return true; + } + } + + if (m_current->type & StackElement::DocumentationMask) + m_contextStack.top()->docModifications.last().setCode(ch); + + return true; +} + +bool TypeSystemParser::importFileElement(const QXmlStreamAttributes &atts) +{ + const QString fileName = atts.value(nameAttribute()).toString(); + if (fileName.isEmpty()) { + m_error = QLatin1String("Required attribute 'name' missing for include-file tag."); + return false; + } + + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + file.setFileName(QLatin1String(":/trolltech/generator/") + fileName); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + m_error = QString::fromLatin1("Could not open file: '%1'").arg(QDir::toNativeSeparators(fileName)); + return false; + } + } + + const QStringRef quoteFrom = atts.value(quoteAfterLineAttribute()); + bool foundFromOk = quoteFrom.isEmpty(); + bool from = quoteFrom.isEmpty(); + + const QStringRef quoteTo = atts.value(quoteBeforeLineAttribute()); + bool foundToOk = quoteTo.isEmpty(); + bool to = true; + + QTextStream in(&file); + while (!in.atEnd()) { + QString line = in.readLine(); + if (from && to && line.contains(quoteTo)) { + to = false; + foundToOk = true; + break; + } + if (from && to) + characters(line + QLatin1Char('\n')); + if (!from && line.contains(quoteFrom)) { + from = true; + foundFromOk = true; + } + } + if (!foundFromOk || !foundToOk) { + QString fromError = QStringLiteral("Could not find quote-after-line='%1' in file '%2'.") + .arg(quoteFrom.toString(), fileName); + QString toError = QStringLiteral("Could not find quote-before-line='%1' in file '%2'.") + .arg(quoteTo.toString(), fileName); + + if (!foundToOk) + m_error = toError; + if (!foundFromOk) + m_error = fromError; + if (!foundFromOk && !foundToOk) + m_error = fromError + QLatin1Char(' ') + toError; + return false; + } + + return true; +} + +static bool convertBoolean(QStringView value, const QString &attributeName, bool defaultValue) +{ +#ifdef QTBUG_69389_FIXED + if (value.compare(trueAttributeValue(), Qt::CaseInsensitive) == 0 + || value.compare(yesAttributeValue(), Qt::CaseInsensitive) == 0) { + return true; + } + if (value.compare(falseAttributeValue(), Qt::CaseInsensitive) == 0 + || value.compare(noAttributeValue(), Qt::CaseInsensitive) == 0) { + return false; + } +#else + if (QtPrivate::compareStrings(value, trueAttributeValue(), Qt::CaseInsensitive) == 0 + || QtPrivate::compareStrings(value, yesAttributeValue(), Qt::CaseInsensitive) == 0) { + return true; + } + if (QtPrivate::compareStrings(value, falseAttributeValue(), Qt::CaseInsensitive) == 0 + || QtPrivate::compareStrings(value, noAttributeValue(), Qt::CaseInsensitive) == 0) { + return false; + } +#endif + const QString warn = QStringLiteral("Boolean value '%1' not supported in attribute '%2'. Use 'yes' or 'no'. Defaulting to '%3'.") + .arg(value) + .arg(attributeName, + defaultValue ? yesAttributeValue() : noAttributeValue()); + + qCWarning(lcShiboken).noquote().nospace() << warn; + return defaultValue; +} + +static bool convertRemovalAttribute(QStringView remove, Modification& mod, QString& errorMsg) +{ + if (remove.isEmpty()) + return true; +#ifdef QTBUG_69389_FIXED + if (remove.compare(QStringViewLiteral("all"), Qt::CaseInsensitive) == 0) { +#else + if (QtPrivate::compareStrings(remove, QStringViewLiteral("all"), Qt::CaseInsensitive) == 0) { +#endif + mod.removal = TypeSystem::All; + return true; + } +#ifdef QTBUG_69389_FIXED + if (remove.compare(QStringViewLiteral("target"), Qt::CaseInsensitive) == 0) { +#else + if (QtPrivate::compareStrings(remove, QStringViewLiteral("target"), Qt::CaseInsensitive) == 0) { +#endif + mod.removal = TypeSystem::TargetLangAndNativeCode; + return true; + } + errorMsg = QString::fromLatin1("Bad removal type '%1'").arg(remove); + return false; +} + +static void getNamePrefixRecursive(StackElement* element, QStringList& names) +{ + if (!element->parent || !element->parent->entry) + return; + getNamePrefixRecursive(element->parent, names); + names << element->parent->entry->name(); +} + +static QString getNamePrefix(StackElement* element) +{ + QStringList names; + getNamePrefixRecursive(element, names); + return names.join(QLatin1Char('.')); +} + +// Returns empty string if there's no error. +static QString checkSignatureError(const QString& signature, const QString& tag) +{ + QString funcName = signature.left(signature.indexOf(QLatin1Char('('))).trimmed(); + static const QRegularExpression whiteSpace(QStringLiteral("\\s")); + Q_ASSERT(whiteSpace.isValid()); + if (!funcName.startsWith(QLatin1String("operator ")) && funcName.contains(whiteSpace)) { + return QString::fromLatin1("Error in <%1> tag signature attribute '%2'.\n" + "White spaces aren't allowed in function names, " + "and return types should not be part of the signature.") + .arg(tag, signature); + } + return QString(); +} + +void TypeSystemParser::applyCommonAttributes(TypeEntry *type, QXmlStreamAttributes *attributes) const +{ + type->setCodeGeneration(m_generate); + const int revisionIndex = + indexOfAttribute(*attributes, QStringViewLiteral("revision")); + if (revisionIndex != -1) + type->setRevision(attributes->takeAt(revisionIndex).value().toInt()); +} + +FlagsTypeEntry * + TypeSystemParser::parseFlagsEntry(const QXmlStreamReader &, + EnumTypeEntry *enumEntry, + const QString &name, QString flagName, + const QVersionNumber &since, + QXmlStreamAttributes *attributes) + +{ + FlagsTypeEntry *ftype = new FlagsTypeEntry(QLatin1String("QFlags<") + name + QLatin1Char('>'), since); + ftype->setOriginator(enumEntry); + ftype->setTargetLangPackage(enumEntry->targetLangPackage()); + // Try to get the guess the qualified flag name + const int lastSepPos = name.lastIndexOf(colonColon()); + if (lastSepPos >= 0 && !flagName.contains(colonColon())) + flagName.prepend(name.left(lastSepPos + 2)); + + ftype->setOriginalName(flagName); + applyCommonAttributes(ftype, attributes); + QString n = ftype->originalName(); + + QStringList lst = n.split(colonColon()); + const QString &targetLangQualifier = enumEntry->targetLangQualifier(); + if (QStringList(lst.mid(0, lst.size() - 1)).join(colonColon()) != targetLangQualifier) { + qCWarning(lcShiboken).noquote().nospace() + << QStringLiteral("enum %1 and flags %2 differ in qualifiers") + .arg(targetLangQualifier, lst.constFirst()); + } + + ftype->setFlagsName(lst.constLast()); + enumEntry->setFlags(ftype); + + m_database->addFlagsType(ftype); + m_database->addType(ftype); + + const int revisionIndex = + indexOfAttribute(*attributes, QStringViewLiteral("flags-revision")); + ftype->setRevision(revisionIndex != -1 + ? attributes->takeAt(revisionIndex).value().toInt() + : enumEntry->revision()); + return ftype; +} + +SmartPointerTypeEntry * + TypeSystemParser::parseSmartPointerEntry(const QXmlStreamReader &, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + QString smartPointerType; + QString getter; + QString refCountMethodName; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("type")) { + smartPointerType = attributes->takeAt(i).value().toString(); + } else if (name == QLatin1String("getter")) { + getter = attributes->takeAt(i).value().toString(); + } else if (name == QLatin1String("ref-count-method")) { + refCountMethodName = attributes->takeAt(i).value().toString(); + } + } + + if (smartPointerType.isEmpty()) { + m_error = QLatin1String("No type specified for the smart pointer. Currently supported types: 'shared',"); + return nullptr; + } + if (smartPointerType != QLatin1String("shared")) { + m_error = QLatin1String("Currently only the 'shared' type is supported."); + return nullptr; + } + + if (getter.isEmpty()) { + m_error = QLatin1String("No function getter name specified for getting the raw pointer held by the smart pointer."); + return nullptr; + } + + QString signature = getter + QLatin1String("()"); + signature = TypeDatabase::normalizedSignature(signature); + if (signature.isEmpty()) { + m_error = QLatin1String("No signature for the smart pointer getter found."); + return nullptr; + } + + QString errorString = checkSignatureError(signature, + QLatin1String("smart-pointer-type")); + if (!errorString.isEmpty()) { + m_error = errorString; + return nullptr; + } + + auto *type = new SmartPointerTypeEntry(name, getter, smartPointerType, refCountMethodName, since); + applyCommonAttributes(type, attributes); + return type; +} + +PrimitiveTypeEntry * + TypeSystemParser::parsePrimitiveTypeEntry(const QXmlStreamReader &reader, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + auto *type = new PrimitiveTypeEntry(name, since); + applyCommonAttributes(type, attributes); + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("target-lang-name")) { + type->setTargetLangName(attributes->takeAt(i).value().toString()); + } else if (name == QLatin1String("target-lang-api-name")) { + type->setTargetLangApiName(attributes->takeAt(i).value().toString()); + } else if (name == preferredConversionAttribute()) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + } else if (name == preferredTargetLangTypeAttribute()) { + const bool v = convertBoolean(attributes->takeAt(i).value(), + preferredTargetLangTypeAttribute(), true); + type->setPreferredTargetLangType(v); + } else if (name == QLatin1String("default-constructor")) { + type->setDefaultConstructor(attributes->takeAt(i).value().toString()); + } + } + + if (type->targetLangName().isEmpty()) + type->setTargetLangName(type->name()); + if (type->targetLangApiName().isEmpty()) + type->setTargetLangApiName(type->name()); + type->setTargetLangPackage(m_defaultPackage); + return type; +} + +ContainerTypeEntry * + TypeSystemParser::parseContainerTypeEntry(const QXmlStreamReader &, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + const int typeIndex = indexOfAttribute(*attributes, QStringViewLiteral("type")); + if (typeIndex == -1) { + m_error = QLatin1String("no 'type' attribute specified"); + return nullptr; + } + const QStringRef typeName = attributes->takeAt(typeIndex).value(); + ContainerTypeEntry::Type containerType = containerTypeFromAttribute(typeName); + if (containerType == ContainerTypeEntry::NoContainer) { + m_error = QLatin1String("there is no container of type ") + typeName.toString(); + return nullptr; + } + auto *type = new ContainerTypeEntry(name, containerType, since); + applyCommonAttributes(type, attributes); + return type; +} + +EnumTypeEntry * + TypeSystemParser::parseEnumTypeEntry(const QXmlStreamReader &reader, + const QString &fullName, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + QString scope; + QString name = fullName; + const int sep = fullName.lastIndexOf(colonColon()); + if (sep != -1) { + scope = fullName.left(sep); + name = fullName.right(fullName.size() - sep - 2); + } + auto *entry = new EnumTypeEntry(scope, name, since); + applyCommonAttributes(entry, attributes); + entry->setTargetLangPackage(m_defaultPackage); + + QString flagNames; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("upper-bound")) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + } else if (name == QLatin1String("lower-bound")) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + } else if (name == forceIntegerAttribute()) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + } else if (name == extensibleAttribute()) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + } else if (name == flagsAttribute()) { + flagNames = attributes->takeAt(i).value().toString(); + } + } + + // put in the flags parallel... + if (!flagNames.isEmpty()) { + const QStringList &flagNameList = flagNames.split(QLatin1Char(',')); + for (const QString &flagName : flagNameList) + parseFlagsEntry(reader, entry, fullName, flagName.trimmed(), since, attributes); + } + return entry; +} + +ObjectTypeEntry * + TypeSystemParser::parseInterfaceTypeEntry(const QXmlStreamReader &, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + auto *otype = new ObjectTypeEntry(name, since); + applyCommonAttributes(otype, attributes); + QString targetLangName = name; + bool generate = true; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("target-lang-name")) { + targetLangName = attributes->takeAt(i).value().toString(); + } else if (name == generateAttribute()) { + generate = convertBoolean(attributes->takeAt(i).value(), + generateAttribute(), true); + } + } + + InterfaceTypeEntry *itype = + new InterfaceTypeEntry(InterfaceTypeEntry::interfaceName(targetLangName), since); + + if (generate) + itype->setCodeGeneration(m_generate); + else + itype->setCodeGeneration(TypeEntry::GenerateForSubclass); + + otype->setDesignatedInterface(itype); + itype->setOrigin(otype); + return otype; +} + +NamespaceTypeEntry * + TypeSystemParser::parseNamespaceTypeEntry(const QXmlStreamReader &reader, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + QScopedPointer result(new NamespaceTypeEntry(name, since)); + applyCommonAttributes(result.data(), attributes); + applyComplexTypeAttributes(reader, result.data(), attributes); + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef attributeName = attributes->at(i).qualifiedName(); + if (attributeName == QLatin1String("files")) { + const QString pattern = attributes->takeAt(i).value().toString(); + QRegularExpression re(pattern); + if (!re.isValid()) { + m_error = msgInvalidRegularExpression(pattern, re.errorString()); + return nullptr; + } + result->setFilePattern(re); + } else if (attributeName == QLatin1String("extends")) { + const auto extendsPackageName = attributes->takeAt(i).value(); + auto allEntries = TypeDatabase::instance()->findNamespaceTypes(name); + auto extendsIt = std::find_if(allEntries.cbegin(), allEntries.cend(), + [extendsPackageName] (const NamespaceTypeEntry *e) { + return e->targetLangPackage() == extendsPackageName; + }); + if (extendsIt == allEntries.cend()) { + m_error = msgCannotFindNamespaceToExtend(name, extendsPackageName); + return nullptr; + } + result->setExtends(*extendsIt); + } + } + + if (result->extends() && !result->hasPattern()) { + m_error = msgExtendingNamespaceRequiresPattern(name); + return nullptr; + } + + return result.take(); +} + +ValueTypeEntry * + TypeSystemParser::parseValueTypeEntry(const QXmlStreamReader &, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + auto *typeEntry = new ValueTypeEntry(name, since); + applyCommonAttributes(typeEntry, attributes); + const int defaultCtIndex = + indexOfAttribute(*attributes, QStringViewLiteral("default-constructor")); + if (defaultCtIndex != -1) + typeEntry->setDefaultConstructor(attributes->takeAt(defaultCtIndex).value().toString()); + return typeEntry; +} + +FunctionTypeEntry * + TypeSystemParser::parseFunctionTypeEntry(const QXmlStreamReader &, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + const int signatureIndex = indexOfAttribute(*attributes, signatureAttribute()); + if (signatureIndex == -1) { + m_error = msgMissingAttribute(signatureAttribute()); + return nullptr; + } + const QString signature = + TypeDatabase::normalizedSignature(attributes->takeAt(signatureIndex).value().toString()); + + TypeEntry *existingType = m_database->findType(name); + + if (!existingType) { + auto *result = new FunctionTypeEntry(name, signature, since); + applyCommonAttributes(result, attributes); + return result; + } + + if (existingType->type() != TypeEntry::FunctionType) { + m_error = QStringLiteral("%1 expected to be a function, but isn't! Maybe it was already declared as a class or something else.") + .arg(name); + return nullptr; + } + + auto *result = reinterpret_cast(existingType); + result->addSignature(signature); + return result; +} + +TypedefEntry * + TypeSystemParser::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 TypeSystemParser::applyComplexTypeAttributes(const QXmlStreamReader &reader, + ComplexTypeEntry *ctype, + QXmlStreamAttributes *attributes) const +{ + bool generate = true; + ctype->setCopyable(ComplexTypeEntry::Unknown); + auto exceptionHandling = m_exceptionHandling; + auto allowThread = m_allowThread; + + QString package = m_defaultPackage; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == streamAttribute()) { + ctype->setStream(convertBoolean(attributes->takeAt(i).value(), streamAttribute(), false)); + } else if (name == generateAttribute()) { + generate = convertBoolean(attributes->takeAt(i).value(), generateAttribute(), true); + } else if (name ==packageAttribute()) { + package = attributes->takeAt(i).value().toString(); + } else if (name == defaultSuperclassAttribute()) { + ctype->setDefaultSuperclass(attributes->takeAt(i).value().toString()); + } else if (name == genericClassAttribute()) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + const bool v = convertBoolean(attributes->takeAt(i).value(), genericClassAttribute(), false); + ctype->setGenericClass(v); + } else if (name == QLatin1String("target-lang-name")) { + ctype->setTargetLangName(attributes->takeAt(i).value().toString()); + } else if (name == QLatin1String("polymorphic-base")) { + ctype->setPolymorphicIdValue(attributes->takeAt(i).value().toString()); + } else if (name == QLatin1String("polymorphic-id-expression")) { + ctype->setPolymorphicIdValue(attributes->takeAt(i).value().toString()); + } else if (name == copyableAttribute()) { + const bool v = convertBoolean(attributes->takeAt(i).value(), copyableAttribute(), false); + ctype->setCopyable(v ? ComplexTypeEntry::CopyableSet : ComplexTypeEntry::NonCopyableSet); + } else if (name == exceptionHandlingAttribute()) { + const auto attribute = attributes->takeAt(i); + const auto v = exceptionHandlingFromAttribute(attribute.value()); + if (v != TypeSystem::ExceptionHandling::Unspecified) { + exceptionHandling = v; + } else { + qCWarning(lcShiboken, "%s", + qPrintable(msgInvalidAttributeValue(attribute))); + } + } else if (name == allowThreadAttribute()) { + const auto attribute = attributes->takeAt(i); + const auto v = allowThreadFromAttribute(attribute.value()); + if (v != TypeSystem::AllowThread::Unspecified) { + allowThread = v; + } else { + qCWarning(lcShiboken, "%s", + qPrintable(msgInvalidAttributeValue(attribute))); + } + } else if (name == QLatin1String("held-type")) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + } else if (name == QLatin1String("hash-function")) { + ctype->setHashFunction(attributes->takeAt(i).value().toString()); + } else if (name == forceAbstractAttribute()) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + } else if (name == deprecatedAttribute()) { + if (convertBoolean(attributes->takeAt(i).value(), deprecatedAttribute(), false)) + ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::Deprecated); + } else if (name == deleteInMainThreadAttribute()) { + if (convertBoolean(attributes->takeAt(i).value(), deleteInMainThreadAttribute(), false)) + ctype->setDeleteInMainThread(true); + } else if (name == QLatin1String("target-type")) { + ctype->setTargetType(attributes->takeAt(i).value().toString()); + } + } + + if (exceptionHandling != TypeSystem::ExceptionHandling::Unspecified) + ctype->setExceptionHandling(exceptionHandling); + if (allowThread != TypeSystem::AllowThread::Unspecified) + ctype->setAllowThread(allowThread); + + // The generator code relies on container's package being empty. + if (ctype->type() != TypeEntry::ContainerType) + ctype->setTargetLangPackage(package); + + if (InterfaceTypeEntry *di = ctype->designatedInterface()) + di->setTargetLangPackage(package); + + if (generate) + ctype->setCodeGeneration(m_generate); + else + ctype->setCodeGeneration(TypeEntry::GenerateForSubclass); +} + +bool TypeSystemParser::parseRenameFunction(const QXmlStreamReader &, + QString *name, QXmlStreamAttributes *attributes) +{ + QString signature; + QString rename; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == signatureAttribute()) { + // Do not remove as it is needed for the type entry later on + signature = attributes->at(i).value().toString(); + } else if (name == renameAttribute()) { + rename = attributes->takeAt(i).value().toString(); + } + } + + if (signature.isEmpty()) { + m_error = msgMissingAttribute(signatureAttribute()); + return false; + } + + *name = signature.left(signature.indexOf(QLatin1Char('('))).trimmed(); + + QString errorString = checkSignatureError(signature, QLatin1String("function")); + if (!errorString.isEmpty()) { + m_error = errorString; + return false; + } + + if (!rename.isEmpty()) { + static const QRegularExpression functionNameRegExp(QLatin1String("^[a-zA-Z_][a-zA-Z0-9_]*$")); + Q_ASSERT(functionNameRegExp.isValid()); + if (!functionNameRegExp.match(rename).hasMatch()) { + m_error = QLatin1String("can not rename '") + signature + QLatin1String("', '") + + rename + QLatin1String("' is not a valid function name"); + return false; + } + FunctionModification mod; + if (!mod.setSignature(signature, &m_error)) + return false; + mod.renamedToName = rename; + mod.modifiers |= Modification::Rename; + m_contextStack.top()->functionMods << mod; + } + return true; +} + +bool TypeSystemParser::parseInjectDocumentation(const QXmlStreamReader &, + QXmlStreamAttributes *attributes) +{ + const int validParent = StackElement::TypeEntryMask + | StackElement::ModifyFunction + | StackElement::ModifyField; + if (!m_current->parent || (m_current->parent->type & validParent) == 0) { + m_error = QLatin1String("inject-documentation must be inside modify-function, " + "modify-field or other tags that creates a type"); + return false; + } + + TypeSystem::DocModificationMode mode = TypeSystem::DocModificationReplace; + TypeSystem::Language lang = TypeSystem::NativeCode; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("mode")) { + const QStringRef modeName = attributes->takeAt(i).value(); + mode = docModificationFromAttribute(modeName); + if (mode == TypeSystem::DocModificationInvalid) { + m_error = QLatin1String("Unknown documentation injection mode: ") + modeName; + return false; + } + } else if (name == formatAttribute()) { + const QStringRef format = attributes->takeAt(i).value(); + lang = languageFromAttribute(format); + if (lang != TypeSystem::TargetLangCode && lang != TypeSystem::NativeCode) { + m_error = QStringLiteral("unsupported class attribute: '%1'").arg(format); + return false; + } + } + } + + QString signature = m_current->type & StackElement::TypeEntryMask + ? QString() : m_currentSignature; + DocModification mod(mode, signature); + mod.setFormat(lang); + m_contextStack.top()->docModifications << mod; + return true; +} + +bool TypeSystemParser::parseModifyDocumentation(const QXmlStreamReader &, + QXmlStreamAttributes *attributes) +{ + const int validParent = StackElement::TypeEntryMask + | StackElement::ModifyFunction + | StackElement::ModifyField; + if (!m_current->parent || (m_current->parent->type & validParent) == 0) { + m_error = QLatin1String("modify-documentation must be inside modify-function, " + "modify-field or other tags that creates a type"); + return false; + } + + const int xpathIndex = indexOfAttribute(*attributes, xPathAttribute()); + if (xpathIndex == -1) { + m_error = msgMissingAttribute(xPathAttribute()); + return false; + } + + const QString xpath = attributes->takeAt(xpathIndex).value().toString(); + QString signature = (m_current->type & StackElement::TypeEntryMask) ? QString() : m_currentSignature; + m_contextStack.top()->docModifications + << DocModification(xpath, signature); + return true; +} + +// m_exceptionHandling +TypeSystemTypeEntry *TypeSystemParser::parseRootElement(const QXmlStreamReader &, + const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == packageAttribute()) { + m_defaultPackage = attributes->takeAt(i).value().toString(); + } else if (name == defaultSuperclassAttribute()) { + m_defaultSuperclass = attributes->takeAt(i).value().toString(); + } else if (name == exceptionHandlingAttribute()) { + const auto attribute = attributes->takeAt(i); + const auto v = exceptionHandlingFromAttribute(attribute.value()); + if (v != TypeSystem::ExceptionHandling::Unspecified) { + m_exceptionHandling = v; + } else { + qCWarning(lcShiboken, "%s", + qPrintable(msgInvalidAttributeValue(attribute))); + } + } else if (name == allowThreadAttribute()) { + const auto attribute = attributes->takeAt(i); + const auto v = allowThreadFromAttribute(attribute.value()); + if (v != TypeSystem::AllowThread::Unspecified) { + m_allowThread = v; + } else { + qCWarning(lcShiboken, "%s", + qPrintable(msgInvalidAttributeValue(attribute))); + } + } + } + + auto *moduleEntry = + const_cast(m_database->findTypeSystemType(m_defaultPackage)); + const bool add = moduleEntry == nullptr; + if (add) + moduleEntry = new TypeSystemTypeEntry(m_defaultPackage, since); + moduleEntry->setCodeGeneration(m_generate); + + if ((m_generate == TypeEntry::GenerateForSubclass || + m_generate == TypeEntry::GenerateNothing) && !m_defaultPackage.isEmpty()) + TypeDatabase::instance()->addRequiredTargetImport(m_defaultPackage); + + if (add) + m_database->addTypeSystemType(moduleEntry); + return moduleEntry; +} + +bool TypeSystemParser::loadTypesystem(const QXmlStreamReader &, + QXmlStreamAttributes *attributes) +{ + QString typeSystemName; + bool generateChild = true; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == nameAttribute()) + typeSystemName = attributes->takeAt(i).value().toString(); + else if (name == generateAttribute()) + generateChild = convertBoolean(attributes->takeAt(i).value(), generateAttribute(), true); + } + if (typeSystemName.isEmpty()) { + m_error = QLatin1String("No typesystem name specified"); + return false; + } + const bool result = + m_database->parseFile(typeSystemName, m_currentPath, generateChild + && m_generate == TypeEntry::GenerateAll); + if (!result) + m_error = QStringLiteral("Failed to parse: '%1'").arg(typeSystemName); + return result; +} + +bool TypeSystemParser::parseRejectEnumValue(const QXmlStreamReader &, + QXmlStreamAttributes *attributes) +{ + if (!m_currentEnum) { + m_error = QLatin1String(" node must be used inside a node"); + return false; + } + const int nameIndex = indexOfAttribute(*attributes, nameAttribute()); + if (nameIndex == -1) { + m_error = msgMissingAttribute(nameAttribute()); + return false; + } + m_currentEnum->addEnumValueRejection(attributes->takeAt(nameIndex).value().toString()); + return true; +} + +bool TypeSystemParser::parseReplaceArgumentType(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("Type replacement can only be specified for argument modifications"); + return false; + } + const int modifiedTypeIndex = indexOfAttribute(*attributes, modifiedTypeAttribute()); + if (modifiedTypeIndex == -1) { + m_error = QLatin1String("Type replacement requires 'modified-type' attribute"); + return false; + } + m_contextStack.top()->functionMods.last().argument_mods.last().modified_type = + attributes->takeAt(modifiedTypeIndex).value().toString(); + return true; +} + +bool TypeSystemParser::parseCustomConversion(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyArgument + && topElement.type != StackElement::ValueTypeEntry + && topElement.type != StackElement::PrimitiveTypeEntry + && topElement.type != StackElement::ContainerTypeEntry) { + m_error = QLatin1String("Conversion rules can only be specified for argument modification, " + "value-type, primitive-type or container-type conversion."); + return false; + } + + QString sourceFile; + QString snippetLabel; + TypeSystem::Language lang = TypeSystem::NativeCode; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == classAttribute()) { + const QStringRef languageAttribute = attributes->takeAt(i).value(); + lang = languageFromAttribute(languageAttribute); + if (lang != TypeSystem::TargetLangCode && lang != TypeSystem::NativeCode) { + m_error = QStringLiteral("unsupported class attribute: '%1'").arg(languageAttribute); + return false; + } + } else if (name == QLatin1String("file")) { + sourceFile = attributes->takeAt(i).value().toString(); + } else if (name == snippetAttribute()) { + snippetLabel = attributes->takeAt(i).value().toString(); + } + } + + if (topElement.type == StackElement::ModifyArgument) { + CodeSnip snip; + snip.language = lang; + m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.append(snip); + return true; + } + + if (topElement.entry->hasConversionRule() || topElement.entry->hasCustomConversion()) { + m_error = QLatin1String("Types can have only one conversion rule"); + return false; + } + + // The old conversion rule tag that uses a file containing the conversion + // will be kept temporarily for compatibility reasons. + if (!sourceFile.isEmpty()) { + if (m_generate != TypeEntry::GenerateForSubclass + && m_generate != TypeEntry::GenerateNothing) { + + const char* conversionFlag = NATIVE_CONVERSION_RULE_FLAG; + if (lang == TypeSystem::TargetLangCode) + conversionFlag = TARGET_CONVERSION_RULE_FLAG; + + QFile conversionSource(sourceFile); + if (conversionSource.open(QIODevice::ReadOnly | QIODevice::Text)) { + const QString conversionRule = + extractSnippet(QString::fromUtf8(conversionSource.readAll()), snippetLabel); + topElement.entry->setConversionRule(QLatin1String(conversionFlag) + conversionRule); + } else { + qCWarning(lcShiboken).noquote().nospace() + << "File containing conversion code for " + << topElement.entry->name() << " type does not exist or is not readable: " + << sourceFile; + } + } + } + + auto *customConversion = new CustomConversion(m_current->entry); + customConversionsForReview.append(customConversion); + return true; +} + +bool TypeSystemParser::parseNativeToTarget(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ConversionRule) { + m_error = QLatin1String("Native to Target conversion code can only be specified for custom conversion rules."); + return false; + } + CodeSnip snip; + if (!readFileSnippet(attributes, &snip)) + return false; + m_contextStack.top()->codeSnips.append(snip); + return true; +} + +bool TypeSystemParser::parseAddConversion(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::TargetToNative) { + m_error = QLatin1String("Target to Native conversions can only be added inside 'target-to-native' tags."); + return false; + } + QString sourceTypeName; + QString typeCheck; + CodeSnip snip; + if (!readFileSnippet(attributes, &snip)) + return false; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("type")) + sourceTypeName = attributes->takeAt(i).value().toString(); + else if (name == QLatin1String("check")) + typeCheck = attributes->takeAt(i).value().toString(); + } + if (sourceTypeName.isEmpty()) { + m_error = QLatin1String("Target to Native conversions must specify the input type with the 'type' attribute."); + return false; + } + m_current->entry->customConversion()->addTargetToNativeConversion(sourceTypeName, typeCheck); + m_contextStack.top()->codeSnips.append(snip); + return true; +} + +static bool parseIndex(const QString &index, int *result, QString *errorMessage) +{ + bool ok = false; + *result = index.toInt(&ok); + if (!ok) + *errorMessage = QStringLiteral("Cannot convert '%1' to integer").arg(index); + return ok; +} + +static bool parseArgumentIndex(const QString &index, int *result, QString *errorMessage) +{ + if (index == QLatin1String("return")) { + *result = 0; + return true; + } + if (index == QLatin1String("this")) { + *result = -1; + return true; + } + return parseIndex(index, result, errorMessage); +} + +bool TypeSystemParser::parseModifyArgument(const QXmlStreamReader &, + const StackElement &topElement, QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyFunction + && topElement.type != StackElement::AddFunction) { + m_error = QString::fromLatin1("argument modification requires function" + " modification as parent, was %1") + .arg(topElement.type, 0, 16); + return false; + } + + QString index; + QString replaceValue; + bool resetAfterUse = false; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == indexAttribute()) { + index = attributes->takeAt(i).value().toString(); + } else if (name == QLatin1String("replace-value")) { + replaceValue = attributes->takeAt(i).value().toString(); + } else if (name == invalidateAfterUseAttribute()) { + resetAfterUse = convertBoolean(attributes->takeAt(i).value(), + invalidateAfterUseAttribute(), false); + } + } + + if (index.isEmpty()) { + m_error = msgMissingAttribute(indexAttribute()); + return false; + } + + int idx; + if (!parseArgumentIndex(index, &idx, &m_error)) + return false; + + if (!replaceValue.isEmpty() && idx) { + m_error = QLatin1String("replace-value is only supported for return values (index=0)."); + return false; + } + + ArgumentModification argumentModification = ArgumentModification(idx); + argumentModification.replace_value = replaceValue; + argumentModification.resetAfterUse = resetAfterUse; + m_contextStack.top()->functionMods.last().argument_mods.append(argumentModification); + return true; +} + +bool TypeSystemParser::parseNoNullPointer(const QXmlStreamReader &reader, + const StackElement &topElement, QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("no-null-pointer requires argument modification as parent"); + return false; + } + + ArgumentModification &lastArgMod = m_contextStack.top()->functionMods.last().argument_mods.last(); + lastArgMod.noNullPointers = true; + + const int defaultValueIndex = + indexOfAttribute(*attributes, QStringViewLiteral("default-value")); + if (defaultValueIndex != -1) { + const QXmlStreamAttribute attribute = attributes->takeAt(defaultValueIndex); + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, attribute))); + } + return true; +} + +bool TypeSystemParser::parseDefineOwnership(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("define-ownership requires argument modification as parent"); + return false; + } + + TypeSystem::Language lang = TypeSystem::TargetLangCode; + QString ownership; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == classAttribute()) { + const QStringRef className = attributes->takeAt(i).value(); + lang = languageFromAttribute(className); + if (lang != TypeSystem::TargetLangCode && lang != TypeSystem::NativeCode) { + m_error = QStringLiteral("unsupported class attribute: '%1'").arg(className); + return false; + } + } else if (name == ownershipAttribute()) { + ownership = attributes->takeAt(i).value().toString(); + } + } + const TypeSystem::Ownership owner = ownershipFromFromAttribute(ownership); + if (owner == TypeSystem::InvalidOwnership) { + m_error = QStringLiteral("unsupported owner attribute: '%1'").arg(ownership); + return false; + } + m_contextStack.top()->functionMods.last().argument_mods.last().ownerships[lang] = owner; + return true; +} + +bool TypeSystemParser::parseArgumentMap(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (!(topElement.type & StackElement::CodeSnipMask)) { + m_error = QLatin1String("Argument maps requires code injection as parent"); + return false; + } + + int pos = 1; + QString metaName; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == indexAttribute()) { + if (!parseIndex(attributes->takeAt(i).value().toString(), &pos, &m_error)) + return false; + if (pos <= 0) { + m_error = QStringLiteral("Argument position %1 must be a positive number").arg(pos); + return false; + } + } else if (name == QLatin1String("meta-name")) { + metaName = attributes->takeAt(i).value().toString(); + } + } + + if (metaName.isEmpty()) + qCWarning(lcShiboken) << "Empty meta name in argument map"; + + if (topElement.type == StackElement::InjectCodeInFunction) { + m_contextStack.top()->functionMods.last().snips.last().argumentMap[pos] = metaName; + } else { + qCWarning(lcShiboken) << "Argument maps are only useful for injection of code " + "into functions."; + } + return true; +} + +bool TypeSystemParser::parseRemoval(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyFunction) { + m_error = QLatin1String("Function modification parent required"); + return false; + } + + TypeSystem::Language lang = TypeSystem::All; + const int classIndex = indexOfAttribute(*attributes, classAttribute()); + if (classIndex != -1) { + const QStringRef value = attributes->takeAt(classIndex).value(); + lang = languageFromAttribute(value); + if (lang == TypeSystem::TargetLangCode) // "target" means TargetLangAndNativeCode here + lang = TypeSystem::TargetLangAndNativeCode; + if (lang != TypeSystem::TargetLangAndNativeCode && lang != TypeSystem::All) { + m_error = QStringLiteral("unsupported class attribute: '%1'").arg(value); + return false; + } + } + m_contextStack.top()->functionMods.last().removal = lang; + return true; +} + +bool TypeSystemParser::parseRename(const QXmlStreamReader &reader, + StackElement::ElementType type, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyField + && topElement.type != StackElement::ModifyFunction + && topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("Function, field or argument modification parent required"); + return false; + } + + Modification *mod = nullptr; + if (topElement.type == StackElement::ModifyFunction) + mod = &m_contextStack.top()->functionMods.last(); + else if (topElement.type == StackElement::ModifyField) + mod = &m_contextStack.top()->fieldMods.last(); + + Modification::Modifiers modifierFlag = Modification::Rename; + if (type == StackElement::Rename) { + const int toIndex = indexOfAttribute(*attributes, toAttribute()); + if (toIndex == -1) { + m_error = msgMissingAttribute(toAttribute()); + return false; + } + const QString renamed_to = attributes->takeAt(toIndex).value().toString(); + if (topElement.type == StackElement::ModifyFunction) + mod->setRenamedTo(renamed_to); + else if (topElement.type == StackElement::ModifyField) + mod->setRenamedTo(renamed_to); + else + m_contextStack.top()->functionMods.last().argument_mods.last().renamed_to = renamed_to; + } else { + const int modifierIndex = indexOfAttribute(*attributes, modifierAttribute()); + if (modifierIndex == -1) { + m_error = msgMissingAttribute(modifierAttribute()); + return false; + } + const QStringRef modifier = attributes->takeAt(modifierIndex).value(); + modifierFlag = modifierFromAttribute(modifier); + if (modifierFlag == Modification::InvalidModifier) { + m_error = QStringLiteral("Unknown access modifier: '%1'").arg(modifier); + return false; + } + if (modifierFlag == Modification::Friendly) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeValueWarning(reader, modifierAttribute(), modifier))); + } + } + + if (mod) + mod->modifiers |= modifierFlag; + return true; +} + +bool TypeSystemParser::parseModifyField(const QXmlStreamReader &reader, + QXmlStreamAttributes *attributes) +{ + FieldModification fm; + fm.modifiers = FieldModification::Readable | FieldModification::Writable; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == nameAttribute()) { + fm.name = attributes->takeAt(i).value().toString(); + } else if (name == removeAttribute()) { + if (!convertRemovalAttribute(attributes->takeAt(i).value(), fm, m_error)) + return false; + } else if (name == readAttribute()) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + if (!convertBoolean(attributes->takeAt(i).value(), readAttribute(), true)) + fm.modifiers &= ~FieldModification::Readable; + } else if (name == writeAttribute()) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + if (!convertBoolean(attributes->takeAt(i).value(), writeAttribute(), true)) + fm.modifiers &= ~FieldModification::Writable; + } + } + if (fm.name.isEmpty()) { + m_error = msgMissingAttribute(nameAttribute()); + return false; + } + m_contextStack.top()->fieldMods << fm; + return true; +} + +bool TypeSystemParser::parseAddFunction(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (!(topElement.type & (StackElement::ComplexTypeEntryMask | StackElement::Root))) { + m_error = QString::fromLatin1("Add function requires a complex type or a root tag as parent" + ", was=%1").arg(topElement.type, 0, 16); + return false; + } + QString originalSignature; + QString returnType = QLatin1String("void"); + bool staticFunction = false; + QString access; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("signature")) { + originalSignature = attributes->takeAt(i).value().toString(); + } else if (name == QLatin1String("return-type")) { + returnType = attributes->takeAt(i).value().toString(); + } else if (name == staticAttribute()) { + staticFunction = convertBoolean(attributes->takeAt(i).value(), + staticAttribute(), false); + } else if (name == accessAttribute()) { + access = attributes->takeAt(i).value().toString(); + } + } + + QString signature = TypeDatabase::normalizedSignature(originalSignature); + if (signature.isEmpty()) { + m_error = QLatin1String("No signature for the added function"); + return false; + } + + QString errorString = checkSignatureError(signature, QLatin1String("add-function")); + if (!errorString.isEmpty()) { + m_error = errorString; + return false; + } + + AddedFunctionPtr func(new AddedFunction(signature, returnType)); + func->setStatic(staticFunction); + if (!signature.contains(QLatin1Char('('))) + signature += QLatin1String("()"); + m_currentSignature = signature; + + if (!access.isEmpty()) { + const AddedFunction::Access a = addedFunctionAccessFromAttribute(access); + if (a == AddedFunction::InvalidAccess) { + m_error = QString::fromLatin1("Bad access type '%1'").arg(access); + return false; + } + func->setAccess(a); + } + + m_contextStack.top()->addedFunctions << func; + m_contextStack.top()->addedFunctionModificationIndex = + m_contextStack.top()->functionMods.size(); + + FunctionModification mod; + if (!mod.setSignature(m_currentSignature, &m_error)) + return false; + mod.setOriginalSignature(originalSignature); + m_contextStack.top()->functionMods << mod; + return true; +} + +bool TypeSystemParser::parseModifyFunction(const QXmlStreamReader &reader, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (!(topElement.type & StackElement::ComplexTypeEntryMask)) { + m_error = QString::fromLatin1("Modify function requires complex type as parent" + ", was=%1").arg(topElement.type, 0, 16); + return false; + } + + QString originalSignature; + QString access; + QString removal; + QString rename; + QString association; + bool deprecated = false; + bool isThread = false; + TypeSystem::ExceptionHandling exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; + TypeSystem::AllowThread allowThread = TypeSystem::AllowThread::Unspecified; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("signature")) { + originalSignature = attributes->takeAt(i).value().toString(); + } else if (name == accessAttribute()) { + access = attributes->takeAt(i).value().toString(); + } else if (name == renameAttribute()) { + rename = attributes->takeAt(i).value().toString(); + } else if (name == QLatin1String("associated-to")) { + association = attributes->takeAt(i).value().toString(); + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + } else if (name == removeAttribute()) { + removal = attributes->takeAt(i).value().toString(); + } else if (name == deprecatedAttribute()) { + deprecated = convertBoolean(attributes->takeAt(i).value(), + deprecatedAttribute(), false); + } else if (name == threadAttribute()) { + isThread = convertBoolean(attributes->takeAt(i).value(), + threadAttribute(), false); + } else if (name == allowThreadAttribute()) { + const QXmlStreamAttribute attribute = attributes->takeAt(i); + allowThread = allowThreadFromAttribute(attribute.value()); + if (allowThread == TypeSystem::AllowThread::Unspecified) { + m_error = msgInvalidAttributeValue(attribute); + return false; + } + } else if (name == exceptionHandlingAttribute()) { + const auto attribute = attributes->takeAt(i); + exceptionHandling = exceptionHandlingFromAttribute(attribute.value()); + if (exceptionHandling == TypeSystem::ExceptionHandling::Unspecified) { + qCWarning(lcShiboken, "%s", + qPrintable(msgInvalidAttributeValue(attribute))); + } + } else if (name == virtualSlotAttribute()) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + } + } + + const QString signature = TypeDatabase::normalizedSignature(originalSignature); + if (signature.isEmpty()) { + m_error = QLatin1String("No signature for modified function"); + return false; + } + + QString errorString = checkSignatureError(signature, QLatin1String("modify-function")); + if (!errorString.isEmpty()) { + m_error = errorString; + return false; + } + + FunctionModification mod; + if (!mod.setSignature(signature, &m_error)) + return false; + mod.setOriginalSignature(originalSignature); + mod.setExceptionHandling(exceptionHandling); + m_currentSignature = signature; + + if (!access.isEmpty()) { + const Modification::Modifiers m = modifierFromAttribute(access); + if ((m & (Modification::AccessModifierMask | Modification::FinalMask)) == 0) { + m_error = QString::fromLatin1("Bad access type '%1'").arg(access); + return false; + } + if (m == Modification::Final || m == Modification::NonFinal) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeValueWarning(reader, + accessAttribute(), access))); + } + mod.modifiers |= m; + } + + if (deprecated) + mod.modifiers |= Modification::Deprecated; + + if (!removal.isEmpty() && !convertRemovalAttribute(removal, mod, m_error)) + return false; + + if (!rename.isEmpty()) { + mod.renamedToName = rename; + mod.modifiers |= Modification::Rename; + } + + if (!association.isEmpty()) + mod.association = association; + + mod.setIsThread(isThread); + if (allowThread != TypeSystem::AllowThread::Unspecified) + mod.setAllowThread(allowThread); + + m_contextStack.top()->functionMods << mod; + return true; +} + +bool TypeSystemParser::parseReplaceDefaultExpression(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (!(topElement.type & StackElement::ModifyArgument)) { + m_error = QLatin1String("Replace default expression only allowed as child of argument modification"); + return false; + } + const int withIndex = indexOfAttribute(*attributes, QStringViewLiteral("with")); + if (withIndex == -1 || attributes->at(withIndex).value().isEmpty()) { + m_error = QLatin1String("Default expression replaced with empty string. Use remove-default-expression instead."); + return false; + } + + m_contextStack.top()->functionMods.last().argument_mods.last().replacedDefaultExpression = + attributes->takeAt(withIndex).value().toString(); + return true; +} + +CustomFunction * + TypeSystemParser::parseCustomMetaConstructor(const QXmlStreamReader &, + StackElement::ElementType type, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + QString functionName = topElement.entry->name().toLower() + + (type == StackElement::CustomMetaConstructor + ? QLatin1String("_create") : QLatin1String("_delete")); + QString paramName = QLatin1String("copy"); + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == nameAttribute()) + functionName = attributes->takeAt(i).value().toString(); + else if (name == QLatin1String("param-name")) + paramName = attributes->takeAt(i).value().toString(); + } + auto *func = new CustomFunction(functionName); + func->paramName = paramName; + return func; +} + +bool TypeSystemParser::parseReferenceCount(const QXmlStreamReader &reader, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("reference-count must be child of modify-argument"); + return false; + } + + ReferenceCount rc; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == actionAttribute()) { + const QXmlStreamAttribute attribute = attributes->takeAt(i); + rc.action = referenceCountFromAttribute(attribute.value()); + switch (rc.action) { + case ReferenceCount::Invalid: + m_error = QLatin1String("unrecognized value '") + attribute.value() + + QLatin1String("' for action attribute."); + return false; + case ReferenceCount::AddAll: + case ReferenceCount::Ignore: + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeValueWarning(reader, attribute))); + break; + default: + break; + } + } else if (name == QLatin1String("variable-name")) { + rc.varName = attributes->takeAt(i).value().toString(); + } + } + + m_contextStack.top()->functionMods.last().argument_mods.last().referenceCounts.append(rc); + return true; +} + +bool TypeSystemParser::parseParentOwner(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("parent-policy must be child of modify-argument"); + return false; + } + ArgumentOwner ao; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == indexAttribute()) { + const QString index = attributes->takeAt(i).value().toString(); + if (!parseArgumentIndex(index, &ao.index, &m_error)) + return false; + } else if (name == actionAttribute()) { + const QStringRef action = attributes->takeAt(i).value(); + ao.action = argumentOwnerActionFromAttribute(action); + if (ao.action == ArgumentOwner::Invalid) { + m_error = QLatin1String("Invalid parent actionr '") + action + QLatin1String("'."); + return false; + } + } + } + m_contextStack.top()->functionMods.last().argument_mods.last().owner = ao; + return true; +} + +bool TypeSystemParser::readFileSnippet(QXmlStreamAttributes *attributes, CodeSnip *snip) +{ + QString fileName; + QString snippetLabel; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("file")) { + fileName = attributes->takeAt(i).value().toString(); + } else if (name == snippetAttribute()) { + snippetLabel = attributes->takeAt(i).value().toString(); + } + } + if (fileName.isEmpty()) + return true; + const QString resolved = m_database->modifiedTypesystemFilepath(fileName, m_currentPath); + if (!QFile::exists(resolved)) { + m_error = QLatin1String("File for inject code not exist: ") + + QDir::toNativeSeparators(fileName); + return false; + } + QFile codeFile(resolved); + if (!codeFile.open(QIODevice::Text | QIODevice::ReadOnly)) { + m_error = msgCannotOpenForReading(codeFile); + return false; + } + QString source = fileName; + if (!snippetLabel.isEmpty()) + source += QLatin1String(" (") + snippetLabel + QLatin1Char(')'); + QString content; + QTextStream str(&content); + str << "// ========================================================================\n" + "// START of custom code block [file: " + << source << "]\n" + << extractSnippet(QString::fromUtf8(codeFile.readAll()), snippetLabel) + << "\n// END of custom code block [file: " << source + << "]\n// ========================================================================\n"; + snip->addCode(content); + return true; +} + +bool TypeSystemParser::parseInjectCode(const QXmlStreamReader &, + const StackElement &topElement, + StackElement* element, QXmlStreamAttributes *attributes) +{ + if (!(topElement.type & StackElement::ComplexTypeEntryMask) + && (topElement.type != StackElement::AddFunction) + && (topElement.type != StackElement::ModifyFunction) + && (topElement.type != StackElement::Root)) { + m_error = QLatin1String("wrong parent type for code injection"); + return false; + } + + TypeSystem::CodeSnipPosition position = TypeSystem::CodeSnipPositionBeginning; + TypeSystem::Language lang = TypeSystem::TargetLangCode; + CodeSnip snip; + if (!readFileSnippet(attributes, &snip)) + return false; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == classAttribute()) { + const QStringRef className = attributes->takeAt(i).value(); + lang = languageFromAttribute(className); + if (lang == TypeSystem::NoLanguage) { + m_error = QStringLiteral("Invalid class specifier: '%1'").arg(className); + return false; + } + } else if (name == positionAttribute()) { + const QStringRef value = attributes->takeAt(i).value(); + position = codeSnipPositionFromAttribute(value); + if (position == TypeSystem::CodeSnipPositionInvalid) { + m_error = QStringLiteral("Invalid position: '%1'").arg(value); + return false; + } + } + } + + snip.position = position; + snip.language = lang; + + if (snip.language == TypeSystem::Interface + && topElement.type != StackElement::InterfaceTypeEntry) { + m_error = QLatin1String("Interface code injections must be direct child of an interface type entry"); + return false; + } + + if (topElement.type == StackElement::ModifyFunction + || topElement.type == StackElement::AddFunction) { + if (snip.language == TypeSystem::ShellDeclaration) { + m_error = QLatin1String("no function implementation in shell declaration in which to inject code"); + return false; + } + + FunctionModification &mod = m_contextStack.top()->functionMods.last(); + mod.snips << snip; + if (!snip.code().isEmpty()) + mod.modifiers |= FunctionModification::CodeInjection; + element->type = StackElement::InjectCodeInFunction; + } else if (topElement.type == StackElement::Root) { + element->entry->addCodeSnip(snip); + } else if (topElement.type != StackElement::Root) { + m_contextStack.top()->codeSnips << snip; + } + return true; +} + +bool TypeSystemParser::parseInclude(const QXmlStreamReader &, + const StackElement &topElement, + TypeEntry *entry, QXmlStreamAttributes *attributes) +{ + QString fileName; + QString location; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("file-name")) + fileName = attributes->takeAt(i).value().toString(); + else if (name == locationAttribute()) + location = attributes->takeAt(i).value().toString(); + } + const Include::IncludeType loc = locationFromAttribute(location); + if (loc == Include::InvalidInclude) { + m_error = QStringLiteral("Location not recognized: '%1'").arg(location); + return false; + } + + Include inc(loc, fileName); + if (topElement.type + & (StackElement::ComplexTypeEntryMask | StackElement::PrimitiveTypeEntry)) { + entry->setInclude(inc); + } else if (topElement.type == StackElement::ExtraIncludes) { + entry->addExtraInclude(inc); + } else { + m_error = QLatin1String("Only supported parent tags are primitive-type, complex types or extra-includes"); + return false; + } + if (InterfaceTypeEntry *di = entry->designatedInterface()) { + di->setInclude(entry->include()); + di->setExtraIncludes(entry->extraIncludes()); + } + return true; +} + +TemplateInstance * + TypeSystemParser::parseTemplateInstanceEnum(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (!(topElement.type & StackElement::CodeSnipMask) && + (topElement.type != StackElement::Template) && + (topElement.type != StackElement::CustomMetaConstructor) && + (topElement.type != StackElement::CustomMetaDestructor) && + (topElement.type != StackElement::NativeToTarget) && + (topElement.type != StackElement::AddConversion) && + (topElement.type != StackElement::ConversionRule)) { + m_error = QLatin1String("Can only insert templates into code snippets, templates, custom-constructors, "\ + "custom-destructors, conversion-rule, native-to-target or add-conversion tags."); + return nullptr; + } + const int nameIndex = indexOfAttribute(*attributes, nameAttribute()); + if (nameIndex == -1) { + m_error = msgMissingAttribute(nameAttribute()); + return nullptr; + } + return new TemplateInstance(attributes->takeAt(nameIndex).value().toString()); +} + +bool TypeSystemParser::parseReplace(const QXmlStreamReader &, + const StackElement &topElement, + StackElement *element, QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::TemplateInstanceEnum) { + m_error = QLatin1String("Can only insert replace rules into insert-template."); + return false; + } + QString from; + QString to; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("from")) + from = attributes->takeAt(i).value().toString(); + else if (name == toAttribute()) + to = attributes->takeAt(i).value().toString(); + } + element->parent->value.templateInstance->addReplaceRule(from, to); + return true; +} + +bool TypeSystemParser::startElement(const QXmlStreamReader &reader) +{ + if (m_ignoreDepth) { + ++m_ignoreDepth; + return true; + } + + const QStringRef tagName = reader.name(); + QXmlStreamAttributes attributes = reader.attributes(); + + QVersionNumber since(0, 0); + int index = indexOfAttribute(attributes, sinceAttribute()); + if (index != -1) { + const QStringRef sinceSpec = attributes.takeAt(index).value(); + since = QVersionNumber::fromString(sinceSpec.toString()); + if (since.isNull()) { + m_error = msgInvalidVersion(sinceSpec, m_defaultPackage); + return false; + } + } + + if (!m_defaultPackage.isEmpty() && since > QVersionNumber(0, 0)) { + TypeDatabase* td = TypeDatabase::instance(); + if (!td->checkApiVersion(m_defaultPackage, since)) { + ++m_ignoreDepth; + return true; + } + } + + if (tagName.compare(QLatin1String("import-file"), Qt::CaseInsensitive) == 0) + return importFileElement(attributes); + + const StackElement::ElementType elementType = elementFromTag(tagName); + if (elementType == StackElement::None) { + m_error = QStringLiteral("Unknown tag name: '%1'").arg(tagName); + return false; + } + + if (m_currentDroppedEntry) { + ++m_currentDroppedEntryDepth; + return true; + } + + auto *element = new StackElement(m_current); + element->type = elementType; + + if (element->type == StackElement::Root && m_generate == TypeEntry::GenerateAll) + customConversionsForReview.clear(); + + if (element->type == StackElement::CustomMetaConstructor + || element->type == StackElement::CustomMetaDestructor) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedElementWarning(reader, tagName))); + } + + 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) { + QString name; + if (element->type != StackElement::FunctionTypeEntry) { + const int nameIndex = indexOfAttribute(attributes, nameAttribute()); + if (nameIndex != -1) { + name = attributes.takeAt(nameIndex).value().toString(); + } else if (element->type != StackElement::EnumTypeEntry) { // anonymous enum? + m_error = msgMissingAttribute(nameAttribute()); + return false; + } + } + + if (m_database->hasDroppedTypeEntries()) { + QString identifier = getNamePrefix(element) + QLatin1Char('.'); + identifier += element->type == StackElement::FunctionTypeEntry + ? attributes.value(signatureAttribute()).toString() + : name; + if (m_database->shouldDropTypeEntry(identifier)) { + m_currentDroppedEntry = element; + m_currentDroppedEntryDepth = 1; + if (ReportHandler::isDebug(ReportHandler::SparseDebug)) { + qCDebug(lcShiboken) + << QStringLiteral("Type system entry '%1' was intentionally dropped from generation.").arg(identifier); + } + return true; + } + } + + // The top level tag 'function' has only the 'signature' tag + // and we should extract the 'name' value from it. + if (element->type == StackElement::FunctionTypeEntry + && !parseRenameFunction(reader, &name, &attributes)) { + return false; + } + + // We need to be able to have duplicate primitive type entries, + // or it's not possible to cover all primitive target language + // types (which we need to do in order to support fake meta objects) + if (element->type != StackElement::PrimitiveTypeEntry + && element->type != StackElement::FunctionTypeEntry) { + TypeEntry *tmp = m_database->findType(name); + if (tmp && !tmp->isNamespace()) + qCWarning(lcShiboken).noquote().nospace() + << QStringLiteral("Duplicate type entry: '%1'").arg(name); + } + + if (element->type == StackElement::EnumTypeEntry) { + const int enumIdentifiedByIndex = indexOfAttribute(attributes, enumIdentifiedByValueAttribute()); + const QString identifiedByValue = enumIdentifiedByIndex != -1 + ? attributes.takeAt(enumIdentifiedByIndex).value().toString() : QString(); + if (name.isEmpty()) { + name = identifiedByValue; + } else if (!identifiedByValue.isEmpty()) { + m_error = QLatin1String("can't specify both 'name' and 'identified-by-value' attributes"); + return false; + } + } + + // Fix type entry name using nesting information. + if (element->type & StackElement::TypeEntryMask + && element->parent && element->parent->type != StackElement::Root) { + name = element->parent->entry->name() + colonColon() + name; + } + + + if (name.isEmpty()) { + m_error = QLatin1String("no 'name' attribute specified"); + return false; + } + + switch (element->type) { + case StackElement::CustomTypeEntry: + element->entry = new TypeEntry(name, TypeEntry::CustomType, since); + break; + case StackElement::PrimitiveTypeEntry: + element->entry = parsePrimitiveTypeEntry(reader, name, since, &attributes); + if (Q_UNLIKELY(!element->entry)) + return false; + break; + case StackElement::ContainerTypeEntry: + if (ContainerTypeEntry *ce = parseContainerTypeEntry(reader, name, since, &attributes)) { + applyComplexTypeAttributes(reader, ce, &attributes); + element->entry = ce; + } else { + return false; + } + break; + + case StackElement::SmartPointerTypeEntry: + if (SmartPointerTypeEntry *se = parseSmartPointerEntry(reader, name, since, &attributes)) { + applyComplexTypeAttributes(reader, se, &attributes); + element->entry = se; + } else { + return false; + } + break; + case StackElement::EnumTypeEntry: + m_currentEnum = parseEnumTypeEntry(reader, name, since, &attributes); + if (Q_UNLIKELY(!m_currentEnum)) + return false; + element->entry = m_currentEnum; + break; + + case StackElement::InterfaceTypeEntry: + if (ObjectTypeEntry *oe = parseInterfaceTypeEntry(reader, name, since, &attributes)) { + applyComplexTypeAttributes(reader, oe, &attributes); + element->entry = oe; + } else { + return false; + } + break; + case StackElement::ValueTypeEntry: + if (ValueTypeEntry *ve = parseValueTypeEntry(reader, name, since, &attributes)) { + applyComplexTypeAttributes(reader, ve, &attributes); + element->entry = ve; + } else { + return false; + } + break; + case StackElement::NamespaceTypeEntry: + if (auto entry = parseNamespaceTypeEntry(reader, name, since, &attributes)) + element->entry = entry; + else + return false; + break; + case StackElement::ObjectTypeEntry: + element->entry = new ObjectTypeEntry(name, since); + applyCommonAttributes(element->entry, &attributes); + applyComplexTypeAttributes(reader, static_cast(element->entry), &attributes); + break; + case StackElement::FunctionTypeEntry: + element->entry = parseFunctionTypeEntry(reader, name, since, &attributes); + 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) { + if (!m_database->addType(element->entry, &m_error)) + return false; + } else { + qCWarning(lcShiboken).noquote().nospace() + << QStringLiteral("Type: %1 was rejected by typesystem").arg(name); + } + + } else if (element->type == StackElement::InjectDocumentation) { + if (!parseInjectDocumentation(reader, &attributes)) + return false; + } else if (element->type == StackElement::ModifyDocumentation) { + if (!parseModifyDocumentation(reader, &attributes)) + return false; + } else if (element->type != StackElement::None) { + bool topLevel = element->type == StackElement::Root + || element->type == StackElement::SuppressedWarning + || element->type == StackElement::Rejection + || element->type == StackElement::LoadTypesystem + || element->type == StackElement::InjectCode + || element->type == StackElement::ExtraIncludes + || element->type == StackElement::ConversionRule + || element->type == StackElement::AddFunction + || element->type == StackElement::Template; + + if (!topLevel && m_current->type == StackElement::Root) { + m_error = QStringLiteral("Tag requires parent: '%1'").arg(tagName); + return false; + } + + StackElement topElement = !m_current ? StackElement(nullptr) : *m_current; + element->entry = topElement.entry; + + switch (element->type) { + case StackElement::Root: + element->entry = parseRootElement(reader, since, &attributes); + element->type = StackElement::Root; + break; + case StackElement::LoadTypesystem: + if (!loadTypesystem(reader, &attributes)) + return false; + break; + case StackElement::RejectEnumValue: + if (!parseRejectEnumValue(reader, &attributes)) + return false; + break; + case StackElement::ReplaceType: + if (!parseReplaceArgumentType(reader, topElement, &attributes)) + return false; + break; + case StackElement::ConversionRule: + if (!TypeSystemParser::parseCustomConversion(reader, topElement, &attributes)) + return false; + break; + case StackElement::NativeToTarget: + if (!parseNativeToTarget(reader, topElement, &attributes)) + return false; + break; + case StackElement::TargetToNative: { + if (topElement.type != StackElement::ConversionRule) { + m_error = QLatin1String("Target to Native conversions can only be specified for custom conversion rules."); + return false; + } + const int replaceIndex = indexOfAttribute(attributes, replaceAttribute()); + const bool replace = replaceIndex == -1 + || convertBoolean(attributes.takeAt(replaceIndex).value(), + replaceAttribute(), true); + m_current->entry->customConversion()->setReplaceOriginalTargetToNativeConversions(replace); + } + break; + case StackElement::AddConversion: + if (!parseAddConversion(reader, topElement, &attributes)) + return false; + break; + case StackElement::ModifyArgument: + if (!parseModifyArgument(reader, topElement, &attributes)) + return false; + break; + case StackElement::NoNullPointers: + if (!parseNoNullPointer(reader, topElement, &attributes)) + return false; + break; + case StackElement::DefineOwnership: + if (!parseDefineOwnership(reader, topElement, &attributes)) + return false; + break; + case StackElement::SuppressedWarning: { + const int textIndex = indexOfAttribute(attributes, textAttribute()); + if (textIndex == -1) { + qCWarning(lcShiboken) << "Suppressed warning with no text specified"; + } else { + const QString suppressedWarning = + attributes.takeAt(textIndex).value().toString(); + if (!m_database->addSuppressedWarning(suppressedWarning, &m_error)) + return false; + } + } + break; + case StackElement::ArgumentMap: + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedElementWarning(reader, tagName))); + if (!parseArgumentMap(reader, topElement, &attributes)) + return false; + break; + case StackElement::Removal: + if (!parseRemoval(reader, topElement, &attributes)) + return false; + break; + case StackElement::Rename: + case StackElement::Access: + if (!parseRename(reader, element->type, topElement, &attributes)) + return false; + break; + case StackElement::RemoveArgument: + if (topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("Removing argument requires argument modification as parent"); + return false; + } + + m_contextStack.top()->functionMods.last().argument_mods.last().removed = true; + break; + + case StackElement::ModifyField: + if (!parseModifyField(reader, &attributes)) + return false; + break; + case StackElement::AddFunction: + if (!parseAddFunction(reader, topElement, &attributes)) + return false; + break; + case StackElement::ModifyFunction: + if (!parseModifyFunction(reader, topElement, &attributes)) + return false; + break; + case StackElement::ReplaceDefaultExpression: + if (!parseReplaceDefaultExpression(reader, topElement, &attributes)) + return false; + break; + case StackElement::RemoveDefaultExpression: + m_contextStack.top()->functionMods.last().argument_mods.last().removedDefaultExpression = true; + break; + case StackElement::CustomMetaConstructor: + case StackElement::CustomMetaDestructor: + element->value.customFunction = + parseCustomMetaConstructor(reader, element->type, topElement, &attributes); + break; + case StackElement::ReferenceCount: + if (!parseReferenceCount(reader, topElement, &attributes)) + return false; + break; + case StackElement::ParentOwner: + if (!parseParentOwner(reader, topElement, &attributes)) + return false; + break; + case StackElement::Array: + if (topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("array must be child of modify-argument"); + return false; + } + m_contextStack.top()->functionMods.last().argument_mods.last().array = true; + break; + case StackElement::InjectCode: + if (!parseInjectCode(reader, topElement, element, &attributes)) + return false; + break; + case StackElement::Include: + if (!parseInclude(reader, topElement, element->entry, &attributes)) + return false; + break; + case StackElement::Rejection: + if (!addRejection(m_database, &attributes, &m_error)) + return false; + break; + case StackElement::Template: { + const int nameIndex = indexOfAttribute(attributes, nameAttribute()); + if (nameIndex == -1) { + m_error = msgMissingAttribute(nameAttribute()); + return false; + } + element->value.templateEntry = + new TemplateEntry(attributes.takeAt(nameIndex).value().toString()); + } + break; + case StackElement::TemplateInstanceEnum: + element->value.templateInstance = + parseTemplateInstanceEnum(reader, topElement, &attributes); + if (!element->value.templateInstance) + return false; + break; + case StackElement::Replace: + if (!parseReplace(reader, topElement, element, &attributes)) + return false; + break; + default: + break; // nada + } + } + + if (!attributes.isEmpty()) { + const QString message = msgUnusedAttributes(tagName, attributes); + qCWarning(lcShiboken, "%s", qPrintable(msgReaderWarning(reader, message))); + } + + m_current = element; + return true; +} diff --git a/sources/shiboken2/ApiExtractor/typesystemparser.h b/sources/shiboken2/ApiExtractor/typesystemparser.h new file mode 100644 index 000000000..aaf22353e --- /dev/null +++ b/sources/shiboken2/ApiExtractor/typesystemparser.h @@ -0,0 +1,274 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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$ +** +****************************************************************************/ +#ifndef TYPESYSTEMPARSER_H +#define TYPESYSTEMPARSER_H + +#include "typesystem.h" + +#include +#include + +QT_FORWARD_DECLARE_CLASS(QXmlStreamAttributes) +QT_FORWARD_DECLARE_CLASS(QXmlStreamReader) + +class TypeSystemEntityResolver; +class TypeDatabase; +class StackElement +{ + public: + enum ElementType { + None = 0x0, + + // Type tags (0x1, ... , 0xff) + ObjectTypeEntry = 0x1, + ValueTypeEntry = 0x2, + InterfaceTypeEntry = 0x3, + NamespaceTypeEntry = 0x4, + ComplexTypeEntryMask = 0x7, + + // Non-complex type tags (0x8, 0x9, ... , 0xf) + PrimitiveTypeEntry = 0x8, + EnumTypeEntry = 0x9, + ContainerTypeEntry = 0xa, + FunctionTypeEntry = 0xb, + CustomTypeEntry = 0xc, + SmartPointerTypeEntry = 0xd, + TypedefTypeEntry = 0xe, + TypeEntryMask = 0xf, + + // Documentation tags + InjectDocumentation = 0x10, + ModifyDocumentation = 0x20, + DocumentationMask = 0xf0, + + // Simple tags (0x100, 0x200, ... , 0xf00) + ExtraIncludes = 0x0100, + Include = 0x0200, + ModifyFunction = 0x0300, + ModifyField = 0x0400, + Root = 0x0500, + CustomMetaConstructor = 0x0600, + CustomMetaDestructor = 0x0700, + ArgumentMap = 0x0800, + SuppressedWarning = 0x0900, + Rejection = 0x0a00, + LoadTypesystem = 0x0b00, + RejectEnumValue = 0x0c00, + Template = 0x0d00, + TemplateInstanceEnum = 0x0e00, + Replace = 0x0f00, + AddFunction = 0x1000, + NativeToTarget = 0x1100, + TargetToNative = 0x1200, + AddConversion = 0x1300, + SimpleMask = 0x3f00, + + // Code snip tags (0x1000, 0x2000, ... , 0xf000) + InjectCode = 0x4000, + InjectCodeInFunction = 0x8000, + CodeSnipMask = 0xc000, + + // Function modifier tags (0x010000, 0x020000, ... , 0xf00000) + Access = 0x010000, + Removal = 0x020000, + Rename = 0x040000, + ModifyArgument = 0x080000, + Thread = 0x100000, + FunctionModifiers = 0xff0000, + + // Argument modifier tags (0x01000000 ... 0xf0000000) + ConversionRule = 0x01000000, + ReplaceType = 0x02000000, + ReplaceDefaultExpression = 0x04000000, + RemoveArgument = 0x08000000, + DefineOwnership = 0x10000000, + RemoveDefaultExpression = 0x20000000, + NoNullPointers = 0x40000000, + ReferenceCount = 0x80000000, + ParentOwner = 0x90000000, + Array = 0xA0000000, + ArgumentModifiers = 0xff000000 + }; + + StackElement(StackElement *p) : entry(nullptr), type(None), parent(p) { } + + TypeEntry* entry; + ElementType type; + StackElement *parent; + + union { + TemplateInstance* templateInstance; + TemplateEntry* templateEntry; + CustomFunction* customFunction; + } value; +}; + +struct StackElementContext +{ + CodeSnipList codeSnips; + AddedFunctionList addedFunctions; + FunctionModificationList functionMods; + FieldModificationList fieldMods; + DocModificationList docModifications; + int addedFunctionModificationIndex = -1; +}; + +class TypeSystemParser +{ +public: + Q_DISABLE_COPY(TypeSystemParser) + + TypeSystemParser(TypeDatabase* database, bool generate); + ~TypeSystemParser(); + + bool parse(QXmlStreamReader &reader); + + QString errorString() const { return m_error; } + +private: + bool startElement(const QXmlStreamReader &reader); + SmartPointerTypeEntry *parseSmartPointerEntry(const QXmlStreamReader &, + const QString &name, + const QVersionNumber &since, + QXmlStreamAttributes *attributes); + bool endElement(const QStringRef& localName); + template // QString/QStringRef + bool characters(const String &ch); + + bool importFileElement(const QXmlStreamAttributes &atts); + + void applyCommonAttributes(TypeEntry *type, QXmlStreamAttributes *attributes) const; + PrimitiveTypeEntry * + parsePrimitiveTypeEntry(const QXmlStreamReader &, const QString &name, + const QVersionNumber &since, QXmlStreamAttributes *); + ContainerTypeEntry * + parseContainerTypeEntry(const QXmlStreamReader &, const QString &name, + const QVersionNumber &since, QXmlStreamAttributes *); + EnumTypeEntry * + parseEnumTypeEntry(const QXmlStreamReader &, const QString &name, + const QVersionNumber &since, QXmlStreamAttributes *); + FlagsTypeEntry * + parseFlagsEntry(const QXmlStreamReader &, EnumTypeEntry *enumEntry, + const QString &name, QString flagName, + const QVersionNumber &since, QXmlStreamAttributes *); + + NamespaceTypeEntry * + parseNamespaceTypeEntry(const QXmlStreamReader &, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes); + + ObjectTypeEntry * + parseInterfaceTypeEntry(const QXmlStreamReader &, const QString &name, + const QVersionNumber &since, QXmlStreamAttributes *); + ValueTypeEntry * + parseValueTypeEntry(const QXmlStreamReader &, const QString &name, + const QVersionNumber &since, QXmlStreamAttributes *); + 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, + QXmlStreamAttributes *); + bool parseInjectDocumentation(const QXmlStreamReader &, QXmlStreamAttributes *); + bool parseModifyDocumentation(const QXmlStreamReader &, QXmlStreamAttributes *); + TypeSystemTypeEntry * + parseRootElement(const QXmlStreamReader &, const QVersionNumber &since, + QXmlStreamAttributes *); + bool loadTypesystem(const QXmlStreamReader &, QXmlStreamAttributes *); + bool parseRejectEnumValue(const QXmlStreamReader &, QXmlStreamAttributes *); + bool parseReplaceArgumentType(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseCustomConversion(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseAddConversion(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseNativeToTarget(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *attributes); + bool parseModifyArgument(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *attributes); + bool parseNoNullPointer(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *attributes); + bool parseDefineOwnership(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseArgumentMap(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseRemoval(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseRename(const QXmlStreamReader &, StackElement::ElementType type, + const StackElement &topElement, QXmlStreamAttributes *); + bool parseModifyField(const QXmlStreamReader &, QXmlStreamAttributes *); + bool parseAddFunction(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseModifyFunction(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseReplaceDefaultExpression(const QXmlStreamReader &, + const StackElement &topElement, QXmlStreamAttributes *); + CustomFunction * + parseCustomMetaConstructor(const QXmlStreamReader &, + StackElement::ElementType type, + const StackElement &topElement, QXmlStreamAttributes *); + bool parseReferenceCount(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseParentOwner(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool readFileSnippet(QXmlStreamAttributes *attributes, CodeSnip *snip); + bool parseInjectCode(const QXmlStreamReader &, const StackElement &topElement, + StackElement* element, QXmlStreamAttributes *); + bool parseInclude(const QXmlStreamReader &, const StackElement &topElement, + TypeEntry *entry, QXmlStreamAttributes *); + TemplateInstance + *parseTemplateInstanceEnum(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseReplace(const QXmlStreamReader &, const StackElement &topElement, + StackElement *element, QXmlStreamAttributes *); + + TypeDatabase* m_database; + StackElement* m_current = nullptr; + StackElement* m_currentDroppedEntry = nullptr; + int m_currentDroppedEntryDepth = 0; + int m_ignoreDepth = 0; + QString m_defaultPackage; + QString m_defaultSuperclass; + TypeSystem::ExceptionHandling m_exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; + TypeSystem::AllowThread m_allowThread = TypeSystem::AllowThread::Unspecified; + QString m_error; + const TypeEntry::CodeGeneration m_generate; + + EnumTypeEntry* m_currentEnum = nullptr; + QStack m_contextStack; + + QString m_currentSignature; + QString m_currentPath; + QScopedPointer m_entityResolver; +}; + +#endif // TYPESYSTEMPARSER_H -- cgit v1.2.3