diff options
Diffstat (limited to 'sources/shiboken2/ApiExtractor/typesystem.cpp')
-rw-r--r-- | sources/shiboken2/ApiExtractor/typesystem.cpp | 233 |
1 files changed, 175 insertions, 58 deletions
diff --git a/sources/shiboken2/ApiExtractor/typesystem.cpp b/sources/shiboken2/ApiExtractor/typesystem.cpp index 869904d43..9c80a6c0b 100644 --- a/sources/shiboken2/ApiExtractor/typesystem.cpp +++ b/sources/shiboken2/ApiExtractor/typesystem.cpp @@ -32,6 +32,8 @@ #include "reporthandler.h" #include <QtCore/QDir> #include <QtCore/QFile> +#include <QtCore/QRegularExpression> +#include <QtCore/QSet> #include <QtCore/QXmlStreamAttributes> #include <QtCore/QXmlStreamReader> @@ -44,11 +46,85 @@ static QString strings_jobject = QLatin1String("jobject"); static inline QString colonColon() { return QStringLiteral("::"); } 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 flagsAttribute() { return QStringLiteral("flags"); } +static inline QString classAttribute() { return QStringLiteral("class"); } +static inline QString functionNameAttribute() { return QStringLiteral("function-name"); } +static inline QString fieldNameAttribute() { return QStringLiteral("field-name"); } +static inline QString enumNameAttribute() { return QStringLiteral("enum-name"); } +static inline QString argumentTypeAttribute() { return QStringLiteral("argument-type"); } +static inline QString returnTypeAttribute() { return QStringLiteral("return-type"); } + +static QVector<CustomConversion *> 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 = QLatin1String("Invalid pattern \"") + patternIn + + QLatin1String("\": ") + re->errorString(); + return false; + } + return true; +} + +static bool addRejection(TypeDatabase *database, const QHash<QString, QString> &attributes, + QString *errorMessage) +{ + typedef QPair<QString, TypeRejection::MatchType> AttributeMatchTypePair; + + TypeRejection rejection; + + const QString className = attributes.value(classAttribute()); + if (!setRejectionRegularExpression(className, &rejection.className, errorMessage)) + return false; + + static const AttributeMatchTypePair attributeMatchTypeMapping[] = + {{functionNameAttribute(), TypeRejection::Function}, + {fieldNameAttribute(), TypeRejection::Field}, + {enumNameAttribute(), TypeRejection::Enum}, + {argumentTypeAttribute(), TypeRejection::ArgumentType}, + {returnTypeAttribute(), TypeRejection::ReturnType} + }; + + // Search for non-empty attribute (function, field, enum) + const auto aend = attributes.cend(); + for (const AttributeMatchTypePair &mapping : attributeMatchTypeMapping) { + const auto it = attributes.constFind(mapping.first); + if (it != aend && !it.value().isEmpty()) { + if (!setRejectionRegularExpression(it.value(), &rejection.pattern, errorMessage)) + return false; + rejection.matchType = mapping.second; + database->addRejection(rejection); + return true; + } + } + + // 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; +} -static QList<CustomConversion*> customConversionsForReview = QList<CustomConversion*>(); Handler::Handler(TypeDatabase* database, bool generate) : m_database(database), m_generate(generate ? TypeEntry::GenerateAll : TypeEntry::GenerateForSubclass) @@ -101,6 +177,7 @@ Handler::Handler(TypeDatabase* database, bool generate) tagNames.insert(QLatin1String("no-null-pointer"), StackElement::NoNullPointers); tagNames.insert(QLatin1String("reference-count"), StackElement::ReferenceCount); tagNames.insert(QLatin1String("parent"), StackElement::ParentOwner); + tagNames.insert(QLatin1String("array"), StackElement::Array); tagNames.insert(QLatin1String("inject-documentation"), StackElement::InjectDocumentation); tagNames.insert(QLatin1String("modify-documentation"), StackElement::ModifyDocumentation); tagNames.insert(QLatin1String("add-function"), StackElement::AddFunction); @@ -209,8 +286,9 @@ bool Handler::endElement(const QStringRef &localName) if (m_generate == TypeEntry::GenerateAll) { TypeDatabase::instance()->addGlobalUserFunctions(m_contextStack.top()->addedFunctions); TypeDatabase::instance()->addGlobalUserFunctionModifications(m_contextStack.top()->functionMods); - foreach (CustomConversion* customConversion, customConversionsForReview) { - foreach (CustomConversion::TargetToNativeConversion* toNative, customConversion->targetToNativeConversions()) + for (CustomConversion *customConversion : qAsConst(customConversionsForReview)) { + const CustomConversion::TargetToNativeConversions &toNatives = customConversion->targetToNativeConversions(); + for (CustomConversion::TargetToNativeConversion *toNative : toNatives) toNative->setSourceType(m_database->findType(toNative->sourceTypeName())); } } @@ -493,7 +571,8 @@ static QString getNamePrefix(StackElement* element) static QString checkSignatureError(const QString& signature, const QString& tag) { QString funcName = signature.left(signature.indexOf(QLatin1Char('('))).trimmed(); - static QRegExp whiteSpace(QLatin1String("\\s")); + 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, " @@ -725,14 +804,16 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts } QString rename = attributes[QLatin1String("rename")]; if (!rename.isEmpty()) { - static QRegExp functionNameRegExp(QLatin1String("^[a-zA-Z_][a-zA-Z0-9_]*$")); - if (!functionNameRegExp.exactMatch(rename)) { + 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(since); - mod.signature = signature; + if (!mod.setSignature(signature, &m_error)) + return false; mod.renamedToName = attributes[QLatin1String("rename")]; mod.modifiers |= Modification::Rename; m_contextStack.top()->functionMods << mod; @@ -850,7 +931,8 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts // put in the flags parallel... const QString flagNames = attributes.value(flagsAttribute()); if (!flagNames.isEmpty()) { - foreach (const QString &flagName, flagNames.split(QLatin1Char(','))) + const QStringList &flagNameList = flagNames.split(QLatin1Char(',')); + for (const QString &flagName : flagNameList) addFlags(name, flagName.trimmed(), attributes, since); } } @@ -1095,7 +1177,7 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts attributes.insert(QLatin1String("default-value"), QString()); break; case StackElement::SuppressedWarning: - attributes.insert(QLatin1String("text"), QString()); + attributes.insert(textAttribute(), QString()); break; case StackElement::ReplaceDefaultExpression: attributes.insert(QLatin1String("with"), QString()); @@ -1177,10 +1259,12 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts attributes.insert(QLatin1String("to"), QString()); break; case StackElement::Rejection: - attributes.insert(QLatin1String("class"), QLatin1String("*")); - attributes.insert(QLatin1String("function-name"), QLatin1String("*")); - attributes.insert(QLatin1String("field-name"), QLatin1String("*")); - attributes.insert(QLatin1String("enum-name"), QLatin1String("*")); + attributes.insert(classAttribute(), QString()); + attributes.insert(functionNameAttribute(), QString()); + attributes.insert(fieldNameAttribute(), QString()); + attributes.insert(enumNameAttribute(), QString()); + attributes.insert(argumentTypeAttribute(), QString()); + attributes.insert(returnTypeAttribute(), QString()); break; case StackElement::Removal: attributes.insert(QLatin1String("class"), QLatin1String("all")); @@ -1202,6 +1286,9 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts case StackElement::ParentOwner: attributes.insert(QLatin1String("index"), QString()); attributes.insert(QLatin1String("action"), QString()); + break; + case StackElement::Array: + break; default: { }; }; @@ -1444,11 +1531,15 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts m_contextStack.top()->functionMods.last().argument_mods.last().ownerships[lang] = owner; } break; - case StackElement::SuppressedWarning: - if (attributes[QLatin1String("text")].isEmpty()) + case StackElement::SuppressedWarning: { + const QString suppressedWarning = attributes.value(textAttribute()); + if (suppressedWarning.isEmpty()) { qCWarning(lcShiboken) << "Suppressed warning with no text specified"; - else - m_database->addSuppressedWarning(attributes[QLatin1String("text")]); + } else { + if (!m_database->addSuppressedWarning(suppressedWarning, &m_error)) + return false; + } + } break; case StackElement::ArgumentMap: { if (!(topElement.type & StackElement::CodeSnipMask)) { @@ -1633,7 +1724,8 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts m_contextStack.top()->addedFunctions << func; FunctionModification mod(since); - mod.signature = m_currentSignature; + if (!mod.setSignature(m_currentSignature, &m_error)) + return false; m_contextStack.top()->functionMods << mod; } break; @@ -1658,7 +1750,9 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts } FunctionModification mod(since); - m_currentSignature = mod.signature = signature; + if (!mod.setSignature(signature, &m_error)) + return false; + m_currentSignature = signature; QString access = attributes[QLatin1String("access")].toLower(); if (!access.isEmpty()) { @@ -1746,8 +1840,8 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts if (rc.action == ReferenceCount::Invalid) { m_error = QLatin1String("unrecognized value for action attribute. supported actions:"); - foreach (const QString &action, actions.keys()) - m_error += QLatin1Char(' ') + action; + for (QHash<QString, ReferenceCount::Action>::const_iterator it = actions.cbegin(), end = actions.cend(); it != end; ++it) + m_error += QLatin1Char(' ') + it.key(); } m_contextStack.top()->functionMods.last().argument_mods.last().referenceCounts.append(rc); @@ -1790,8 +1884,13 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts m_contextStack.top()->functionMods.last().argument_mods.last().owner = ao; } 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 (!(topElement.type & StackElement::ComplexTypeEntryMask) && (topElement.type != StackElement::AddFunction) @@ -1848,8 +1947,9 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts if (m_generate != TypeEntry::GenerateForSubclass && m_generate != TypeEntry::GenerateNothing && !file_name.isEmpty()) { - if (QFile::exists(file_name)) { - QFile codeFile(file_name); + const QString resolved = m_database->modifiedTypesystemFilepath(file_name, false); + if (QFile::exists(resolved)) { + QFile codeFile(resolved); if (codeFile.open(QIODevice::Text | QIODevice::ReadOnly)) { QString content = QLatin1String("// ========================================================================\n" "// START of custom code block [file: "); @@ -1930,18 +2030,9 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts } } break; - case StackElement::Rejection: { - QString cls = attributes[QLatin1String("class")]; - QString function = attributes[QLatin1String("function-name")]; - QString field = attributes[QLatin1String("field-name")]; - QString enum_ = attributes[QLatin1String("enum-name")]; - if (cls == QLatin1String("*") && function == QLatin1String("*") && field == QLatin1String("*") && enum_ == QLatin1String("*")) { - m_error = QLatin1String("bad reject entry, neither 'class', 'function-name' nor " - "'field' specified"); + case StackElement::Rejection: + if (!addRejection(m_database, attributes, &m_error)) return false; - } - m_database->addRejection(cls, function, field, enum_); - } break; case StackElement::Template: element->value.templateEntry = new TemplateEntry(attributes[nameAttribute()], since); @@ -2021,7 +2112,7 @@ FunctionModificationList ComplexTypeEntry::functionModifications(const QString & FunctionModificationList lst; for (int i = 0; i < m_functionMods.count(); ++i) { const FunctionModification &mod = m_functionMods.at(i); - if (mod.signature == signature) + if (mod.matches(signature)) lst << mod; } return lst; @@ -2172,7 +2263,7 @@ QString TemplateInstance::expandCode() const QString CodeSnipAbstract::code() const { QString res; - foreach (const CodeSnipFragment &codeFrag, codeList) + for (const CodeSnipFragment &codeFrag : codeList) res.append(codeFrag.code()); return res; @@ -2186,9 +2277,26 @@ QString CodeSnipFragment::code() const return m_code; } +bool FunctionModification::setSignature(const QString &s, QString *errorMessage) +{ + if (s.startsWith(QLatin1Char('^'))) { + m_signaturePattern.setPattern(s); + if (!m_signaturePattern.isValid()) { + if (errorMessage) { + *errorMessage = QLatin1String("Invalid signature pattern: \"") + + s + QLatin1String("\": ") + m_signaturePattern.errorString(); + } + return false; + } + } else { + m_signature = s; + } + return true; +} + QString FunctionModification::toString() const { - QString str = signature + QLatin1String("->"); + QString str = signature() + QLatin1String("->"); if (modifiers & AccessModifierMask) { switch (modifiers & AccessModifierMask) { case Private: str += QLatin1String("private"); break; @@ -2205,7 +2313,7 @@ QString FunctionModification::toString() const if (modifiers & Writable) str += QLatin1String("writable"); if (modifiers & CodeInjection) { - foreach (const CodeSnip &s, snips) { + for (const CodeSnip &s : snips) { str += QLatin1String("\n//code injection:\n"); str += s.code(); } @@ -2227,8 +2335,14 @@ bool FunctionModification::operator!=(const FunctionModification& other) const bool FunctionModification::operator==(const FunctionModification& other) const { - if (signature != other.signature) + if (m_signature.isEmpty() != other.m_signature.isEmpty()) + return false; + + if (m_signature.isEmpty() + ? m_signaturePattern != other.m_signaturePattern + : m_signature != other.m_signature) { return false; + } if (association != other.association) return false; @@ -2254,7 +2368,8 @@ bool FunctionModification::operator==(const FunctionModification& other) const static AddedFunction::TypeInfo parseType(const QString& signature, int startPos = 0, int* endPos = 0) { AddedFunction::TypeInfo result; - QRegExp regex(QLatin1String("\\w")); + static const QRegularExpression regex(QLatin1String("\\w")); + Q_ASSERT(regex.isValid()); int length = signature.length(); int start = signature.indexOf(regex, startPos); if (start == -1) { @@ -2455,9 +2570,19 @@ QString ContainerTypeEntry::typeName() const } } -static bool strLess(const char* a, const char* b) +static const QSet<QString> &primitiveCppTypes() { - return ::strcmp(a, b) < 0; + static QSet<QString> result; + if (result.isEmpty()) { + static const char *cppTypes[] = { + "bool", "char", "double", "float", "int", + "long", "long long", "short", + "wchar_t" + }; + for (const char *cppType : cppTypes) + result.insert(QLatin1String(cppType)); + } + return result; } bool TypeEntry::isCppPrimitive() const @@ -2465,19 +2590,13 @@ bool TypeEntry::isCppPrimitive() const if (!isPrimitive()) return false; - const PrimitiveTypeEntry *referencedType = - static_cast<const PrimitiveTypeEntry *>(this)->basicReferencedTypeEntry(); - QByteArray typeName = (referencedType ? referencedType->name() : m_name).toUtf8(); - - if (typeName.contains(' ') || m_type == VoidType) + if (m_type == VoidType) return true; - // Keep this sorted!! - static const char* cppTypes[] = { "bool", "char", "double", "float", "int", "long", "long long", "short", "wchar_t" }; - const int N = sizeof(cppTypes)/sizeof(char*); - const char** res = qBinaryFind(&cppTypes[0], &cppTypes[N], typeName.constData(), strLess); - - return res != &cppTypes[N]; + const PrimitiveTypeEntry *referencedType = + static_cast<const PrimitiveTypeEntry *>(this)->basicReferencedTypeEntry(); + const QString &typeName = referencedType ? referencedType->name() : m_name; + return typeName.contains(QLatin1Char(' ')) || primitiveCppTypes().contains(typeName); } // Again, stuff to avoid ABI breakage. @@ -2563,9 +2682,7 @@ CustomConversion::CustomConversion(TypeEntry* ownerType) CustomConversion::~CustomConversion() { - foreach (TargetToNativeConversion* targetToNativeConversion, m_d->targetToNativeConversions) - delete targetToNativeConversion; - m_d->targetToNativeConversions.clear(); + qDeleteAll(m_d->targetToNativeConversions); delete m_d; } |