aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken2/ApiExtractor/typesystem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken2/ApiExtractor/typesystem.cpp')
-rw-r--r--sources/shiboken2/ApiExtractor/typesystem.cpp233
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;
}