diff options
Diffstat (limited to 'sources/shiboken2/ApiExtractor/typesystem.cpp')
-rw-r--r-- | sources/shiboken2/ApiExtractor/typesystem.cpp | 645 |
1 files changed, 512 insertions, 133 deletions
diff --git a/sources/shiboken2/ApiExtractor/typesystem.cpp b/sources/shiboken2/ApiExtractor/typesystem.cpp index 869904d43..4a8d3063d 100644 --- a/sources/shiboken2/ApiExtractor/typesystem.cpp +++ b/sources/shiboken2/ApiExtractor/typesystem.cpp @@ -32,6 +32,9 @@ #include "reporthandler.h" #include <QtCore/QDir> #include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QRegularExpression> +#include <QtCore/QSet> #include <QtCore/QXmlStreamAttributes> #include <QtCore/QXmlStreamReader> @@ -44,11 +47,86 @@ 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 inline QString xPathAttribute() { return QStringLiteral("xpath"); } + +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,18 +179,26 @@ 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); } +static QString readerFileName(const QXmlStreamReader &reader) +{ + const QFile *file = qobject_cast<const QFile *>(reader.device()); + return file != nullptr ? file->fileName() : QString(); +} + static QString msgReaderError(const QXmlStreamReader &reader, const QString &what) { QString message; QTextStream str(&message); str << "Error: "; - if (const QFile *file = qobject_cast<const QFile *>(reader.device())) - str << "file=" << QDir::toNativeSeparators(file->fileName()) << ", "; + const QString fileName = readerFileName(reader); + if (!fileName.isEmpty()) + str << "file=" << QDir::toNativeSeparators(fileName) << ", "; str << "line=" << reader.lineNumber() << ", column=" << reader.columnNumber() << ", message=" << what; return message; @@ -126,6 +212,11 @@ static QString msgReaderError(const QXmlStreamReader &reader) bool Handler::parse(QXmlStreamReader &reader) { m_error.clear(); + m_currentPath.clear(); + const QString fileName = readerFileName(reader); + if (!fileName.isEmpty()) + m_currentPath = QFileInfo(fileName).absolutePath(); + while (!reader.atEnd()) { switch (reader.readNext()) { case QXmlStreamReader::NoToken: @@ -209,8 +300,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())); } } @@ -281,6 +373,7 @@ bool Handler::endElement(const QStringRef &localName) 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); @@ -493,7 +586,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, " @@ -521,11 +615,10 @@ void Handler::addFlags(const QString &name, QString flagName, if (QStringList(lst.mid(0, lst.size() - 1)).join(colonColon()) != m_currentEnum->targetLangQualifier()) { qCWarning(lcShiboken).noquote().nospace() << QStringLiteral("enum %1 and flags %2 differ in qualifiers") - // avoid constFirst to stay Qt 5.5 compatible - .arg(m_currentEnum->targetLangQualifier(), lst.first()); + .arg(m_currentEnum->targetLangQualifier(), lst.constFirst()); } - ftype->setFlagsName(lst.last()); + ftype->setFlagsName(lst.constLast()); m_currentEnum->setFlags(ftype); m_database->addFlagsType(ftype); @@ -663,20 +756,20 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts break; case StackElement::ValueTypeEntry: attributes.insert(QLatin1String("default-constructor"), QString()); - // fall throooough + Q_FALLTHROUGH(); case StackElement::ObjectTypeEntry: attributes.insert(QLatin1String("force-abstract"), QLatin1String("no")); attributes.insert(QLatin1String("deprecated"), QLatin1String("no")); attributes.insert(QLatin1String("hash-function"), QString()); attributes.insert(QLatin1String("stream"), QLatin1String("no")); - // fall throooough + Q_FALLTHROUGH(); case StackElement::InterfaceTypeEntry: attributes[QLatin1String("default-superclass")] = m_defaultSuperclass; attributes.insert(QLatin1String("polymorphic-id-expression"), QString()); attributes.insert(QLatin1String("delete-in-main-thread"), QLatin1String("no")); attributes.insert(QLatin1String("held-type"), QString()); attributes.insert(QLatin1String("copyable"), QString()); - // fall through + Q_FALLTHROUGH(); case StackElement::NamespaceTypeEntry: attributes.insert(QLatin1String("target-lang-name"), QString()); attributes[QLatin1String("package")] = m_defaultPackage; @@ -725,14 +818,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; + FunctionModification mod; + if (!mod.setSignature(signature, &m_error)) + return false; mod.renamedToName = attributes[QLatin1String("rename")]; mod.modifiers |= Modification::Rename; m_contextStack.top()->functionMods << mod; @@ -837,7 +932,7 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts else m_currentEnum = new EnumTypeEntry(QStringList(names.mid(0, names.size() - 1)).join(colonColon()), - names.last(), since); + names.constLast(), since); m_currentEnum->setAnonymous(!attributes[QLatin1String("identified-by-value")].isEmpty()); element->entry = m_currentEnum; m_currentEnum->setCodeGeneration(m_generate); @@ -850,7 +945,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); } } @@ -872,7 +968,7 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts itype->setOrigin(otype); element->entry = otype; } - // fall through + Q_FALLTHROUGH(); case StackElement::ValueTypeEntry: { if (!element->entry) { ValueTypeEntry* typeEntry = new ValueTypeEntry(name, since); @@ -882,12 +978,12 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts element->entry = typeEntry; } - // fall through + Q_FALLTHROUGH(); case StackElement::NamespaceTypeEntry: if (!element->entry) element->entry = new NamespaceTypeEntry(name, since); - // fall through + Q_FALLTHROUGH(); case StackElement::ObjectTypeEntry: if (!element->entry) element->entry = new ObjectTypeEntry(name, since); @@ -908,16 +1004,6 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts if (!targetLangName.isEmpty()) ctype->setTargetLangName(targetLangName); - // The expense policy - QString limit = attributes[QLatin1String("expense-limit")]; - if (!limit.isEmpty() && limit != QLatin1String("none")) { - ExpensePolicy ep; - ep.limit = limit.toInt(); - ep.cost = attributes[QLatin1String("expense-cost")]; - ctype->setExpensePolicy(ep); - } - - ctype->setIsPolymorphicBase(convertBoolean(attributes[QLatin1String("polymorphic-base")], QLatin1String("polymorphic-base"), false)); ctype->setPolymorphicIdValue(attributes[QLatin1String("polymorphic-id-expression")]); //Copyable @@ -1001,7 +1087,6 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts attributes.insert(sinceAttribute(), QLatin1String("0")); fetchAttributeValues(tagName, atts, &attributes); - double since = attributes[sinceAttribute()].toDouble(); const int validParent = StackElement::TypeEntryMask | StackElement::ModifyFunction @@ -1034,8 +1119,8 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts } QString signature = m_current->type & StackElement::TypeEntryMask ? QString() : m_currentSignature; - DocModification mod(mode, signature, since); - mod.format = lang; + DocModification mod(mode, signature); + mod.setFormat(lang); m_contextStack.top()->docModifications << mod; } else { m_error = QLatin1String("inject-documentation must be inside modify-function, " @@ -1045,17 +1130,17 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts } else if (element->type == StackElement::ModifyDocumentation) { // check the XML tag attributes QHash<QString, QString> attributes; - attributes.insert(QLatin1String("xpath"), QString()); + attributes.insert(xPathAttribute(), QString()); attributes.insert(sinceAttribute(), QLatin1String("0")); fetchAttributeValues(tagName, atts, &attributes); - double since = attributes[sinceAttribute()].toDouble(); const int validParent = StackElement::TypeEntryMask | StackElement::ModifyFunction | StackElement::ModifyField; if (m_current->parent && m_current->parent->type & validParent) { QString signature = (m_current->type & StackElement::TypeEntryMask) ? QString() : m_currentSignature; - m_contextStack.top()->docModifications << DocModification(attributes[QLatin1String("xpath")], signature, since); + m_contextStack.top()->docModifications + << DocModification(attributes.value(xPathAttribute()), signature); } else { m_error = QLatin1String("modify-documentation must be inside modify-function, " "modify-field or other tags that creates a type"); @@ -1095,7 +1180,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 +1262,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 +1289,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: { }; }; @@ -1238,19 +1328,18 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts return false; } bool generateChild = (convertBoolean(attributes[QLatin1String("generate")], QLatin1String("generate"), true) && (m_generate == TypeEntry::GenerateAll)); - if (!m_database->parseFile(name, generateChild)) { + if (!m_database->parseFile(name, m_currentPath, generateChild)) { m_error = QStringLiteral("Failed to parse: '%1'").arg(name); return false; } } break; - case StackElement::RejectEnumValue: { + case StackElement::RejectEnumValue: if (!m_currentEnum) { m_error = QLatin1String("<reject-enum-value> node must be used inside a <enum-type> node"); return false; } - QString name = attributes[nameAttribute()]; - } break; + break; case StackElement::ReplaceType: { if (topElement.type != StackElement::ModifyArgument) { m_error = QLatin1String("Type replacement can only be specified for argument modifications"); @@ -1290,7 +1379,7 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts return false; } - CodeSnip snip(since); + CodeSnip snip; snip.language = lang; m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.append(snip); } else { @@ -1332,7 +1421,7 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts m_error = QLatin1String("Native to Target conversion code can only be specified for custom conversion rules."); return false; } - m_contextStack.top()->codeSnips << CodeSnip(0); + m_contextStack.top()->codeSnips << CodeSnip(); } break; case StackElement::TargetToNative: { @@ -1356,7 +1445,7 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts } QString typeCheck = attributes[QLatin1String("check")]; static_cast<TypeEntry*>(m_current->entry)->customConversion()->addTargetToNativeConversion(sourceTypeName, typeCheck); - m_contextStack.top()->codeSnips << CodeSnip(0); + m_contextStack.top()->codeSnips << CodeSnip(); } break; case StackElement::ModifyArgument: { @@ -1388,7 +1477,7 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts return false; } - ArgumentModification argumentModification = ArgumentModification(idx, since); + ArgumentModification argumentModification = ArgumentModification(idx); argumentModification.replace_value = replace_value; argumentModification.resetAfterUse = convertBoolean(attributes[QLatin1String("invalidate-after-use")], QLatin1String("invalidate-after-use"), false); m_contextStack.top()->functionMods.last().argument_mods.append(argumentModification); @@ -1444,11 +1533,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)) { @@ -1632,8 +1725,9 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts m_contextStack.top()->addedFunctions << func; - FunctionModification mod(since); - mod.signature = m_currentSignature; + FunctionModification mod; + if (!mod.setSignature(m_currentSignature, &m_error)) + return false; m_contextStack.top()->functionMods << mod; } break; @@ -1657,8 +1751,10 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts return false; } - FunctionModification mod(since); - m_currentSignature = mod.signature = signature; + FunctionModification mod; + if (!mod.setSignature(signature, &m_error)) + return false; + m_currentSignature = signature; QString access = attributes[QLatin1String("access")].toLower(); if (!access.isEmpty()) { @@ -1746,8 +1842,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 +1886,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) @@ -1837,7 +1938,7 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts return false; } - CodeSnip snip(since); + CodeSnip snip; snip.language = languageNames[className]; snip.position = positionNames[position]; bool in_file = false; @@ -1848,8 +1949,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, m_currentPath); + if (QFile::exists(resolved)) { + QFile codeFile(resolved); if (codeFile.open(QIODevice::Text | QIODevice::ReadOnly)) { QString content = QLatin1String("// ========================================================================\n" "// START of custom code block [file: "); @@ -1875,7 +1977,7 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts } if (topElement.type == StackElement::ModifyFunction || topElement.type == StackElement::AddFunction) { - FunctionModification mod = m_contextStack.top()->functionMods.last(); + FunctionModification mod = m_contextStack.top()->functionMods.constLast(); if (snip.language == TypeSystem::ShellDeclaration) { m_error = QLatin1String("no function implementation in shell declaration in which to inject code"); return false; @@ -1930,21 +2032,12 @@ 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); + element->value.templateEntry = new TemplateEntry(attributes.value(nameAttribute())); break; case StackElement::TemplateInstanceEnum: if (!(topElement.type & StackElement::CodeSnipMask) && @@ -1958,7 +2051,7 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts "custom-destructors, conversion-rule, native-to-target or add-conversion tags."); return false; } - element->value.templateInstance = new TemplateInstance(attributes[nameAttribute()], since); + element->value.templateInstance = new TemplateInstance(attributes.value(nameAttribute())); break; case StackElement::Replace: if (topElement.type != StackElement::TemplateInstanceEnum) { @@ -1976,6 +2069,23 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts return true; } +PrimitiveTypeEntry::PrimitiveTypeEntry(const QString &name, double vr) : + TypeEntry(name, PrimitiveType, vr), + m_preferredConversion(true), + m_preferredTargetLangType(true) +{ +} + +QString PrimitiveTypeEntry::targetLangName() const +{ + return m_targetLangName; +} + +QString PrimitiveTypeEntry::targetLangApiName() const +{ + return m_targetLangApiName; +} + PrimitiveTypeEntry *PrimitiveTypeEntry::basicReferencedTypeEntry() const { if (!m_referencedTypeEntry) @@ -1988,6 +2098,16 @@ PrimitiveTypeEntry *PrimitiveTypeEntry::basicReferencedTypeEntry() const return m_referencedTypeEntry; } +bool PrimitiveTypeEntry::preferredConversion() const +{ + return m_preferredConversion; +} + +void PrimitiveTypeEntry::setPreferredConversion(bool b) +{ + m_preferredConversion = b; +} + typedef QHash<const PrimitiveTypeEntry*, QString> PrimitiveTypeEntryTargetLangPackageMap; Q_GLOBAL_STATIC(PrimitiveTypeEntryTargetLangPackageMap, primitiveTypeEntryTargetLangPackages); @@ -2021,7 +2141,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; @@ -2038,6 +2158,17 @@ FieldModification ComplexTypeEntry::fieldModification(const QString &name) const return mod; } +QString ComplexTypeEntry::targetLangPackage() const +{ + return m_package; +} + +QString ComplexTypeEntry::targetLangName() const +{ + return m_targetLangName.isEmpty() ? + TypeEntry::targetLangName() : m_targetLangName; +} + // The things we do not to break the ABI... typedef QHash<const ComplexTypeEntry*, QString> ComplexTypeEntryDefaultConstructorMap; Q_GLOBAL_STATIC(ComplexTypeEntryDefaultConstructorMap, complexTypeEntryDefaultConstructors); @@ -2102,16 +2233,46 @@ QString EnumTypeEntry::targetLangQualifier() const return m_qualifier; } +QString EnumTypeEntry::qualifiedTargetLangName() const +{ + QString qualifiedName; + QString pkg = targetLangPackage(); + QString qualifier = targetLangQualifier(); + + if (!pkg.isEmpty()) + qualifiedName += pkg + QLatin1Char('.'); + if (!qualifier.isEmpty()) + qualifiedName += qualifier + QLatin1Char('.'); + qualifiedName += targetLangName(); + + return qualifiedName; +} + QString EnumTypeEntry::targetLangApiName() const { return QLatin1String("jint"); } +bool EnumTypeEntry::preferredConversion() const +{ + return false; +} + QString FlagsTypeEntry::targetLangApiName() const { return QLatin1String("jint"); } +bool FlagsTypeEntry::preferredConversion() const +{ + return false; +} + +QString FlagsTypeEntry::targetLangPackage() const +{ + return m_enum->targetLangPackage(); +} + void EnumTypeEntry::addEnumValueRedirection(const QString &rejected, const QString &usedValue) { m_enumRedirections << EnumValueRedirection(rejected, usedValue); @@ -2131,6 +2292,11 @@ QString FlagsTypeEntry::qualifiedTargetLangName() const + QLatin1Char('.') + targetLangName(); } +QString FlagsTypeEntry::targetLangName() const +{ + return m_targetLangName; +} + /*! * The Visual Studio 2002 compiler doesn't support these symbols, * which our typedefs unforntuatly expand to. @@ -2172,7 +2338,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 +2352,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 +2388,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(); } @@ -2220,41 +2403,11 @@ QString FunctionModification::toString() const return str; } -bool FunctionModification::operator!=(const FunctionModification& other) const -{ - return !(*this == other); -} - -bool FunctionModification::operator==(const FunctionModification& other) const -{ - if (signature != other.signature) - return false; - - if (association != other.association) - return false; - - if (modifiers != other.modifiers) - return false; - - if (removal != other.removal) - return false; - - if (m_thread != other.m_thread) - return false; - - if (m_allowThread != other.m_allowThread) - return false; - - if (m_version != other.m_version) - return false; - - return true; -} - 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) { @@ -2318,7 +2471,9 @@ static AddedFunction::TypeInfo parseType(const QString& signature, int startPos return result; } -AddedFunction::AddedFunction(QString signature, QString returnType, double vr) : m_access(Public), m_version(vr) +AddedFunction::AddedFunction(QString signature, QString returnType, double vr) : + m_version(vr), + m_access(Public) { Q_ASSERT(!returnType.isEmpty()); m_returnType = parseType(returnType); @@ -2339,7 +2494,7 @@ AddedFunction::AddedFunction(QString signature, QString returnType, double vr) : break; } // is const? - m_isConst = signature.right(signatureLength - endPos).contains(QLatin1String("const")); + m_isConst = signature.rightRef(signatureLength - endPos).contains(QLatin1String("const")); } } @@ -2385,6 +2540,25 @@ AddedFunction::TypeInfo AddedFunction::TypeInfo::fromSignature(const QString& si return parseType(signature); } +ComplexTypeEntry::ComplexTypeEntry(const QString &name, TypeEntry::Type t, double vr) : + TypeEntry(QString(name).replace(QLatin1String(".*::"), QString()), t, vr), + m_qualifiedCppName(name), + m_qobject(false), + m_polymorphicBase(false), + m_genericClass(false) +{ +} + +bool ComplexTypeEntry::isComplex() const +{ + return true; +} + +QString ComplexTypeEntry::lookupName() const +{ + return m_lookupName.isEmpty() ? targetLangName() : m_lookupName; +} + QString ComplexTypeEntry::targetLangApiName() const { return strings_jobject; @@ -2401,6 +2575,18 @@ QString StringTypeEntry::targetLangPackage() const { return QString(); } + +bool StringTypeEntry::isNativeIdBased() const +{ + return false; +} + +CharTypeEntry::CharTypeEntry(const QString &name, double vr) : + ValueTypeEntry(name, CharType, vr) +{ + setCodeGeneration(GenerateNothing); +} + QString CharTypeEntry::targetLangApiName() const { return strings_jchar; @@ -2409,6 +2595,22 @@ QString CharTypeEntry::targetLangName() const { return strings_char; } + +QString CharTypeEntry::targetLangPackage() const +{ + return QString(); +} + +bool CharTypeEntry::isNativeIdBased() const +{ + return false; +} + +VariantTypeEntry::VariantTypeEntry(const QString &name, double vr) : + ValueTypeEntry(name, VariantType, vr) +{ +} + QString VariantTypeEntry::targetLangApiName() const { return strings_jobject; @@ -2422,6 +2624,11 @@ QString VariantTypeEntry::targetLangPackage() const return QString(); } +bool VariantTypeEntry::isNativeIdBased() const +{ + return false; +} + QString ContainerTypeEntry::typeName() const { switch(m_type) { @@ -2455,9 +2662,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,25 +2682,26 @@ 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. typedef QHash<const TypeEntry*, CustomConversion*> TypeEntryCustomConversionMap; Q_GLOBAL_STATIC(TypeEntryCustomConversionMap, typeEntryCustomConversionMap); +TypeEntry::TypeEntry(const QString &name, TypeEntry::Type t, double vr) : + m_name(name), + m_type(t), + m_version(vr) +{ +} + TypeEntry::~TypeEntry() { if (typeEntryCustomConversionMap()->contains(this)) { @@ -2511,6 +2729,131 @@ CustomConversion* TypeEntry::customConversion() const return 0; } +TypeSystemTypeEntry::TypeSystemTypeEntry(const QString &name, double vr) : + TypeEntry(name, TypeSystemType, vr) +{ +} + +VoidTypeEntry::VoidTypeEntry() : + TypeEntry(QLatin1String("void"), VoidType, 0) +{ +} + +VarargsTypeEntry::VarargsTypeEntry() : + TypeEntry(QLatin1String("..."), VarargsType, 0) +{ +} + +TemplateArgumentEntry::TemplateArgumentEntry(const QString &name, double vr) : + TypeEntry(name, TemplateArgumentType, vr) +{ +} + +ArrayTypeEntry::ArrayTypeEntry(const TypeEntry *nested_type, double vr) : + TypeEntry(QLatin1String("Array"), ArrayType, vr), + m_nestedType(nested_type) +{ + Q_ASSERT(m_nestedType); +} + +QString ArrayTypeEntry::targetLangName() const +{ + return m_nestedType->targetLangName() + QLatin1String("[]"); +} + +QString ArrayTypeEntry::targetLangApiName() const +{ + if (m_nestedType->isPrimitive()) + return m_nestedType->targetLangApiName() + QLatin1String("Array"); + else + return QLatin1String("jobjectArray"); +} + +EnumTypeEntry::EnumTypeEntry(const QString &nspace, const QString &enumName, double vr) : + TypeEntry(nspace.isEmpty() ? enumName : nspace + QLatin1String("::") + enumName, + EnumType, vr), + m_qualifier(nspace), + m_targetLangName(enumName) +{ +} + +QString EnumTypeEntry::targetLangPackage() const +{ + return m_packageName; +} + +void EnumTypeEntry::setTargetLangPackage(const QString &package) +{ + m_packageName = package; +} + +QString EnumTypeEntry::targetLangName() const +{ + return m_targetLangName; +} + +EnumValueTypeEntry::EnumValueTypeEntry(const QString& name, const QString& value, const EnumTypeEntry* enclosingEnum, double vr) : + TypeEntry(name, TypeEntry::EnumValue, vr), + m_value(value), + m_enclosingEnum(enclosingEnum) +{ +} + +FlagsTypeEntry::FlagsTypeEntry(const QString &name, double vr) : + TypeEntry(name, FlagsType, vr) +{ +} + +ContainerTypeEntry::ContainerTypeEntry(const QString &name, Type type, double vr) : + ComplexTypeEntry(name, ContainerType, vr), + m_type(type) +{ + setCodeGeneration(GenerateForSubclass); +} + +SmartPointerTypeEntry::SmartPointerTypeEntry(const QString &name, + const QString &getterName, + const QString &smartPointerType, + const QString &refCountMethodName, + double vr) : + ComplexTypeEntry(name, SmartPointerType, vr), + m_getterName(getterName), + m_smartPointerType(smartPointerType), + m_refCountMethodName(refCountMethodName) +{ +} + +NamespaceTypeEntry::NamespaceTypeEntry(const QString &name, double vr) : + ComplexTypeEntry(name, NamespaceType, vr) +{ +} + +ValueTypeEntry::ValueTypeEntry(const QString &name, double vr) : + ComplexTypeEntry(name, BasicValueType, vr) +{ +} + +bool ValueTypeEntry::isValue() const +{ + return true; +} + +bool ValueTypeEntry::isNativeIdBased() const +{ + return true; +} + +ValueTypeEntry::ValueTypeEntry(const QString &name, Type t, double vr) : + ComplexTypeEntry(name, t, vr) +{ +} + +StringTypeEntry::StringTypeEntry(const QString &name, double vr) : + ValueTypeEntry(name, StringType, vr) +{ + setCodeGeneration(GenerateNothing); +} + /* static void injectCode(ComplexTypeEntry *e, const char *signature, @@ -2563,9 +2906,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; } @@ -2665,3 +3006,41 @@ void CustomConversion::TargetToNativeConversion::setConversion(const QString& co { m_d->conversion = conversion; } + +InterfaceTypeEntry::InterfaceTypeEntry(const QString &name, double vr) : + ComplexTypeEntry(name, InterfaceType, vr) +{ +} + +bool InterfaceTypeEntry::isNativeIdBased() const +{ + return true; +} + +QString InterfaceTypeEntry::qualifiedCppName() const +{ + const int len = ComplexTypeEntry::qualifiedCppName().length() - interfaceName(QString()).length(); + return ComplexTypeEntry::qualifiedCppName().left(len); +} + +FunctionTypeEntry::FunctionTypeEntry(const QString &name, const QString &signature, + double vr) : + TypeEntry(name, FunctionType, vr) +{ + addSignature(signature); +} + +ObjectTypeEntry::ObjectTypeEntry(const QString &name, double vr) + : ComplexTypeEntry(name, ObjectType, vr) +{ +} + +InterfaceTypeEntry *ObjectTypeEntry::designatedInterface() const +{ + return m_interface; +} + +bool ObjectTypeEntry::isNativeIdBased() const +{ + return true; +} |